Self-hosted, always-on playlist sync for Spotify, TIDAL, Qobuz, Deezer, Amazon Music, Apple Music, and YouTube Music β plus a local, Jellyfin-ready audio mirror.
A free, open-source, self-hosted alternative to Soundiiz, TuneMyMusic, and FreeYourMusic that you own and run.
One-way, authoritative-group, or full bidirectional (N-way) sync Β· one-off playlist transfers Β· ISRC-accurate matching Β· all from your browser
Quick Start Β· Features Β· Screenshots Β· Docker Β· How it works Β· Report Bug Β· Request Feature
Share this project
Set it up once β every playlist you curate stays mirrored across every service, in date-added order.
Note
Web app + headless CLI, one engine. Click through a browser UI to connect services, build syncs, and transfer playlists β or run it .env + cron style. Both drive the same sync core.
Table of contents
- β¨ Features
- πΈ Screenshots
- π Quick Start
- π³ Always running: Docker
- βοΈ How it works
- π¦ Playlist metadata backups
- πΏ Local download mirror (Jellyfin)
- π Connecting each service
- π₯οΈ Headless CLI
- π‘οΈ Safety rails
- ποΈ Caching & song archive
- π§± Project layout
- π©Ί Troubleshooting
- π License
SongMirror keeps your playlists identical everywhere without manual re-adding, one-by-one copying, or a paid cloud service holding your library. It is cross-platform, self-hosted, and open source.
- π True mirroring, not append-only β adds and removals. Choose a source of truth (Spotify by default) and the others follow it.
- β Authoritative groups β trust two or more services (for example Spotify + Apple Music) while every other selected service remains a destination-only mirror.
- β Bidirectional N-way sync β an add or removal on any connected service propagates to all the others, echo-free, behind removal guards.
- β₯ Liked and favorite tracks β sync each service's built-in liked collection across all seven music providers, either into the destination's own favorites or a new named playlist.
- π― ISRC-accurate matching β exact recording identity where available, with Unicode-aware fuzzy title/artist/duration fallbacks (feat-credit drift, "- 2015 Remaster" suffixes, non-Latin scripts, video-only uploads β all handled).
- ποΈ Multiple named syncs β set up as many independent syncs as you like, each with its own services, playlists, schedule, and safety caps.
- βͺοΈ One-off transfers β copy any playlist from one service to another with a live progress bar; pause, resume, or stop mid-copy, and manually resolve unmatched tracks.
- π Followed playlists β sync and transfer playlists you follow but don't own, not just ones you created.
- π¦ Portable metadata backups β download one playlist or a service's entire library as ordered, versioned JSON/XML; single playlists also export as import-ready Soundiiz JSON.
- πΏ Local download mirror β keep offline audio, one folder per playlist in Jellyfin's
AlbumArtist/Albumlayout, with covers and an auto-updated.m3u8. - π‘οΈ Safety rails β dry-run by default, per-pass add/removal caps, net-loss protection, empty-snapshot guard, fail-closed on expired tokens.
- ποΈ Ever-growing song archive β every track ever seen is recorded in a local SQLite database (name, artist, album, ISRC, raw metadata, first/last seen).
- π³ Runs anywhere β one
docker compose up -dfor the browser app, or plain CLI + cron / Task Scheduler.
Important
Self-hosted and private by design. Your listening data and credentials never leave your machine. The web UI has no login β bind it to your LAN and don't port-forward it to the internet.
One dashboard for every library β sync status, jobs, live activity, and service health
Set up any number of syncs β one-way, authoritative-group, or bidirectional β in a short wizard
Connect every service in your browser β one-click OAuth, guided token paste, or an API key
Browse and pair playlists across services
The fastest way to run it is Docker β Compose pulls the published image, serves the web UI, and runs your syncs on schedule.
For a persistent installation with automatic restarts:
git clone https://github.com/ahnafnafee/songmirror.git
cd songmirror
docker compose up -dOr try the public GHCR image directly without cloning the repository:
docker run --rm -d --name songmirror -p 127.0.0.1:8888:8080 ghcr.io/ahnafnafee/songmirror:latestThen open http://localhost:8888 and connect your services in the browser. The Compose setup needs no .env to start; everything is configured in the UI and saved under ./data.
The direct docker run option is disposable: docker stop songmirror removes the container and its configuration. Use Compose for a durable installation with persistent credentials, caches, and downloads, or see the container image guide for tags and digest pinning.
Prefer running it without Docker?
uv sync
uv run uvicorn songmirror.web:app --host 0.0.0.0 --port 8080 # then open http://127.0.0.1:8080Requires
uv(Python 3.13+). For the local download mirror, alsouv tool install spotdland haveffmpegon PATH.
The Docker container is the recommended deployment: it serves the web UI, runs your syncs on their schedules, and restarts with the host. Compose pulls ghcr.io/ahnafnafee/songmirror:latest, runs it as songmirror, and persists all auth + caches in ./data.
docker compose up -d # pull the published image + start in the background
# open http://<host>:8888 and connect your services + create syncs in the browser
docker compose logs -f # watch it workTo update, run docker compose up -d --pull always. To build the current checkout instead, run docker compose up -d --build. See the container image guide for tags, digest pinning, direct pulls, verification, updates, and rollback.
No .env is needed to start β everything is configured in the browser and saved under ./data. OAuth, partner-token, and API-key setup all live on the Accounts page; each wizard explains the service-specific prerequisites and exact callback URI. Then build your syncs on the Sync page.
Opening SongMirror from another computer works at http://<server>:8888. The default Spotify connection uses a pasted sp_dc web session, so it needs no developer app or callback URL. If you intentionally use the legacy developer-app OAuth fallback behind Docker or a reverse proxy, set the browser-visible base URL in .env:
SPOTIFY_AUTH_MODE=oauth
SPOTIFY_CLIENT_ID=your-client-id
SPOTIFY_CLIENT_SECRET=your-client-secret
SONGMIRROR_PUBLIC_URL=https://music.example.comSongMirror will then advertise https://music.example.com/oauth/spotify/callback; register that exact URI in the Spotify app dashboard and recreate the container with docker compose up -d --force-recreate. A reverse-proxy base path is supported too (for example, https://example.com/songmirror). Spotify requires HTTPS for every non-loopback redirect; plain HTTP is accepted only with literal loopback addresses such as 127.0.0.1, not a LAN IP or localhost.
| Image | ghcr.io/ahnafnafee/songmirror:latest supports AMD64 and ARM64. Each build is also published with a commit-specific sha-... tag; Git tags such as v1.2.3 additionally publish 1.2.3, 1.2, and 1. Use the container image guide to pin an immutable digest. |
| Port | The UI is published on host 8888 (the 8888:8080 mapping in docker-compose.yml; change the host side if it clashes). LAN-only β don't port-forward it to the internet; the UI has no login yet. |
| Persistence | ./data holds credentials, tokens, caches, and the song archive. Back it up to keep your setup across rebuilds. |
| Downloads | Set DOWNLOAD_DIR (in .env or your shell) to your host music dir (e.g. F:\Torrent\Music); compose bind-mounts it to /music. From Docker, set JELLYFIN_URL to http://host.docker.internal:8096. |
| Expired sessions | Renewable sessions recover on the next scheduled or manual pass. TIDAL web, Qobuz, and Apple Music tokens must be re-pasted on the Accounts page when rejected; no restart is needed. |
Every pass, for each selected playlist name that exists on the source:
- Snapshot the source playlist (tracks, ISRCs, added-at dates).
- Reconcile the same-named playlist on every selected, connected target concurrently through that service's account-authorized playlist API.
- Missing tracks are resolved (cached links β ISRC β scored search) and appended oldest-first; tracks gone from the source are removed behind guards.
- Optionally, spotDL syncs a local audio folder per playlist.
The default source of truth is Spotify, but one-way mode is provider-agnostic β any connected playlist peer can be the source instead.
Same hierarchy the cross-service tools use (TuneLink, MusicBrainz): hard identifier β search β fuzzy score.
- Cached link β once a source track is matched to a target's catalog id / video id, that link is stored and reused (immune to title drift).
- ISRC β exact recording identity where the service exposes it.
- Scored search β RapidFuzz
token_set_ratio+ Jaro-Winkler, over both the raw and romanized (anyascii) title and artist, anchored by duration. This handles, without hardcoding:- Multi-artist credits β one service lists every feature, another lists the primary (
Arijit Singh, Ved Sharma, β¦βArijit Singh). - Title decoration β
(feat. β¦),- 2015 Remaster,(From "β¦"), extra "Official Music Video" suffixes. - Transliteration β Cyrillic / Bengali / Greek / Arabic (
ΠΠ°ΠΌΠΈΠ½βKamin,নΰ§ΰ¦Άΰ¦Ύΰ¦° বΰ§ΰ¦ΰ¦ΎβNeshar Bojha). - Video-only tracks β YouTube search falls back to the
videosfilter for indie/OST tracks that live on YT only as uploads.
- Multi-artist credits β one service lists every feature, another lists the primary (
The duration anchor unlocks the looser title match, so a different version (Runaway - Piano Version) or a wrong-artist cover isn't accepted when its length disagrees. Tracks with no confident match are reported and skipped.
Use an authoritative group when you actively curate the same logical playlist on two or more services, but want every other selected service to follow them. A typical setup is Spotify + Apple Music as authorities, with TIDAL, Qobuz, Deezer, Amazon Music, and YouTube Music as mirrors.
- Membership comes only from authorities β a track added on Spotify or Apple Music propagates to the other authority and every mirror. A track added only on a mirror is drift; it is never imported back into the authorities.
- One order authority β choose which authority supplies playlist names and the ordering of additions. The other authorities still contribute membership changes.
- Confirmed removals propagate from either authority β an absence must appear in two consecutive complete reads before it can delete anything. A simultaneous authority-side addition wins over a removal.
- Mirrors never get a vote β deleting a track from a mirror repairs that mirror; it does not delete the track from Spotify or Apple Music.
- Safe first pass β every authority set has its own baseline. Its first successful pass may add missing tracks, but holds all removals until a later pass proves the baseline is stable.
- Fail closed β if any authority is disconnected, unreadable, or its playlist cannot be opened/created, that logical playlist is skipped instead of silently falling back to fewer authorities.
Removal writes remain opt-in and capped. Enable Mirror removals for the job (or set MAX_REMOVALS in headless mode) if mirrors should be pruned to match the authoritative set.
By default one provider is the source of truth and edits flow one way. In N-way mode every selected provider is a peer: add or remove a track on any one and the change propagates to the others.
Bidirectional sync is impossible statelessly, so each logical playlist's canonical membership is snapshotted after every clean pass. Each pass diffs every provider against that snapshot, unions the changes, and reconciles everyone to the result:
- Echo-free β a propagated add becomes part of the snapshot, so it's never bounced back.
- Add-wins on conflict β losing a song is worse than keeping an extra one.
- Read-collapse guard β if a provider suddenly reads far fewer tracks than the baseline (a transient API hiccup), it's skipped that pass so one bad read can't cascade a mass-delete.
- Same rails as one-way β per-pass
MAX_ADDS/MAX_REMOVALScaps and net-loss protection hold on every write side. - Removals are opt-in β
MAX_REMOVALSdefaults to 0, so a track that disappears from one provider (deleted there, or silently pulled by licensing) is kept on the others and only logged. Set a cap (or the UI's "Mirror removals" toggle) to propagate deletions.
Always dry-run first. Run without
--execute(or use Preview in the UI) and read the plan β it prints every proposed add/remove on every provider before anything is written.
On a sync's Playlists step, select the source service's built-in liked collection. SongMirror then asks where it should go on every selected destination: directly into that service's own liked/favorite collection, or into a new playlist whose suggested name you can edit. A new selection is liked-only; turn on Also sync every regular playlist or pick individual playlists to include both.
This works across Spotify Liked Songs, TIDAL/Qobuz/Deezer Favorite Tracks, Amazon Music My Likes, Apple Music Favorite Songs, and YouTube Music Liked Music. The same one-way, authoritative-group, and N-way reconciliation paths and safety caps apply. As with ordinary playlists, removal writes remain off by default until Mirror removals is enabled.
TIDAL's pasted web-player Bearer handles both ordinary playlists and native Favorite Tracks when it carries the web player's user read/write grants (r_usr and w_usr). This path does not use a TIDAL developer app, client ID, or API key. If an older or read-only browser token lacks either grant, capture a fresh signed-in OpenAPI request in Accounts.
Some of these integrations use the providers' first-party web interfaces and can change without notice; the feasibility assessment records the API and distribution constraints for each provider.
The Playlists page can download a fresh snapshot without requiring a second provider or a sync job:
- Use Local backup on a service card to save every playlist from that service in one versioned JSON or XML file.
- Open a playlist to export only that playlist. Its Soundiiz option follows Soundiiz's documented JSON import shape, so the downloaded track list can be uploaded through Soundiiz's Import Playlist β From File flow.
- SongMirror JSON/XML preserves playlist order and names plus provider track/occurrence IDs, available ISRCs, artists, albums, album track positions, durations, added dates, artwork links, and unavailable-entry markers. ID-less catalog ghosts remain in the backup instead of disappearing. Files contain no cookies, tokens, request headers, previews, or streaming-file URLs.
Exports are downloaded by the browser to the device running the UI; SongMirror does not need write access to a host backup directory. The schema_version field lets future releases evolve the lossless format without making old snapshots ambiguous.
Keep an offline audio copy of each synced playlist, one folder per playlist, via spotDL. Sync is true mirroring: new tracks are downloaded, removed tracks are deleted locally. The layout is Jellyfin-ready β point a Jellyfin music library at the download dir and both the tracks and the playlists appear, staying updated every pass:
<DOWNLOAD_DIR>/
<Playlist>/
<Playlist>.m3u8 # auto-(re)generated; Jellyfin imports it as a playlist
cover.jpg # the source playlist cover, highest resolution
<AlbumArtist>/
<Album>/
Artists - Title.mp3 # tagged + cover art embedded
Enable it by setting DOWNLOAD_DIR and installing spotDL + ffmpeg:
uv tool install spotdl # isolated CLI; or: pipx install spotdl
# ffmpeg required: winget install ffmpeg (or: spotdl --download-ffmpeg)- Incremental β after the first full download, only newly-added tracks are fetched; removed tracks (and their emptied album folders) are pruned. An interrupted run continues next pass.
- Newest-first
.m3u8β written in date-added order, newest at the top (setLOCAL_MIRROR_ORDER=oldestto flip). Rebuild covers / tags / mtimes from existing files withuv run main.py --refresh-local. - Playlist covers in Jellyfin β Jellyfin ignores a cover file next to an m3u, so set
JELLYFIN_URL+JELLYFIN_API_KEYand each pass uploads the real playlist cover via the Jellyfin API. - Audio quality β the source is YouTube, so without a YT Music Premium cookie the ceiling is ~128β160 kbps.
LOCAL_MIRROR_FORMAT=opuskeeps YouTube's native stream without an mp3 re-encode; a Premium cookie (LOCAL_MIRROR_COOKIE_FILE) unlocks 256 kbps AAC. Selectingflacchanges the output container but cannot turn a lossy source into lossless audio.
Monochrome's current FLAC path uses browser-gated, single-use playback resources rather than a stable, provider-authorized file-export API, so SongMirror does not automate it. Use the local mirror only for content you own or are otherwise authorized to copy.
In the web app, the Accounts page walks you through each service and shows the exact values to paste. Nothing is proxied through a third party.
SongMirror refreshes credentials just in time, not with a separate token-refresh timer. Every manual or scheduled sync pass validates the connectors it uses and renews supported access tokens before the first request (or once after an authentication rejection). It is normal for a short-lived access token to expire between passesβthe durable refresh token or renewal cookie is what matters. The Accounts page validates status when it loads or regains focus, but it is not the background keep-alive; enabled sync schedules are.
| Service | Renewal behavior |
|---|---|
| Spotify | The default connection mints a web-player access token from the saved sp_dc cookie on demand and retries with a new token after a 401; the underlying signed-in session can still be revoked. Legacy developer-app OAuth remains supported for existing installs. |
| TIDAL | A pasted web-player Bearer cannot be renewed and must be captured again after expiry. It does not require a developer app, client ID, or API key. |
| Qobuz | The pasted X-User-Auth-Token is used until Qobuz rejects it, then must be captured again. |
| Deezer | The short-lived Pipe JWT renews automatically from the saved refresh-token before use and once after a 401/403; rotated renewal state is persisted. |
| Amazon Music | The web access token renews through /pandaToken using the captured browser user agent, referer, and allowlisted cookies. The current POST config.json?skipToken=false flow bootstraps device context when needed, and rotated cookies are persisted. Logout, security changes, or server-side revocation still require a fresh capture. |
| Apple Music | The pasted Bearer and Media-User-Token cannot be renewed by SongMirror and must be captured again after rejection. |
| YouTube Music | Data API OAuth refreshes automatically within 60 seconds of expiry. Browser mode attempts Google's cookie rotation whenever a sync target is built; an already-expired browser session must be exported again. |
| Jellyfin | The API key has no access-token refresh cycle; replace it only if it is revoked or deleted. |
- Sign in at https://open.spotify.com.
- Open browser DevTools (
F12) β Application (Chrome/Edge) or Storage (Firefox) β Cookies βhttps://open.spotify.com. - Copy the value of the
sp_dccookie and paste it into Accounts β Spotify.
That single signed-in web session handles library browsing, playlist reads and writes, and catalog search. It does not require a Spotify developer app, API key, or Premium account. Treat sp_dc like a password: SongMirror stores it in its private data directory, but the integration uses Spotify's internal web-player operations and can need maintenance if Spotify changes them. Existing developer-app OAuth credentials remain a compatible fallback.
Sign in at https://listen.tidal.com, open DevTools β Network, open a playlist, and filter for openapi.tidal.com/v2. Copy a request's headers (or copy it as cURL) into the wizard. SongMirror keeps only the Bearer token and two-letter catalog country. That signed-in web session supports ordinary playlists and native Favorite Tracks reads, additions, and removals; no TIDAL developer app, client ID, or API key is used.
Only catalog metadata and the signed-in user's playlists are used; playback assets are outside this integration. Browser tokens are short-lived, so re-paste when the account reports Expired.
Sign in at https://play.qobuz.com, open DevTools β Network, and filter for api.json/0.2. Choose any request containing X-App-Id and X-User-Auth-Tokenβincluding an authenticated album/story requestβthen copy its request headers or copy it as cURL and paste it into the wizard. SongMirror persists only those two values, sends them using the same header-based flow as the web player, and discards cookies and unrelated browser metadata. No business API approval or user id is required; existing partner credentials remain a compatible environment fallback.
The adapter uses catalog search and playlist endpoints onlyβit does not request stream or file URLs.
Sign in at https://www.deezer.com, open DevTools β Network, and reload the page. Filter for auth.deezer.com/login/renew, copy that request's headers (or copy it as cURL), and paste it into the renewal field. Firefox may instead copy the request cookies as a bare semicolon-delimited block; that shape is accepted too. SongMirror retains only the dedicated refresh-token cookie and uses it to renew Deezer's short-lived Pipe JWT automatically. You may also paste a current pipe.deezer.com/api request as an immediate bootstrap, but it is not required when renewal is configured. Playlist additions and removals both use the renewable Pipe session; no arl cookie is needed. Existing developer OAuth tokens remain a compatible environment fallback.
No developer approval is required for the default connector. It uses the same authenticated GraphQL and token-renewal routes as the Amazon Music web player:
- Sign in at https://music.amazon.com and open DevTools β Network.
- Reload the page, filter for
config.json, and select the signed-in request. (pandaTokenworks too when it appears, but it is not required.) - Choose Copy request headers or Copy as cURL, then paste it into the renewal field. Keep the complete
User-Agent,Referer, andCookieheaders so SongMirror can replay the same browser context. - Optionally copy the signed-in
config.jsonResponse into the bootstrap field; SongMirror can normally fetch that device context using the renewal session.
SongMirror derives the same AmznMusic authorization value locally and refreshes it through music.amazon.com/pandaToken before expiry or once after an authentication rejection. During connection it uses the current browser-style config request when device context is needed, requires /pandaToken to mint an access token, and rejects the connection if Amazon revokes the Music renewal cookie. It stores only the browser user agent, language, Music referer, a named allowlist of Amazon authentication/session cookies, and limited Music-client device context; analytics, experiment, AWS-console, CSRF, and other unrelated browser data are discarded. Those retained cookies are still sensitive, so keep SongMirror private on your LAN. A logout, password/security change, or Amazon-side revocation can still require one fresh capture.
This is an unsupported first-party web-client interface and Amazon can change it without notice. The documented Amazon Music Web API is still a closed beta; approved partner credentials remain an optional fallback when configured through environment variables.
No Apple Developer account needed β two headers from music.apple.com are enough. Open https://music.apple.com, sign in, open DevTools β Network, play a song, filter for amp-api.music.apple.com, and from any request's headers copy:
authorization: Bearer eyJ...β Bearer token (theeyJ...part, withoutBearer)media-user-token: ...β User token (full value)
The connect wizard lets you paste the raw headers and parses the values for you. Tokens last months; re-paste them on the Accounts page when they expire.
Talks to the official YouTube Data API v3, whose OAuth refresh token is durable and survives restarts.
- In the Google Cloud console, create a project, enable YouTube Data API v3, and create an OAuth client of type TVs and Limited Input devices.
- On the OAuth consent screen, set Publishing status β In production (leaving it in "Testing" expires the token after 7 days).
- In the app, paste the client ID + secret and complete the on-screen device code.
Quota: the Data API allows 10,000 units/day (a search costs 100, an add/remove 50). Steady-state upkeep is cheap; a big first-time backlog can hit the cap and resume the next day.
Prefer .env + cron / Task Scheduler? The same engine runs headless.
uv sync
cp .env.example .env # fill in credentials
uv run main.py # dry run β prints every add/remove it *would* do
uv run main.py --execute # apply for realUseful flags:
uv run main.py --execute --playlists "Aurora,Chill" # only these pairs
uv run main.py --execute --loop --interval 15m # run forever
uv run main.py --execute --max-removals 100 # one-off larger cleanup
uv run main.py --execute --sync-mode group --sync-source spotify \
--authorities spotify,apple --providers spotify,apple,tidal,ytmusicKey env vars (see .env.example): the credentials for whichever providers you use, PLAYLISTS, SYNC_INTERVAL, MAX_ADDS / MAX_REMOVALS, DOWNLOAD_DIR, SYNC_MODE, SYNC_SOURCE, SYNC_AUTHORITIES, and PROVIDERS.
Removals are destructive, so they're guarded:
- Dry run is the default β nothing changes without
--execute(or the UI's real-sync action). - If the source returns 0 tracks for a playlist the target shows as non-empty, removals are skipped that pass (a transient API failure can't empty a playlist).
- Removals are off by default β
MAX_REMOVALS=0holds every removal back (logged, never applied), so a licensing takedown on one platform can't cascade a deletion to the rest. Opt in per sync with the "Mirror removals" toggle (or setMAX_REMOVALS), and even then more pending removals than the cap in one pass β all skipped and logged. - More than
MAX_ADDSpending additions β the rest continue next pass (giant one-burst backfills are what trip bot detection). - Net-loss protection β a target-side track resembling a source track that has no match on that service is held, not deleted.
- Any provider authentication failure aborts that provider's pass immediately β no partial deletes on expired tokens.
Everything resolvable is cached so steady-state passes are near-instant: per-service resolve caches (ISRC + search, including misses), a snapshot_id-keyed track-list cache, hard identifier links in SQLite, and a per-pair snapshot-skip (unchanged since last clean sync).
Every pass also archives the metadata of every track it sees into song_cache.db β a SQLite file that only ever grows. Removed tracks stay archived with name, artist, album, duration, ISRC, raw snapshot JSON, and first/last-seen timestamps:
sqlite3 song_cache.db "SELECT name, artist, album, first_seen FROM songs ORDER BY first_seen DESC LIMIT 20"CLI entry: uv run main.py (thin shim) or python -m songmirror. Web entry: songmirror.web:app.
songmirror/
engine/ # provider-agnostic sync core (no web deps): runner, matching, targets/, spotify, downloads, archive
services/ # stateful services over the engine: accounts/ connectors, syncs, sync_service, transfers, playlists, settings
web/ # FastAPI app: thin HTTP/SSE over services/ (routers/)
frontend/ # React + Vite SPA (built and served by the API in production)
Adding another service: subclass MirrorTarget, implement ~8 methods, add its builder to engine/targets' _REGISTRY, and add a matching Connector under services/accounts. All reconciliation β diff, ordering, safety rails, logging, snapshot-skip β is inherited.
Missing required environment variableβ fill in.env(CLI) or connect the service in the UI.- TIDAL, Qobuz, or Apple reports
Expired/401/403β these pasted sessions have no renewable secret; capture a fresh signed-in request or token in Accounts. - TIDAL says the web-player token lacks liked-track access β capture a fresh signed-in
openapi.tidal.com/v2request in Accounts. Favorite Tracks needs the web session'sr_usrandw_usrgrants; it does not use a developer API key. - Deezer renewal fails β capture a fresh
auth.deezer.com/login/renewrequest (or itsrefresh-tokencookie). A current Pipe Bearer alone is only a temporary bootstrap. - Amazon Music renewal fails β capture a fresh signed-in
POST /config.json?skipToken=falserequest with its completeUser-Agent,Referer, andCookieheaders. The response JSON is optional. - YouTube Music browser mode expires β export fresh browser request headers. For the most durable unattended setup, use Data API OAuth with an in-production consent screen.
- Spotify reports Expired β sign in again at
open.spotify.comand paste a freshsp_dccookie in Accounts. - A playlist isn't syncing β confirm it's in the sync's playlist scope and exists on the source (targets are auto-created on a real pass).
Copyright Β© 2026 Ahnaf An Nafee.
This project is MIT licensed.
