Fix: auto-sync capped public Spotify playlists at 100 tracks (#838)
Regression in 2.6.9. The spotify_public source adapter (used by auto-sync / refresh_mirrored) called scrape_spotify_embed() directly — the embed widget only exposes ~100 tracks — instead of fetch_spotify_public(), the wrapper the rest of the app uses, which pulls the full list via the paginated public API and only falls back to the embed on failure. So initial discovery got the whole playlist but every auto-sync re-fetch truncated it to 100. Switched the adapter to fetch_spotify_public (same return shape — drop-in). Albums still resolve via the embed (already whole); on any failure it falls back to the embed exactly as before. Test: the adapter returns all 150 tracks when the full fetch yields 150 (was capped at 100). 29 adapter tests pass.
This commit is contained in:
parent
56bdcee434
commit
a15fe15842
2 changed files with 38 additions and 2 deletions
|
|
@ -35,14 +35,19 @@ class SpotifyPublicPlaylistSource(PlaylistSource):
|
|||
"""``playlist_id`` is a Spotify URL or ``open.spotify.com`` URI."""
|
||||
from core.spotify_public_scraper import (
|
||||
parse_spotify_url,
|
||||
scrape_spotify_embed,
|
||||
fetch_spotify_public,
|
||||
)
|
||||
|
||||
parsed = parse_spotify_url(playlist_id)
|
||||
if not parsed:
|
||||
return None
|
||||
|
||||
data = scrape_spotify_embed(parsed["type"], parsed["id"])
|
||||
# #838: use the full-fetch wrapper (paginates past the embed widget's
|
||||
# ~100-track cap), NOT scrape_spotify_embed directly. The rest of the app
|
||||
# already uses fetch_spotify_public; the adapter calling the capped embed
|
||||
# scraper is why auto-sync truncated >100-track playlists to 100 while the
|
||||
# initial discovery got the whole thing. Same return shape, so drop-in.
|
||||
data = fetch_spotify_public(parsed["type"], parsed["id"])
|
||||
if not isinstance(data, dict) or data.get("error"):
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -915,3 +915,34 @@ def test_mirror_dict_spotify_public_emits_spotify_hint():
|
|||
extra = _json.loads(d["extra_data"])
|
||||
assert extra["discovered"] is False
|
||||
assert extra["spotify_hint"]["id"] == "sptrk"
|
||||
|
||||
|
||||
def test_spotify_public_adapter_paginates_past_100(monkeypatch):
|
||||
"""#838: auto-sync truncated >100-track playlists to 100 because the adapter
|
||||
called the embed scraper (≤100) directly instead of the full-fetch wrapper.
|
||||
With the wrapper, a playlist whose full fetch returns 150 tracks keeps all 150."""
|
||||
src = SpotifyPublicPlaylistSource()
|
||||
|
||||
monkeypatch.setattr(
|
||||
"core.spotify_public_scraper.parse_spotify_url",
|
||||
lambda url: {"type": "playlist", "id": "big"},
|
||||
)
|
||||
full = {
|
||||
"id": "big", "type": "playlist", "name": "Big PL", "subtitle": "owner",
|
||||
"url": "https://open.spotify.com/playlist/big", "url_hash": "bighash",
|
||||
"tracks": [
|
||||
{"id": f"t{i}", "name": f"Song {i}", "artists": [{"name": "A"}],
|
||||
"duration_ms": 1000, "is_explicit": False, "track_number": i + 1}
|
||||
for i in range(150)
|
||||
],
|
||||
}
|
||||
# The full paginated path succeeds → wrapper returns all 150 (no embed cap).
|
||||
monkeypatch.setattr(
|
||||
"core.spotify_public_api.fetch_public_playlist_full",
|
||||
lambda spotify_id: full,
|
||||
)
|
||||
|
||||
detail = src.get_playlist("https://open.spotify.com/playlist/big")
|
||||
assert detail is not None
|
||||
assert len(detail.tracks) == 150, "pre-#838 the adapter capped this at 100"
|
||||
assert detail.meta.track_count == 150
|
||||
|
|
|
|||
Loading…
Reference in a new issue