185 lines
5.7 KiB
Markdown
185 lines
5.7 KiB
Markdown
# QTransfer
|
|
|
|
Spotify -> Qobuz playlist transfer tool in Go.
|
|
|
|
## Features
|
|
|
|
- Spotify OAuth login via Authorization Code + PKCE (client secret not required).
|
|
- Manual Spotify callback code entry (paste callback URL/code) enabled by default.
|
|
- Session cache for Spotify/Qobuz credentials and tokens (so you do not need to re-enter each run).
|
|
- Fetches all Spotify playlists and liked songs.
|
|
- Playlist URL mode (`--playlist-url`) for direct targeted transfers.
|
|
- Monitor mode to detect playlist updates (`--monitor`) with optional sync-to-Qobuz (`--monitor-transfer`) and sync modes (`--sync-mode`).
|
|
- Interactive selection prompt (or non-interactive flags).
|
|
- Creates Qobuz playlists and fills them with best-effort track matches.
|
|
- Transfers liked songs into a dedicated playlist (not favorites).
|
|
- Writes a JSON transfer report with unmatched tracks and errors.
|
|
|
|
## Requirements
|
|
|
|
- Go 1.22+
|
|
- Spotify app client ID with redirect URI configured (default: `http://127.0.0.1:8888/callback`)
|
|
- Qobuz account username/password
|
|
|
|
## Build
|
|
|
|
```bash
|
|
go build ./cmd/qtransfer
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
./qtransfer login
|
|
```
|
|
|
|
`qtransfer login` runs an interactive setup and stores Spotify/Qobuz credentials/tokens in the session file.
|
|
|
|
After login, run transfers without re-entering credentials:
|
|
|
|
```bash
|
|
./qtransfer
|
|
```
|
|
|
|
For first-time non-interactive usage (without `login`), you can still pass flags:
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--spotify-client-id "<spotify-client-id>" \
|
|
--qobuz-username "<qobuz-user>" \
|
|
--qobuz-password "<qobuz-password>"
|
|
```
|
|
|
|
Credentials/tokens are cached in `~/.config/qtransfer/session.json` by default.
|
|
|
|
### Useful flags
|
|
|
|
- `--all`: transfer all Spotify playlists
|
|
- `--liked`: include liked songs as a generated Qobuz playlist
|
|
- `--playlist "Name"` (repeatable): transfer specific playlists by exact name
|
|
- `--playlist-url "..."` (repeatable): transfer specific Spotify playlists by URL/URI/ID
|
|
- `--config sync.toml`: TOML config with global options and per-playlist monitor sync jobs
|
|
- `--spotify-manual-code=true|false`: paste callback URL/code manually or use local callback server
|
|
- `--remember-creds=true|false`: persist/reuse tokens and credentials in session file
|
|
- `--session-file path`: custom session file path (default `~/.config/qtransfer/session.json`)
|
|
- `--monitor`: monitor selected playlists for updates
|
|
- `--monitor-interval 5m`: monitor polling interval
|
|
- `--monitor-once`: run one monitor check and exit
|
|
- `--monitor-transfer`: in monitor mode, sync changed playlists to Qobuz
|
|
- `--sync-mode append|mirror`: append only, or mirror source by recreating target playlist
|
|
- `--target-playlist-id 123456`: bind single monitored source playlist to an existing Qobuz playlist ID
|
|
- `--qobuz-self-test`: run Qobuz login/verify/search checks and exit (skips Spotify)
|
|
- `--qobuz-self-test-write`: when self-test is enabled, also create a test playlist and add one track
|
|
- `--qobuz-self-test-query "..."`: search query used during self-test
|
|
- `--non-interactive`: fail instead of prompting when no explicit selection is given
|
|
- `--dry-run`: resolve matches only, do not create playlists or add tracks
|
|
- `--report transfer-report.json`: report output path
|
|
- `--public-playlists`: create public Qobuz playlists
|
|
|
|
Quick auth check without waiting for Spotify:
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--qobuz-self-test \
|
|
--qobuz-username "<qobuz-user>" \
|
|
--qobuz-password "<qobuz-password>"
|
|
```
|
|
|
|
Transfer from direct Spotify playlist URLs:
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--spotify-client-id "<spotify-client-id>" \
|
|
--qobuz-username "<qobuz-user>" \
|
|
--qobuz-password "<qobuz-password>" \
|
|
--playlist-url "https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd" \
|
|
--playlist-url "spotify:playlist:37i9dQZF1DWY4xHQp97fN6"
|
|
```
|
|
|
|
Monitor selected playlists for changes:
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--spotify-client-id "<spotify-client-id>" \
|
|
--playlist-url "https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd" \
|
|
--monitor --monitor-interval 2m
|
|
```
|
|
|
|
Monitor with append-only sync (never removes from Qobuz):
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--playlist-url "https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd" \
|
|
--monitor --monitor-transfer --sync-mode append --monitor-interval 2m
|
|
```
|
|
|
|
Monitor with mirror sync (recreates playlist on change):
|
|
|
|
```bash
|
|
./qtransfer \
|
|
--playlist-url "https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd" \
|
|
--monitor --monitor-transfer --sync-mode mirror --monitor-interval 2m
|
|
```
|
|
|
|
Config-file mode:
|
|
|
|
```bash
|
|
./qtransfer --config sync.toml
|
|
```
|
|
|
|
Example `sync.toml`:
|
|
|
|
```toml
|
|
[global]
|
|
monitor = true
|
|
monitor_transfer = true
|
|
monitor_interval = "10m"
|
|
sync_mode = "append"
|
|
include_liked = true
|
|
|
|
[[playlist]]
|
|
url = "https://open.spotify.com/playlist/37i9dQZF1DX0XUsuxWHRQd"
|
|
sync_mode = "append"
|
|
target_playlist_id = 61646089
|
|
|
|
[[playlist]]
|
|
url = "spotify:playlist:37i9dQZF1DWY4xHQp97fN6"
|
|
sync_mode = "mirror"
|
|
enabled = true
|
|
```
|
|
|
|
Login command:
|
|
|
|
```bash
|
|
./qtransfer login
|
|
```
|
|
|
|
Logout command (removes cached session):
|
|
|
|
```bash
|
|
./qtransfer logout
|
|
```
|
|
|
|
### Environment variables
|
|
|
|
- `SPOTIFY_CLIENT_ID`
|
|
- `SPOTIFY_REDIRECT_URI` (optional)
|
|
- `SPOTIFY_SCOPES` (optional)
|
|
- `QTRANSFER_SPOTIFY_MANUAL_CODE` (optional, defaults to true)
|
|
- `QTRANSFER_SESSION_FILE` (optional)
|
|
- `QTRANSFER_REMEMBER_CREDS` (optional, defaults to true)
|
|
- `QTRANSFER_CONFIG` (optional)
|
|
- `QTRANSFER_MONITOR` (optional)
|
|
- `QTRANSFER_MONITOR_ONCE` (optional)
|
|
- `QTRANSFER_MONITOR_TRANSFER` (optional)
|
|
- `QTRANSFER_MONITOR_INTERVAL` (optional)
|
|
- `QTRANSFER_SYNC_MODE` (optional: append|mirror)
|
|
- `QTRANSFER_TARGET_PLAYLIST_ID` (optional, monitor mode only)
|
|
- `QTRANSFER_QOBUZ_SELF_TEST` (optional)
|
|
- `QTRANSFER_QOBUZ_SELF_TEST_WRITE` (optional)
|
|
- `QTRANSFER_QOBUZ_SELF_TEST_QUERY` (optional)
|
|
- `QOBUZ_USERNAME`
|
|
- `QOBUZ_PASSWORD`
|
|
- `QOBUZ_APP_ID` (optional, defaults to reverse-engineered app id)
|
|
- `QOBUZ_APP_SECRET` (optional, defaults to reverse-engineered app secret)
|