SpotiSync
Automatically sync tracks between playlists - from your Liked Songs, your own playlists, or any public playlist on Spotify.
Want to share your liked tracks as a public playlist? Create a backup that never loses tracks? Mirror someone else's playlist to your own? SpotiSync handles it all.
What It Does
- Sync your Liked Songs to any playlist you own
- Use any public Spotify playlist as a source (even ones you don't own!)
- Sync between your own playlists
- Archive mode: keep tracks even after their removal from the source playlist (
skip_removals) - Filter out local files, podcasts, and specific tracks
- Create artist-specific playlists with include filters
- Preview changes before applying (
dry-runmode) - Run on a schedule with Docker
Quick Start
Create a Spotify Developer App
- Go to the Spotify Developer Dashboard
- Click Create app
- Set Redirect URI to
https://example.com/callbackThis is the default. If you change it, update
spotify.redirect_uriin your config to match exactly. - Note down the App's Client ID and Client Secret
Required scopes (configured automatically):
user-library-read- Read your Liked Songsplaylist-read-private- Read private playlistsplaylist-modify-public/playlist-modify-private- Modify playlists
Install SpotiSync
Install SpotiSync with pipx to run it as a standalone tool without affecting your system Python:
pipx install spotisync
Or with pip in a virtual environment:
pip install spotisync
Note: I recommend
pipxfor global CLI installation!
Or with Poetry (for development):
git clone https://github.com/bulletinmybeard/spotisync.git
cd spotisync
poetry install
Run the Setup Wizard
spotisync init
This guides you through:
- Entering your Spotify credentials
- Choosing a source (Liked Songs or a playlist)
- Selecting a target playlist
- Configuring filters (optional)
Authenticate
spotisync auth
A browser window opens to authorize SpotiSync with your Spotify account.
Sync
# Preview what will change
spotisync sync --dry-run
# Run the sync
spotisync sync
CLI Commands
| Command | Description |
|---|---|
spotisync init |
Interactive setup wizard |
spotisync init -o PATH |
Setup with custom config output path |
spotisync auth |
Authenticate with Spotify |
spotisync auth --clear |
Clear stored authentication token |
spotisync sync |
Run all enabled sync groups |
spotisync sync --dry-run |
Preview changes without applying |
spotisync sync -n NAME |
Sync specific group(s) by name |
spotisync sync --json |
JSON output for automation |
spotisync status |
Show auth and config status |
spotisync status --json |
JSON output |
spotisync config |
Display current configuration (YAML) |
spotisync config --json |
Display as JSON |
spotisync add |
Add a new sync group (wizard) |
Global option: --config/-c PATH to override config file location.
Automation
SpotiSync supports JSON output for scripting and CI/CD:
# Check sync results programmatically
spotisync sync --json | jq '.summary'
# Sync specific groups with JSON output
spotisync sync -n "daily-backup" -n "discover-weekly" --json
# Get authentication status
spotisync status --json | jq '.authentication.authenticated'
Exit codes: 0 on success, 1 on error.
Configuration
SpotiSync uses a config.yaml file. The setup wizard creates this for you, or see config.example.yaml for all options.
Multiple sync groups - Sync different sources to different targets:
sync_groups:
- name: liked-to-public
source: liked_tracks
target: 3cEYpjA9oz9GiPac4AsH4n
- name: discover-backup
source: 37i9dQZEVXcQ9COmYvdajy # Any public playlist (e.g., Spotify's Discover Weekly)
target: 5Rrf7mqN8uus2AaQQQNdc1
Config file locations (checked in order):
- Docker:
/app/config.yaml - Development (git repo):
./config.yaml - User home:
~/.spotisync/config.yaml
Override with --config/-c PATH on any command.
How to get a playlist ID: Open the playlist in Spotify, click Share → Copy link. The ID is the string after
/playlist/:https://open.spotify.com/playlist/37i9dQZEVXcQ9COmYvdajy→37i9dQZEVXcQ9COmYvdajy
Filtering
SpotiSync supports multiple different filter options to dictate which tracks should be included or excluded from the sync.
Examples
Include filters - Create artist-specific playlists from Liked Songs:
sync_groups:
- name: eddie-unchained
source: liked_tracks
target: 3cEYpjA9oz9GiPac4AsH4n # Your target playlist ID
filters:
include:
artists:
- 6mdiAmATAx73kdxrNrnlao # Iron Maiden
Include filters - Filter a public playlist to specific artists:
sync_groups:
- name: doom-eternal-uncluttered
source: 6s4aGjq9b42OP4nMGNCLUu # DOOM Eternal soundtrack (public playlist)
target: 9pXbKfV5dQ3sLz2nTj1uRw # Your custom playlist
filters:
include:
artists:
- 13ab1LgQZ3tQOhkDRRYB8Y # Mick Gordon
Exclude filters - Skip unwanted tracks:
sync_groups:
- name: no-remasters
source: 6vr1Nnese49l0hxQEljOQn # Your source playlist ID
target: 3cEYojA9oz9hiPac4AsH4n # Your target playlist ID
filters:
exclude:
tracks:
- '(?i)\bremaster(?:ed)?(?:\s+\d{4})?\b' # e.g., Metallica / Enter Sandman - Remastered 2021
Combined filters - Use both include and exclude:
filters:
exclude:
artists:
- '(?i)christmas' # Regex patterns
albums:
- 0sNOF9WDwhWunNAHPD3Baj # Spotify IDs
tracks:
- '(?i)remix'
- '(?i)live'
Other filters:
skip_podcasts: true- Skip podcast episodes (default)
Include filters are applied before exclude filters, so you can create an artist playlist and still exclude particular tracks.
Filter inheritance: Per-group filters merge with root filters:
skip_podcasts: Per-group value overrides rootinclude/excludelists: Combined (both root and group patterns apply)
Archive Mode
Want a backup playlist that never loses tracks? Use skip_removals to keep tracks in the target even after removing them from the source:
sync_groups:
- name: liked-tracks-archive
source: liked_tracks
target: 3cEYpjA9oz9GiPac4AsH4n # Your target playlist ID
skip_removals: true # Tracks removed from the source stay in the target playlist
This is useful for:
- Backup playlists that hold all tracks you've ever liked
- Preserving a history of tracks from a dynamic source playlist
Using Regex for Track Filtering
Spotify tracks often include version markers in their titles like "(Live)", "- Remastered 2021", or "(Acoustic)". Regular expressions (regex) let you match these patterns flexibly to include or exclude specific track versions.
Regex Basics & Common Patterns
Regex Basics
The patterns below use these common regex features:
| Syntax | Meaning | Example |
|---|---|---|
(?i) |
Case-insensitive | (?i)live matches "Live", "LIVE", "live" |
\b |
Word boundary | \blive\b matches "Live" but not "Oliver" |
(a|b) |
Either a or b | (remix|live) matches either |
? |
Optional | remaster(ed)? matches "remaster" or "remastered" |
\d{4} |
Four digits | Matches years like "2021" |
YAML tip: Use single quotes around patterns to avoid escaping backslashes.
Common Patterns
| Pattern | Matches | Example Track Title |
|---|---|---|
'(?i)\bremaster(ed)?(\s+\d{4})?\b' |
Remastered versions | "Enter Sandman - Remastered 2021" |
'(?i)\blive\b' |
Live recordings | "Nothing Else Matters (Live)" |
'(?i)acoustic' |
Acoustic versions | "Layla (Acoustic)" |
'(?i)remix' |
Remixes | "Blinding Lights (Remix)" |
'(?i)\bdemo\b' |
Demo recordings | "Bohemian Rhapsody (Demo)" |
'(?i)instrumental' |
Instrumentals | "Stairway to Heaven (Instrumental)" |
'(?i)radio edit' |
Radio edits | "Purple Haze - Radio Edit" |
'(?i)(deluxe|bonus)' |
Deluxe/bonus tracks | "Album Name (Deluxe Edition)" |
'(?i)(feat\.|ft\.|featuring)' |
Featuring artists | "Song (feat. Artist)" |
Docker
This repo includes docker-compose.yml for scheduled syncing.
# Start the container (runs cron daemon)
docker compose up -d
# Run a manual sync
docker exec spotisync spotisync sync
# Check status
docker exec spotisync spotisync status
Volume mounts required:
./config.yaml:/app/config.yaml- Your configuration./tokens.json:/app/tokens.json- Auth tokens (created afterspotisync auth)
Configure the schedule in config.yaml:
cron:
enabled: true
schedule: "0 * * * *" # Every hour
Note: Container name is
spotisync- matches the shell wrapper functions below.
Shell Integration (Development Only)
Note: This section is only relevant if you run SpotiSync from the cloned repo via
poetry run. If you installed it viapiporpipx, just usespotisyncdirectly.
These optional wrapper functions provide a convenient spotisync shortcut when running from a cloned repo.
Linux/macOS (ZSH/Bash)
Create ~/spotisync_shell.sh:
For Poetry:
spotisync() {
local project_dir="$HOME/path/to/spotisync"
if [[ ! -d "$project_dir" ]]; then
echo "Error: spotisync project not found at $project_dir"
return 1
fi
(cd "$project_dir" && poetry run spotisync "$@")
}
For Docker:
spotisync() {
if ! docker ps --format '{{.Names}}' | grep -q 'spotisync'; then
echo "Error: spotisync container is not running"
return 1
fi
docker exec -it spotisync spotisync "$@"
}
Then add to ~/.zshrc or ~/.bashrc:
[ -f "$HOME/spotisync_shell.sh" ] && source "$HOME/spotisync_shell.sh"
Reload: source ~/.zshrc
Windows (PowerShell)
Create ~\spotisync_shell.ps1:
For Poetry:
function spotisync {
$projectDir = "$env:USERPROFILE\path\to\spotisync"
if (-not (Test-Path $projectDir)) {
Write-Error "spotisync project not found at $projectDir"
return
}
Push-Location $projectDir
try { poetry run spotisync @args }
finally { Pop-Location }
}
For Docker:
function spotisync {
$running = docker ps --format '{{.Names}}' | Select-String -Quiet 'spotisync'
if (-not $running) {
Write-Error "spotisync container is not running"
return
}
docker exec -it spotisync spotisync @args
}
Then add to your PowerShell profile ($PROFILE):
if (Test-Path "$env:USERPROFILE\spotisync_shell.ps1") { . "$env:USERPROFILE\spotisync_shell.ps1" }
Reload: . $PROFILE
Requirements
- Spotify account
- Spotify Developer App credentials
- Python 3.12+
Links
License
MIT License - see the LICENSE file for details.
Metadata
Release files for spotisync 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| spotisync-0.1.2.tar.gz | 30.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| spotisync-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.4 kB
Release files / spotisync-0.1.2.tar.gz
| Download URL | spotisync-0.1.2.tar.gz |
|---|---|
| Size | 30.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a3e9f7dd08e3f42457ad6e5e2d06a2345d54529b9f7cae8ca23f2020f21dbd39
|
|
BLAKE2b-256 checksum How to use checksums |
1509fb38ab89b6e4e3c53487fa4b380d358f32d153ee10ae5e7d7d3a6fcb48d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.4 CPython/3.12.12 Linux/6.11.0-1018-azure
|
Release files / spotisync-0.1.2-py3-none-any.whl
| Download URL | spotisync-0.1.2-py3-none-any.whl |
|---|---|
| Size | 35.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0869370cf348de8f7a5f5e7ffb994ccda461b5a57b521870d519e432473cccaf
|
|
BLAKE2b-256 checksum How to use checksums |
4801ed80f6030b68faf1dcbb386e7d26596d36b7ced7224c07131f252b96b89b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.4 CPython/3.12.12 Linux/6.11.0-1018-azure
|