Playlists: resolve synthetic mirrored batch refs (youtube_mirrored_<pk>/auto_mirror_<pk>) to PK

This commit is contained in:
BoulderBadgeDad 2026-06-12 17:27:35 -07:00
parent c6e077fefe
commit 47889387ad
3 changed files with 58 additions and 2 deletions

View file

@ -19,6 +19,31 @@ from urllib.parse import parse_qs, urlparse
_SPOTIFY_ID_RE = re.compile(r"^[A-Za-z0-9]{16,32}$")
# Synthetic batch playlist_id prefixes that wrap a mirrored_playlists PK.
# Download/discovery flows build a batch playlist_id as f"{prefix}{pk}" — e.g.
# auto_mirror_<pk> (core/automation/handlers/sync_playlist.py), youtube_mirrored_<pk>
# (YouTube discovery), and mirrored_<pk> (web_server url hashes). The trailing
# digits are the mirrored_playlists primary key, NOT an upstream source id, so a
# (source, source_playlist_id) lookup will never match them.
_MIRRORED_PK_PREFIXES = ("youtube_mirrored_", "auto_mirror_", "mirrored_")
def extract_mirrored_pk(playlist_ref: object) -> Optional[int]:
"""Return the mirrored_playlists PK from a synthetic batch ref, else None.
Handles the synthetic forms above plus a bare numeric ref. Anything else
(a real upstream source id) returns None so the caller falls back to a
(source, source_playlist_id) lookup.
"""
ref = str(playlist_ref or "").strip()
if not ref:
return None
for prefix in _MIRRORED_PK_PREFIXES:
if ref.startswith(prefix):
tail = ref[len(prefix):]
return int(tail) if tail.isdigit() else None
return int(ref) if ref.isdigit() else None
@dataclass(frozen=True)
class MirroredSourceRef:

View file

@ -13704,8 +13704,12 @@ class MusicDatabase:
row = self.get_mirrored_playlist_by_source(default_source, ref, profile_id)
if row:
return row
if ref.isdigit():
return self.get_mirrored_playlist(int(ref))
# Fallback: bare numeric ref or a synthetic batch id (auto_mirror_<pk>,
# youtube_mirrored_<pk>, mirrored_<pk>) whose trailing digits are the PK.
from core.playlists.source_refs import extract_mirrored_pk
pk = extract_mirrored_pk(ref)
if pk is not None:
return self.get_mirrored_playlist(pk)
return None
def set_mirrored_playlist_organize_by_playlist(

View file

@ -55,3 +55,30 @@ def test_empty_refs_return_none(tmp_path):
assert db.resolve_mirrored_playlist(None) is None
assert db.resolve_mirrored_playlist('') is None
assert db.resolve_mirrored_playlist(' ') is None
def test_synthetic_mirrored_batch_ref_resolves_by_pk(tmp_path):
"""A discovery/mirror batch carries a synthetic playlist_id like
youtube_mirrored_<pk> with batch source 'mirrored'. (source, source_playlist_id)
can't match it — it must resolve via the embedded PK. Regression for the
organize-by-playlist 'all found but no folder built' report."""
db = MusicDatabase(str(tmp_path / "m.db"))
pk = db.mirror_playlist(source='youtube', source_playlist_id='abc123XYZ',
name='My Mirror', tracks=[], profile_id=1)
assert pk
for ref in (f'youtube_mirrored_{pk}', f'auto_mirror_{pk}', f'mirrored_{pk}'):
row = db.resolve_mirrored_playlist(ref, profile_id=1, default_source='mirrored')
assert row is not None and row['id'] == pk, ref
def test_extract_mirrored_pk_pure():
from core.playlists.source_refs import extract_mirrored_pk
assert extract_mirrored_pk('youtube_mirrored_63') == 63
assert extract_mirrored_pk('auto_mirror_7') == 7
assert extract_mirrored_pk('mirrored_12') == 12
assert extract_mirrored_pk('42') == 42 # bare PK
assert extract_mirrored_pk('908622995') == 908622995 # numeric upstream id (PK fallback only)
assert extract_mirrored_pk('37i9dQZF1DXcBWIGoYBM5M') is None # real spotify id
assert extract_mirrored_pk('youtube_mirrored_') is None # no digits
assert extract_mirrored_pk('') is None
assert extract_mirrored_pk(None) is None