build spotify-to-navidrome migrator with recovery flow

This commit is contained in:
2026-04-09 03:10:58 +02:00
parent 650a0c6a87
commit c1360a6423
23 changed files with 3383 additions and 0 deletions

View File

@@ -6,14 +6,18 @@ Spotify -> Navidrome playlist transfer tool in Go.
- 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 include `user-library-read`
- Navidrome URL, username, and password
## Build
@@ -36,10 +40,32 @@ go build ./cmd/navimigrate
### 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):
```bash
./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
@@ -64,18 +90,62 @@ Write-path self-test (creates playlist, adds one track, then deletes playlist):
--navidrome-self-test-write
```
### Missing-track recovery flow
Step 1: transfer and download unmatched albums from Qobuz:
```bash
./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:
```bash
./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_ID`
- `SPOTIFY_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_URL`
- `NAVIDROME_USERNAME`
- `NAVIDROME_PASSWORD`
- `NAVIMIGRATE_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)