fix(backend): match official audio by yt-dlp artist metadata, not just channel name
The strict cascade steps required the YouTube channel name to contain the artist, so official uploads on label channels (BANGTANTV, HYBE LABELS, …) were rejected and the no-channel fallback grabbed a fan reupload instead (observed: BTS "Pied Piper"). Add yt-dlp's music `artist` field as an OR alternative to the channel check in the strict steps (two --match-filter args = OR). A metadata dump of "BTS Pied Piper" confirmed only the official upload has artist=BTS (fan/live/lyric reuploads: NA), and it's the #1 result — so it now passes strict step 2. channel_is_verified was rejected as a signal: lyric videos and fan MVs also run on verified channels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
f097dddc64
commit
04a29b684d
2 files changed
+22
-2
No files matched your search
@@ -53,6 +53,13 @@ Backend runs in Docker (see `docker-compose.yml`); on the deploy host it lives a
|
||||
- **Search cascade** (`ytdlp.js`): YTM search first (`web_music` player client, Songs filter
|
||||
param), then regular YouTube; each candidate filtered by duration (±10%, min 10s),
|
||||
title keyword, artist channel, and variant exclusions (remix/cover/sped/…).
|
||||
- **Official audio via `artist` metadata (not just channel).** The strict steps accept a candidate
|
||||
if the channel name matches the artist **OR** yt-dlp's music `artist` field does. YouTube "Art
|
||||
Tracks" (label-provided official audio) carry `artist=<Artist>` even when the channel is
|
||||
`BANGTANTV`/`HYBE LABELS`/… (no artist name). Confirmed on "BTS Pied Piper": only the official
|
||||
upload had `artist=BTS`; every fan/live/lyric reupload had `artist=NA`. `channel_is_verified` was
|
||||
rejected as a signal — lyric videos and fan MVs also run on verified channels. Implemented as two
|
||||
`--match-filter` args (yt-dlp OR's them) in `mf()`.
|
||||
- **"Mittelweg" rule** (with AND without duration): when nothing matches in the filtered steps,
|
||||
relax only the duration constraint while **still requiring the artist channel**. If nothing
|
||||
matches, download **nothing** and fail cleanly with "Nicht eindeutig gefunden — bitte
|
||||
|
||||
+15
-2
@@ -122,6 +122,12 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
||||
const artistFirstWord = (rawArtist || mainArtist).split(/[\s,/]+/)[0];
|
||||
const artistChannelPart = (artistFirstWord.length >= 3 && !JP_RE.test(artistFirstWord))
|
||||
? ` & channel ~= (?i)${escRe(artistFirstWord)}` : '';
|
||||
// yt-dlp fills `artist` (music metadata) on YouTube "Art Tracks" — the label-provided official
|
||||
// audio — even when the channel doesn't contain the artist name (BANGTANTV/HYBE/…). Verified with
|
||||
// "BTS Pied Piper": ONLY the official upload had artist=BTS; every fan/live/lyric reupload had
|
||||
// none. Used as an OR alternative to the channel check so those official uploads still pass strict.
|
||||
const artistMetaPart = (artistFirstWord.length >= 3 && !JP_RE.test(artistFirstWord))
|
||||
? ` & artist ~= (?i)${escRe(artistFirstWord)}` : '';
|
||||
|
||||
// Use a space (not " - ") between artist and title: this matches how users type
|
||||
// in the YTM search box and ranks the exact official track first. A dash skews
|
||||
@@ -240,8 +246,15 @@ async function downloadTrack({ jobId, artist, rawArtist, albumArtist, title, alb
|
||||
} else {
|
||||
durPart = '';
|
||||
}
|
||||
return ['--match-filter',
|
||||
`${durPart}!is_live${titleKwPart}${strict ? artistChannelPart : ''}${variant ? variantPart : ''}`];
|
||||
const base = `${durPart}!is_live${titleKwPart}${variant ? variantPart : ''}`;
|
||||
// Non-strict, or no artist constraint (short / Japanese name): single filter.
|
||||
if (!strict || !artistChannelPart) return ['--match-filter', base];
|
||||
// Strict: pass if the channel name matches the artist OR yt-dlp's music `artist` metadata does
|
||||
// (catches official label-channel uploads like BANGTANTV). Two --match-filter args = OR in yt-dlp.
|
||||
return [
|
||||
'--match-filter', `${base}${artistChannelPart}`,
|
||||
'--match-filter', `${base}${artistMetaPart}`,
|
||||
];
|
||||
};
|
||||
|
||||
const PI_FILTERED = ['--playlist-items', '1-5', '--max-downloads', '1'];
|
||||
|
||||
Reference in new issue
Block a user