Better dumb search
This commit is contained in:
1 parent
389b11e349
commit
a7775a6b87
3 files changed
+105
-21
No files matched your search
+65
-4
@@ -102,6 +102,46 @@ function request_track() {
|
||||
return [trim((string) $artist), trim((string) $title)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip a featuring-artist credit ("(feat. X)", "(ft. X)", "(featuring X)",
|
||||
* "(with X)", bracket variants, or an unparenthesized trailing "feat. X")
|
||||
* before it goes into a Genius/dumb search query.
|
||||
*
|
||||
* Genius's canonical song title never includes the featuring credit (it's
|
||||
* separate metadata), but translation/cover pages often spell it out
|
||||
* verbatim in their title — e.g. searching "Havana (feat. Young Thug)"
|
||||
* surfaces only German/French translation pages (titled "... ft. Young
|
||||
* Thug ... (Deutsche Übersetzung)" etc.), while "Havana" alone correctly
|
||||
* finds the original as the first hit. Leaves other qualifiers like
|
||||
* "(Remix)"/"(Live)" alone — those genuinely change which page you want.
|
||||
*/
|
||||
function strip_feature_credit($title) {
|
||||
$title = preg_replace('/[\(\[]\s*(?:feat\.?|ft\.?|featuring|with)\b[^\)\]]*[\)\]]/i', '', $title);
|
||||
$title = preg_replace('/\s+(?:feat\.?|ft\.?|featuring)\b.+$/i', '', $title);
|
||||
return trim(preg_replace('/\s{2,}/', ' ', $title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loose "is this search hit actually by the artist we want" check —
|
||||
* case-insensitive, ignores punctuation/spacing and anything in brackets
|
||||
* (so "Camila Cabello (Ft. Young Thug)" still matches "Camila Cabello").
|
||||
* An empty side always matches (nothing to compare against). Used to skip
|
||||
* past hits attributed to someone else entirely — most notably Genius's
|
||||
* "Genius Deutsche Übersetzungen" / "Genius traductions françaises" / etc.
|
||||
* pseudo-artist accounts that translation pages get filed under.
|
||||
*/
|
||||
function artist_loosely_matches($hitArtist, $wantArtist) {
|
||||
$norm = function ($s) {
|
||||
$s = preg_replace('/[\(\[][^\)\]]*[\)\]]/', '', (string) $s);
|
||||
$s = preg_replace('/[^\p{L}\p{N}]+/u', '', $s);
|
||||
return strtolower($s);
|
||||
};
|
||||
$a = $norm($hitArtist);
|
||||
$b = $norm($wantArtist);
|
||||
if ($a === '' || $b === '') return true;
|
||||
return strpos($a, $b) !== false || strpos($b, $a) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inner HTML of a balanced <div> whose opening tag starts at $lt.
|
||||
* Correctly handles nested <div> elements. Shared by the Genius and dumb
|
||||
@@ -154,15 +194,36 @@ function container_to_text($fragment) {
|
||||
* templates, these regexes may need updating.
|
||||
*/
|
||||
|
||||
/** Search dumb for a track; returns the first song result's page path (e.g. "/Artist-title-lyrics"), or '' if none. */
|
||||
/**
|
||||
* Search dumb for a track; returns a song result's page path (e.g.
|
||||
* "/Artist-title-lyrics"), or '' if none. Scans the first 5 song hits for
|
||||
* one actually attributed to $artist (see artist_loosely_matches) and
|
||||
* returns that; falls back to the top hit if none match rather than
|
||||
* returning nothing, so an unusual artist-name spelling can't regress
|
||||
* availability entirely.
|
||||
*/
|
||||
function dumb_search($artist, $title, $timeout) {
|
||||
$base = cfg('dumb.url');
|
||||
if ($base === '') return '';
|
||||
$query = rawurlencode(trim($artist . ' - ' . $title, ' -'));
|
||||
$query = rawurlencode(trim($artist . ' - ' . strip_feature_credit($title), ' -'));
|
||||
$html = http_get($base . '/search?q=' . $query, $timeout);
|
||||
if ($html === null) return '';
|
||||
if (!preg_match('/<h2>\s*Songs\s*<\/h2>.*?<a[^>]+id="search-item"[^>]+href="([^"]+)"/is', $html, $m)) return '';
|
||||
return html_entity_decode($m[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
|
||||
if (!preg_match('/<h2>\s*Songs\s*<\/h2>(.*?)(?=<h2>|$)/is', $html, $section)) return '';
|
||||
if (!preg_match_all('/<a[^>]+id="search-item"[^>]+href="([^"]+)"[^>]*>.*?<span>([^<]*)<\/span>/is', $section[1], $hits, PREG_SET_ORDER)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$fallback = '';
|
||||
$checked = 0;
|
||||
foreach ($hits as $h) {
|
||||
$href = html_entity_decode($h[1], ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
if ($fallback === '') $fallback = $href;
|
||||
if ($checked++ >= 5) break;
|
||||
$hitArtist = html_entity_decode($h[2], ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
if (artist_loosely_matches($hitArtist, $artist)) return $href;
|
||||
}
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user