SpotiSync - Smart syncing for Spotify playlists
Project description
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.
Project details
Release history Release notifications | RSS feed
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 spotisync-0.1.2.tar.gz.
File metadata
- Download URL: spotisync-0.1.2.tar.gz
- Upload date:
- Size: 30.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.4 CPython/3.12.12 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3e9f7dd08e3f42457ad6e5e2d06a2345d54529b9f7cae8ca23f2020f21dbd39
|
|
| MD5 |
9ceb8e5fafc708e380bd659bbd2af2c0
|
|
| BLAKE2b-256 |
1509fb38ab89b6e4e3c53487fa4b380d358f32d153ee10ae5e7d7d3a6fcb48d6
|
File details
Details for the file spotisync-0.1.2-py3-none-any.whl.
File metadata
- Download URL: spotisync-0.1.2-py3-none-any.whl
- Upload date:
- Size: 35.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.1.4 CPython/3.12.12 Linux/6.11.0-1018-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0869370cf348de8f7a5f5e7ffb994ccda461b5a57b521870d519e432473cccaf
|
|
| MD5 |
55c756fcc539a49514b2137a96ef1f3f
|
|
| BLAKE2b-256 |
4801ed80f6030b68faf1dcbb386e7d26596d36b7ced7224c07131f252b96b89b
|