false, 'lyrics' => '', 'url' => '']; if (!cfg('lyrics.enabled') || cfg('genius.token') === '') { json_out($empty); } [$artist, $title] = request_track(); if ($artist === '' && $title === '') { json_out($empty); } $token = cfg('genius.token'); $timeout = 5; $headers = ['Authorization: Bearer ' . $token]; /** * Return the inner HTML of a balanced
whose opening tag starts at $lt. * Correctly handles nested
elements. */ function inner_balanced_div($html, $lt) { $open = strpos($html, '>', $lt); if ($open === false) return ''; $i = $open + 1; $start = $i; $len = strlen($html); $depth = 1; while ($i < $len && $depth > 0) { $next = strpos($html, '<', $i); if ($next === false) break; if (strncasecmp(substr($html, $next, 5), ' newlines before stripping tags. $fragment = preg_replace('##i', "\n", $fragment); // Drop any stray script/style. $fragment = preg_replace('#<(script|style)\b[^>]*>.*?#is', '', $fragment); // Block-level tags imply a line break. $fragment = preg_replace('##i', "\n", $fragment); $text = strip_tags($fragment); $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); return $text; } /* -- Find the song on Genius ----------------------------------------------- */ $query = rawurlencode(trim($artist . ' - ' . $title, ' -')); $search = http_get('https://api.genius.com/search?q=' . $query, $timeout, $headers); if ($search === null) json_out($empty); $json = json_decode($search, true); $hits = dot_get($json, 'response.hits', []); if (!is_array($hits) || count($hits) === 0) json_out($empty); $pageUrl = (string) dot_get($hits, '0.result.url', ''); // Optionally prefer the first English-language match (a few extra API calls). if (cfg('lyrics.prefer_english')) { $checked = 0; foreach ($hits as $h) { if ($checked++ >= 5) break; $id = dot_get($h, 'result.id', null); if (!$id) continue; $songRaw = http_get('https://api.genius.com/songs/' . rawurlencode((string) $id), $timeout, $headers); if ($songRaw === null) continue; if (dot_get(json_decode($songRaw, true), 'response.song.language', '') === 'en') { $pageUrl = (string) dot_get($h, 'result.url', $pageUrl); break; } } } if ($pageUrl === '') json_out($empty); /* -- Scrape the lyric containers ------------------------------------------- */ $page = http_get($pageUrl, $timeout); if ($page === null) { json_out(['found' => false, 'lyrics' => '', 'url' => $pageUrl]); } $marker = 'data-lyrics-container="true"'; $pieces = []; $offset = 0; while (($mpos = strpos($page, $marker, $offset)) !== false) { // Backtrack to the start of this opening
tag. $lt = strrpos(substr($page, 0, $mpos), ' false, 'lyrics' => '', 'url' => $pageUrl]); } json_out(['found' => true, 'lyrics' => $lyrics, 'url' => $pageUrl]);