Playlists: resolve synthetic mirrored batch refs (youtube_mirrored_<pk>/auto_mirror_<pk>) to PK
This commit is contained in:
parent
c6e077fefe
commit
47889387ad
3 changed files with 58 additions and 2 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue