5.2 KiB
5.2 KiB
NaviMigrate
Spotify -> Navidrome playlist transfer tool in Go.
What it does
- Authenticates with Spotify using Authorization Code + PKCE (no client secret required).
- Fetches one or more Spotify playlists by URL/URI/ID.
- Optionally transfers Spotify liked songs as a dedicated playlist.
- Searches for matching tracks in Navidrome using Subsonic
search3. - Creates matching playlists in Navidrome and adds matched tracks.
- Writes a JSON transfer report with unmatched tracks and errors.
- Optional recovery mode: for unmatched tracks, search Qobuz and download whole albums using
qobuz-dl. - Optional re-add mode: after Navidrome rescan, re-match downloaded tracks and add them to existing playlists.
Requirements
- Go 1.22+
- Spotify app client ID with redirect URI configured (default:
http://127.0.0.1:8888/callback) - If using
--liked, Spotify scopes must includeuser-library-read - Navidrome URL, username, and password
Build
go build ./cmd/navimigrate
Usage
./navimigrate \
--spotify-client-id "<spotify-client-id>" \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--playlist-url "https://open.spotify.com/playlist/16DZpOTLqZvdbqxEavLmWk"
Useful flags
--playlist-url "..."(repeatable): Spotify playlist URL/URI/ID--liked: include Spotify liked songs in transfer--liked-playlist-name "Spotify Liked Songs": target playlist name for liked songs--dry-run: resolve matches only, do not create playlists or add tracks--match-threshold 45: minimum score needed to accept a match--remember-spotify=true|false: save and reuse Spotify refresh token (default true)--session-file ~/.config/navimigrate/session.json: session storage path--report transfer-report.json: report output path--concurrency 4: concurrent track match workers--spotify-manual-code=true|false: manual callback URL/code entry or local callback server--qobuz-download-missing: search/download albums for unmatched tracks--qobuz-manifest missing-downloads.json: manifest path used by recovery flows--qobuz-dl-path qobuz-dl: binary path, or qobuz-dl project directory--qobuz-output /path/to/music: output directory for qobuz-dl--add-downloaded-manifest missing-downloads.json: re-add tracks from manifest after Navidrome rescan--add-downloaded-force: retry entries already marked added
Liked-only run (no explicit playlist URLs):
./navimigrate \
--spotify-client-id "<spotify-client-id>" \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--liked
Navidrome self-test
Quick auth/search test without Spotify:
./navimigrate \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--navidrome-self-test
Write-path self-test (creates playlist, adds one track, then deletes playlist):
./navimigrate \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--navidrome-self-test \
--navidrome-self-test-write
Missing-track recovery flow
Step 1: transfer and download unmatched albums from Qobuz:
./navimigrate \
--spotify-client-id "<spotify-client-id>" \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--playlist-url "https://open.spotify.com/playlist/..." \
--qobuz-download-missing \
--qobuz-username "<qobuz-email>" \
--qobuz-password "<qobuz-password>" \
--qobuz-dl-path "/home/joren/dev/qobuz-dl" \
--qobuz-output "/path/to/downloads"
Step 2: after Navidrome rescans your library, re-add from manifest:
./navimigrate \
--navidrome-url "https://music.lofitrek.com" \
--navidrome-username "test_user" \
--navidrome-password "test_user" \
--add-downloaded-manifest "missing-downloads.json"
Environment variables
SPOTIFY_CLIENT_IDSPOTIFY_REDIRECT_URI(optional)SPOTIFY_SCOPES(optional)NAVIMIGRATE_SPOTIFY_MANUAL_CODE(optional, defaults to true)NAVIMIGRATE_REMEMBER_SPOTIFY(optional, defaults to true)NAVIMIGRATE_SESSION_FILE(optional)NAVIMIGRATE_INCLUDE_LIKED(optional)NAVIMIGRATE_LIKED_NAME(optional)NAVIDROME_URLNAVIDROME_USERNAMENAVIDROME_PASSWORDNAVIMIGRATE_DRY_RUN(optional)NAVIMIGRATE_CONCURRENCY(optional)NAVIMIGRATE_MATCH_THRESHOLD(optional)NAVIMIGRATE_REPORT(optional)NAVIMIGRATE_QOBUZ_DOWNLOAD_MISSING(optional)NAVIMIGRATE_QOBUZ_MANIFEST(optional)NAVIMIGRATE_QOBUZ_DL_PATH(optional)NAVIMIGRATE_QOBUZ_OUTPUT(optional)NAVIMIGRATE_QOBUZ_QUALITY(optional)QOBUZ_USERNAME(optional, required when qobuz download mode enabled)QOBUZ_PASSWORD(optional, required when qobuz download mode enabled)QOBUZ_APP_ID(optional)QOBUZ_APP_SECRET(optional)NAVIMIGRATE_ADD_DOWNLOADED_MANIFEST(optional)NAVIMIGRATE_ADD_DOWNLOADED_FORCE(optional)NAVIMIGRATE_NAVIDROME_SELF_TEST(optional)NAVIMIGRATE_NAVIDROME_SELF_TEST_WRITE(optional)NAVIMIGRATE_NAVIDROME_SELF_TEST_QUERY(optional)