From 04a29b684d6f5b723eecdc4f6f9bfa27e6b17d41 Mon Sep 17 00:00:00 2001 From: Honey Date: Sun, 26 Jul 2026 21:19:09 +0200 Subject: [PATCH] fix(backend): match official audio by yt-dlp artist metadata, not just channel name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CLAUDE.md | 7 +++++++ backend/lib/ytdlp.js | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9d5b1e5..39c3c89 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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=` 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 diff --git a/backend/lib/ytdlp.js b/backend/lib/ytdlp.js index b49277f..efbb36e 100644 --- a/backend/lib/ytdlp.js +++ b/backend/lib/ytdlp.js @@ -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'];