sssync
A scriptable playlist sync tool for Qobuz, Spotify, and Jellyfin.
sssync sync spotify qobuz --all
sssync sync qobuz jellyfin <playlist-id> "My Playlist"
sssync sync qobuz jellyfin --dry-run
Features
- Sync playlists from Spotify or Qobuz into Qobuz or Jellyfin (Spotify is read-only, so it's a source only)
- ISRC-first matching — exact track identification via ISRC, with fuzzy title/artist/duration fallback
- Incremental, append-only syncs — existing playlist contents are never touched; re-running adds only what's missing
- Favorites sync between services that support them
- Dry-run mode — preview matches without writing anything
- Simple TOML config, streamrip-style CLI
Installation
Requires Python 3.11+.
Install as an isolated tool (recommended):
uv tool install sssync
# or
pipx install sssync
Try it without installing anything:
uvx sssync playlists qobuz
Or with pip in a virtualenv:
pip install sssync
Configuration
sssync config
Creates and opens ~/.config/sssync/config.toml:
[qobuz]
# Session token: log into https://play.qobuz.com → DevTools →
# Application → Cookies → qobuz.com → user_auth_token
token = ""
[spotify]
client_id = ""
client_secret = ""
redirect_uri = "http://127.0.0.1:8888/callback"
[jellyfin]
url = "http://your-server/jellyfin"
api_key = ""
# or: api_key_path = "~/jellyfin_api_key.txt"
Only the sections you use need to be filled in — sources are loaded lazily per command.
Usage
List playlists on a source:
sssync playlists qobuz
sssync playlists jellyfin
Sync a playlist (accepts a name, an id, or a URL):
sssync sync spotify qobuz "My Playlist"
sssync sync qobuz jellyfin <playlist-id>
sssync sync qobuz jellyfin <playlist-id> "My Playlist"
Sync everything:
sssync sync spotify qobuz --all
Preview without writing:
sssync sync qobuz jellyfin <playlist-id> --dry-run
Sync favorites:
sssync favorites spotify qobuz
How matching works
- ISRC — if both services expose the track's ISRC, it's an exact match.
- Fuzzy — normalized title/artist similarity (rapidfuzz) with a duration tolerance. Thresholds are tunable in the
[sync]section of the config:
[sync]
title_threshold = 80
artist_threshold = 75
duration_tolerance_ms = 5000
min_score = 75
Unmatched tracks are reported, never silently dropped. API errors during matching are captured per track and included in the sync report.
Safety
Writes are append-only by design. sssync never removes or reorders tracks in an existing destination playlist, and dry-run makes no changes at all. Destination playlists keep their cover art and metadata.
Development
git clone https://github.com/uprcse/sssync.git
cd sssync
pip install -e ".[dev]"
pytest tests/
ruff check sssync tests
CI runs pytest + ruff on Python 3.11–3.13. Releases are published to PyPI automatically on tag push via Trusted Publishing.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sssync-0.2.2.tar.gz.
File metadata
- Download URL: sssync-0.2.2.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be9c211e8a1a851331439e92aa0b4e5ec3a3331705db2f8a55bbed785cbe7393
|
|
| MD5 |
68beb1fdc35ced44c3355d1f653f5752
|
|
| BLAKE2b-256 |
5dbad6433fead3ec0945d2005535cc85d5e791ca0dfc9e1b1544c095d44ed395
|
Provenance
The following attestation bundles were made for sssync-0.2.2.tar.gz:
Publisher:
release.yml on uprcse/sssync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sssync-0.2.2.tar.gz -
Subject digest:
be9c211e8a1a851331439e92aa0b4e5ec3a3331705db2f8a55bbed785cbe7393 - Sigstore transparency entry: 2668785301
- Sigstore integration time:
-
Permalink:
uprcse/sssync@4fa1e2e6139eb43986edac2b28c94a3ec5123249 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/uprcse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4fa1e2e6139eb43986edac2b28c94a3ec5123249 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sssync-0.2.2-py3-none-any.whl.
File metadata
- Download URL: sssync-0.2.2-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
12ebfa2840c7a8b7f3dcda19d08400029f9bbcb0439677842cd76967e98de9f4
|
|
| MD5 |
1e9c0527dccb1fe68fb8e6d1bc2c980c
|
|
| BLAKE2b-256 |
20563724fcacf8e09d8aaa873a79df8a4eb628f3ec7742927c3479e96e9100f5
|
Provenance
The following attestation bundles were made for sssync-0.2.2-py3-none-any.whl:
Publisher:
release.yml on uprcse/sssync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sssync-0.2.2-py3-none-any.whl -
Subject digest:
12ebfa2840c7a8b7f3dcda19d08400029f9bbcb0439677842cd76967e98de9f4 - Sigstore transparency entry: 2668785385
- Sigstore integration time:
-
Permalink:
uprcse/sssync@4fa1e2e6139eb43986edac2b28c94a3ec5123249 -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/uprcse
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4fa1e2e6139eb43986edac2b28c94a3ec5123249 -
Trigger Event:
push
-
Statement type: