Skip to main content

SpotiSync

CI PyPI version Python Versions Poetry Ruff Type checking: mypy License: MIT

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-run mode)
  • Run on a schedule with Docker

Quick Start

Create a Spotify Developer App

  1. Go to the Spotify Developer Dashboard
  2. Click Create app
  3. Set Redirect URI to https://example.com/callback

    This is the default. If you change it, update spotify.redirect_uri in your config to match exactly.

  4. Note down the App's Client ID and Client Secret

Required scopes (configured automatically):

  • user-library-read - Read your Liked Songs
  • playlist-read-private - Read private playlists
  • playlist-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 pipx for 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):

  1. Docker: /app/config.yaml
  2. Development (git repo): ./config.yaml
  3. 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 root
  • include/exclude lists: 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 after spotisync 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 via pip or pipx, just use spotisync directly.

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)

Source distribution for spotisync 0.1.2
File Size Uploaded
spotisync-0.1.2.tar.gz 30.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spotisync 0.1.2
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page