fix: route sparse-metadata playlist tracks by yt-dlp's own metadata

SoundCloud sets (and similar) return flat-playlist entries without per-track
artist/title. When a track Hit has no artist, download via an output template
(-o <root>/%(artist,uploader,channel)s/<source>/...) so yt-dlp places the file
under the real artist instead of "Unknown Artist". yt_download gains an optional
outtmpl mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 06:55:56 -07:00
parent f103b6c253
commit 6730f1f141
2 changed files with 37 additions and 11 deletions

View File

@@ -60,13 +60,27 @@ def test_act_youtube_soundcloud_folder(monkeypatch):
def test_act_youtube_youtube_folder(monkeypatch):
captured = {}
monkeypatch.setattr(mf, "yt_download",
lambda url, target, quality, dry_run, hit=None: captured.update(target=target) or True)
lambda url, target, quality, dry_run, hit=None, outtmpl=None:
captured.update(target=target) or True)
h = mf.Hit(source="youtube", kind="track", title="T", artist="A",
payload={"videoId": "vid", "extractor": "youtube"})
mf.act_youtube(h, "/media/music", "best", False)
assert captured["target"] == "/media/music/A/youtube"
def test_act_youtube_unknown_artist_uses_metadata_template(monkeypatch):
captured = {}
monkeypatch.setattr(mf, "yt_download",
lambda url, target, quality, dry_run, hit=None, outtmpl=None:
captured.update(target=target, outtmpl=outtmpl) or True)
h = mf.Hit(source="youtube", kind="track", title="", artist="",
payload={"videoId": None, "extractor": "soundcloud", "url": "https://soundcloud.com/a/t"})
mf.act_youtube(h, "/media/music", "best", False)
assert captured["target"] is None
assert "%(artist,uploader,channel)s" in captured["outtmpl"]
assert captured["outtmpl"].endswith("/soundcloud/%(title)s [%(id)s].%(ext)s")
# ---- download_single per-source folder ----
def test_download_single_bandcamp_folder(monkeypatch):
monkeypatch.setattr(mf, "run_yt_dlp_get_metadata",