Skip to content

Playlists

Overview

Pushtunes can push playlists between any supported services — streaming services (Spotify, YouTube Music, Tidal) and local servers (Subsonic, Jellyfin) — without going through a separate CSV export step. Track-by-track matching uses similarity algorithms.

Basic Usage

# Between streaming services
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to tidal
pushtunes push playlist --from ytm --playlist-name "Synthwave Mix" --to spotify

# From streaming services to local servers
# Note: uses in-memory fuzzy matching against your entire music library
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to subsonic
pushtunes push playlist --from ytm --playlist-name "Chill Vibes" --to jellyfin

# From local servers to streaming services
pushtunes push playlist --from subsonic --playlist-name "My Mix" --to spotify
# Jellyfin requires --source-playlist-id (multiple playlists can have same name)
pushtunes push playlist --from jellyfin --source-playlist-id "8e5d6f1d17423354e83a3c17cae758dc" --to ytm

Finding Playlist IDs

Spotify:

When using Spotify as a source, you need the playlist ID: 1. Open the playlist in Spotify (desktop or web) 2. Click Share → Copy link to playlist 3. URL: https://open.spotify.com/playlist/37i9dQZF1DXaXB8fQg7xif 4. ID is the last part: 37i9dQZF1DXaXB8fQg7xif

Or use the Spotify URI: right-click playlist → Share → Copy Spotify URI → extract the part after playlist:.

Jellyfin:

  1. Open the playlist in Jellyfin web UI
  2. URL: https://jellyfin.example.com/web/index.html#!/details?id=8e5d6f1d17423354e83a3c17cae758dc
  3. ID is the value after id=

Or via API: navigate to https://jellyfin.example.com/Users/{userId}/Items?IncludeItemTypes=Playlist and find the Id field.

TL;DR: Spotify and Jellyfin require --source-playlist-id (they allow duplicate names). YouTube Music works with either --playlist-name or --source-playlist-id.

Strict Matching with --require-all-tracks

By default, pushtunes creates a playlist even if some tracks can't be matched (partial success). For cross-service pushing, use strict mode:

pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks

In strict mode: - All tracks must be matched (automatically or via mappings) - If even one track can't be matched, the operation fails - No partial playlist is created - Fix failures with mappings and retry

Without strict mode (default): playlist is created with whatever tracks could be matched; missing tracks are skipped.

Handling Conflicts

When a playlist with the same name already exists on the target, use --on-conflict:

  • abort (default): Show differences and exit without making changes

    pushtunes push playlist --from subsonic --playlist-name=chill --to ytm --on-conflict=abort
    
    Displays: tracks in common, tracks to add, tracks to remove.

  • replace: Replace the entire playlist with tracks from source

    pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
      --on-conflict=replace
    

  • append: Add only missing tracks to the existing playlist

    pushtunes push playlist --from subsonic --playlist-name=favorites --to spotify --on-conflict=append
    

  • sync: Fully synchronize (add missing, remove extras, maintain order)

    pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
      --on-conflict=sync
    

Using Target Playlist IDs

Spotify and Jellyfin allow duplicate playlist names. You can target a specific existing playlist using --playlist-id:

# First push creates the playlist — note the Playlist ID in output
pushtunes push playlist --from subsonic --playlist-name=argon --to spotify
# Output: Playlist ID: 59EkFxUZG0CnApHQIgYVUK

# Subsequent pushes using the ID
pushtunes push playlist --from subsonic --playlist-name=argon --to spotify \
  --playlist-id=59EkFxUZG0CnApHQIgYVUK --on-conflict=sync

Same for Jellyfin:

pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to jellyfin \
  --playlist-id=8e5d6f1d17423354e83a3c17cae758dc --on-conflict=sync

You can find playlist IDs in the web player URL or from the previous push output.

Working with Mappings

When tracks can't be auto-matched (different names, remasters, etc.), use mappings to provide manual matches. See Mappings for full details and mappings_complete_example.csv for a template.

Step 1: Identify Failed Matches

Run with --report to see which tracks failed:

pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks \
  --report=not_found,similarity_too_low

Note: The --export-csv option is only available for push albums and push tracks, not for playlists. Create your mappings file manually based on the report output.

Step 2: Fill in Mappings

For cross-service pushes, use the track-level columns:

type,artist,title,spotify_id,ytm_id,spotify_artist,spotify_title,ytm_artist,ytm_title,subsonic_artist,subsonic_title,subsonic_album,jellyfin_artist,jellyfin_title,jellyfin_album
track,"Volkor X","This Means War","","","","","Volkor X","This Really Means War",,,,,
track,"Mitch Murder","Current Events","","","","","Mitch Murder","Current Events (Remastered)",,,,,

For Subsonic/Jellyfin targets, use the subsonic_* and jellyfin_* columns:

type,artist,title,spotify_id,ytm_id,spotify_artist,spotify_title,ytm_artist,ytm_title,subsonic_artist,subsonic_title,subsonic_album,jellyfin_artist,jellyfin_title,jellyfin_album
track,"The Beatles","Let It Be",,,,,,,Beatles,Let It Be,Let It Be (Remastered),,,
track,"Foo Fighters","Best Of You",,,,,,,Foo Fighters,Best of You,Greatest Hits,,,

Step 3: Retry with Mappings

pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks \
  --mappings-file=playlist_mappings.csv

Adjusting Similarity Threshold

If too many tracks aren't matching, try lowering the threshold:

pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --similarity=0.7 \
  --require-all-tracks

Default: 0.8 (80% similarity required). Range: 0.0 to 1.0. Lower thresholds may cause incorrect matches.

Verbose Output

Use -v or --verbose to see detailed track matching results:

pushtunes push playlist --from subsonic --playlist-name=jazz --to spotify -v
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks \
  --verbose \
  --report=not_found,similarity_too_low

Example Workflows

Safe Cross-Service Migration

# Step 1: Try with strict mode, capture failures
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks \
  --report=not_found,similarity_too_low

# Step 2: Create a mappings file manually for failed tracks
# Step 3: Retry with mappings
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --require-all-tracks \
  --mappings-file=playlist_mappings.csv

Bidirectional Sync

# Mirror Spotify playlist to YTM
pushtunes push playlist --from spotify --source-playlist-id "37i9dQZF1DXaXB8fQg7xif" --to ytm \
  --on-conflict=sync

# Mirror YTM playlist to Spotify
pushtunes push playlist --from ytm --playlist-name "Favorites" --to spotify \
  --on-conflict=sync

Limitations

  1. Track availability: Not all tracks available on one service exist on another
  2. Different versions: Services may have different versions (remaster, live, etc.)
  3. Metadata differences: Artist names and track titles may differ between services
  4. Rate limiting: Large playlists may take time due to API rate limits

Tips for Best Results

  1. Always use --require-all-tracks for cross-service pushing to ensure completeness
  2. Use playlist IDs when possible for more reliable lookups
  3. Create mappings for problem tracks instead of lowering similarity threshold
  4. Test with small playlists first before migrating large collections
  5. Use --verbose and --report to understand matching behavior

Troubleshooting

"Playlist not found"

  • Check playlist name spelling (case-insensitive but must match)
  • Try using --source-playlist-id instead
  • Verify you're authenticated (check env vars for Spotify, browser.json for YTM)

"Only X/Y tracks matched"

  • Use --report=not_found,similarity_too_low to see which tracks failed
  • Review the failures and create manual mappings
  • Consider lowering --similarity threshold (but be careful)

"No tracks could be matched"

  • Verify the target service has the music available
  • Check if track/artist names are significantly different
  • Use mappings to provide manual matches

See Also