The curl_cffi fallback in spotify_resolve.py was silently catching
SpotipyFree ImportErrors and hitting api.spotify.com/v1/ directly —
the same rate-limited endpoint. Removed it entirely.
Also explicitly pip-install SpotipyFree before TzurSoffer's package
so the import is always available regardless of dependency resolution.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SpotipyFree wraps SpotAPI which uses tls_client to impersonate Chrome's
TLS fingerprint and hits Spotify's internal endpoints. This is exactly
what TzurSoffer's spotdl fork uses. Removes the complex GQL hash
extraction (which couldn't find hashes in lazy-loaded webpack chunks).
curl_cffi + TOTP kept as fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Log each Python stderr line separately (no 500-char truncation) so
we can see exactly what's happening in the bundle scan
- Try embed page (open.spotify.com/embed/...) first for GQL hashes —
focused app, fewer bundles, GQL chunks directly referenced
- Suppress noisy bundle-fetch errors to keep logs readable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fetchPlaylist/getAlbum/getTrack GraphQL hashes are not in homepage bundles
— they live in lazy-loaded chunks that only appear on the playlist/album/track
page. Fetch the specific page HTML to find the extra JS bundles, then scan
those for the persisted query hash. Also improved hash regex (no semicolon
restriction, wider window, fallback nearest-hex search).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
api.spotify.com/v1 is IP-banned for 24h. Switch to Spotify's internal
partner API (api-partner.spotify.com/pathfinder/v1/query) which uses
separate infrastructure and rate limits. Persisted query hashes are
extracted dynamically from JS bundles alongside the TOTP secret.
Falls back to REST only if GQL hash not found or partner API fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
spotdl save hangs for 60s initializing its full pipeline. Rewrite
spotify_resolve.py to use curl_cffi (already installed) which
impersonates Chrome's TLS fingerprint directly. TOTP token fetched
in Python too. RATE_LIMITED propagates without TOTP fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
spotdl doesn't handle tracking params in URLs. Also separate stdout/stderr
in Python script and always try parsing stdout before checking exit code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>