🎡 Plexist

January 26, 2026 Β· View on GitHub

CodeQL DockerHub Docker Dev Image CI

🎡 Plexist

Plex + Playlist = Plexist β€” An application for recreating and syncing Deezer, Apple Music, Spotify, Qobuz, and Tidal playlists in Plex. (because Plex music playlist are a croc of tihs)

Plexist Logo

Features

FeatureDescription
Playlist SyncRecreates your streaming playlists in Plex using files from your library
Multi-Service SyncSync playlists between any services (e.g., Spotify β†’ Qobuz, Tidal β†’ Plex)
Auto UpdatesKeeps playlists in sync with your streaming services
New PlaylistsAutomatically creates Plex playlists when added to your streaming service
Liked TracksSyncs favorited tracks to Plex as 5-star ratings (appears in "Liked Tracks" smart playlist)
ISRC + MBID MatchingUses ISRC codes and MusicBrainz MBID proxy matching, then falls back to fuzzy matching

Supported Services

  • Spotify
  • Deezer
  • Apple Music
  • Tidal
  • Qobuz

Multi-Service Sync

Sync playlists between any two services β€” not just to Plex! Configure source β†’ destination pairs to sync playlists directly between streaming services.

Supported Sync Directions

ServiceRead (Source)Write (Destination)
Spotifyβœ…βŒ
Deezerβœ…βœ…
Apple Musicβœ…βœ…*
Tidalβœ…βœ…
Qobuzβœ…βœ…
Plexβœ…βœ…

*Apple Music write has limitations: the API doesn't support clearing/deleting playlists, so tracks are appended to existing playlists rather than replaced.

Configuration

Set the SYNC_PAIRS environment variable with comma-separated source:destination pairs:

# Sync Spotify playlists to Qobuz
SYNC_PAIRS=spotify:qobuz

# Sync Tidal playlists to Plex
SYNC_PAIRS=tidal:plex

# Multiple sync pairs
SYNC_PAIRS=spotify:qobuz,tidal:plex,deezer:tidal

How It Works

  1. Fetches playlists from the source service
  2. Matches tracks in the destination using:
  • ISRC codes (International Standard Recording Code) for exact matching
  • MusicBrainz MBID proxy (ISRC β†’ MusicBrainz β†’ Plex MBID index)
  • Metadata fallback (title/artist/album) when ISRC/MBID unavailable
  1. Creates or updates playlists in the destination service
  2. Reports results including matched, missing, and failed tracks

πŸ’‘ Note: When SYNC_PAIRS is configured, it replaces the default Plex-centric sync behavior. To sync to Plex, include it as a destination (e.g., spotify:plex).

What it will NOT do:

  • Steal Shit!

Prerequisites

Plex (Required)

VariableDescription
PLEX_URLYour Plex server URL (e.g., http://192.168.0.69:32400)
PLEX_TOKENYour Plex authentication token β€” How to find it

Matching & Cache Options

VariableDefaultDescription
MUSICBRAINZ_ENABLED1Enable ISRC β†’ MusicBrainz β†’ MBID proxy matching
MUSICBRAINZ_CACHE_TTL_DAYS90Cache duration for successful ISRC lookups
MUSICBRAINZ_NEGATIVE_CACHE_TTL_DAYS7Cache duration for ISRCs not found in MusicBrainz
MUSICBRAINZ_API_KEY(optional)Optional MusicBrainz API key (sent as a Bearer token)
PLEX_EXTENDED_CACHE_ENABLED1Enable extended cache indexes for faster matching
PLEX_DURATION_BUCKET_SECONDS5Duration bucket size used for matching heuristics

Performance Tuning Recommendations

These settings control Plex API throughput and local CPU usage. Start with the tier that best matches your hardware and adjust if you see timeouts or rate-limit warnings.

Hardware tierExample devicesMAX_REQUESTS_PER_SECONDMAX_CONCURRENT_REQUESTSNotes
Low-powerRaspberry Pi 3/4, older mini PCs4–62–3Favor stability over speed; keep concurrency low.
Entry NASSynology/QNAP (Celeron/Atom)6–83–4Increase only if CPU stays <70% and Plex remains responsive.
Mid-rangeModern desktop CPU (4–8 cores)10–156–8Good default for most home servers.
High-end12–24 core workstation/server15–258–12Watch Plex responsiveness during large syncs.
Cloud VMAzure T4 or similar12–186–10GPU doesn’t help this workload; tune based on CPU/RAM.
Large server32+ cores, ample RAM20–3012–16Use higher values only if Plex stays snappy.

Tip: If you see Plex timeouts or slow UI response, reduce MAX_CONCURRENT_REQUESTS first, then lower MAX_REQUESTS_PER_SECOND.

Service Configuration

🟒 Spotify

Requirements

Liked Tracks Sync (Optional)

To sync your Spotify liked/saved tracks to Plex ratings, set up OAuth authentication:

  1. Go to your Spotify Developer Dashboard
  2. Select your app β†’ Edit Settings
  3. Add a Redirect URI (e.g., http://localhost:8888/callback)
  4. Set the SPOTIFY_REDIRECT_URI environment variable to match
  5. On first run, authorize the app (check container logs for the URL)
  6. Mount .spotify_cache as a volume to persist OAuth tokens
VariableRequiredDescription
SPOTIFY_CLIENT_IDβœ…Your Spotify app Client ID
SPOTIFY_CLIENT_SECRETβœ…Your Spotify app Client Secret
SPOTIFY_USER_IDβœ…Your Spotify user ID
SPOTIFY_REDIRECT_URIFor liked tracksOAuth redirect URI (e.g., http://localhost:8888/callback)
SPOTIFY_CACHE_PATHOptionalPath to cache OAuth tokens (e.g., /app/data/.spotify_cache)
🟣 Deezer

Requirements

Get your Profile ID or Playlist IDs:

Profile ID:

  1. Login to deezer.com
  2. Click on your profile
  3. Grab the ID from the URL: https://www.deezer.com/profile/########

Playlist ID:

  • From URL: https://www.deezer.com/playlist/10484834882 β†’ ID is 10484834882

Write Support (Sync TO Deezer)

To use Deezer as a sync destination (e.g., SYNC_PAIRS=spotify:deezer), you need an OAuth access token:

  1. Create an app at Deezer Developers
  2. Note your Application ID and Secret Key
  3. Install the deezer-python package: pip install deezer-python
  4. Run the OAuth helper:
    deezer-oauth YOUR_APP_ID YOUR_SECRET_KEY
    
  5. Open the URL in your browser and authorize the app
  6. Copy the access token from the callback URL
VariableRequiredDescription
DEEZER_USER_IDOne of theseSyncs all playlists for user
DEEZER_PLAYLIST_IDOne of theseSpace-separated playlist IDs
DEEZER_ACCESS_TOKENFor write operationsOAuth access token (see above)
🍎 Apple Music

Requirements

Getting MusicKit Credentials

  1. Go to Certificates, Identifiers & Profiles
  2. Click + to create a new key
  3. Name it (e.g., "Plexist MusicKit") and enable MusicKit
  4. Download the .p8 private key file (one-time download only!)
  5. Note your Key ID and Team ID

Getting Your Music User Token

Option 1: Use Apple Music Token Generator
Option 2: Use MusicKit in a native iOS/macOS app

VariableRequiredDescription
APPLE_MUSIC_TEAM_IDβœ…Apple Developer Team ID
APPLE_MUSIC_KEY_IDβœ…MusicKit Key ID
APPLE_MUSIC_PRIVATE_KEYβœ…Key content or file path (e.g., /app/data/AuthKey.p8)
APPLE_MUSIC_USER_TOKENFor library accessMusic User Token
APPLE_MUSIC_PUBLIC_PLAYLIST_IDSFor public playlistsSpace-separated playlist IDs
APPLE_MUSIC_STOREFRONTFor public playlistsStorefront code (e.g., us, gb)
APPLE_MUSIC_DEVELOPER_TOKEN_TTL_SECONDSOptionalToken TTL (default: 43200)
APPLE_MUSIC_REQUEST_TIMEOUT_SECONDSOptionalRequest timeout (default: 10)
APPLE_MUSIC_MAX_RETRIESOptionalMax retries (default: 3)
APPLE_MUSIC_RETRY_BACKOFF_SECONDSOptionalRetry backoff (default: 1.0)

πŸ’‘ Public Playlist Mode: Omit APPLE_MUSIC_USER_TOKEN and set APPLE_MUSIC_PUBLIC_PLAYLIST_IDS + APPLE_MUSIC_STOREFRONT to sync only public playlists.

πŸ”΅ Tidal

Requirements

Tidal uses OAuth device flow for authentication.

Getting OAuth Tokens

import tidalapi

session = tidalapi.Session()
session.login_oauth_simple()  # Follow the printed URL to authorize

# Save these values:
print(f"Access Token: {session.access_token}")
print(f"Refresh Token: {session.refresh_token}")
print(f"Token Expiry: {session.expiry_time.isoformat()}")
VariableRequiredDescription
TIDAL_ACCESS_TOKENFor user playlistsOAuth access token
TIDAL_REFRESH_TOKENFor user playlistsOAuth refresh token
TIDAL_TOKEN_EXPIRYFor user playlistsExpiry datetime (ISO format)
TIDAL_PUBLIC_PLAYLIST_IDSFor public playlistsSpace-separated playlist UUIDs
TIDAL_REQUEST_TIMEOUT_SECONDSOptionalRequest timeout (default: 10)
TIDAL_MAX_RETRIESOptionalMax retries (default: 3)
TIDAL_RETRY_BACKOFF_SECONDSOptionalRetry backoff (default: 1.0)

πŸ’‘ Public Playlist Mode: Find playlist UUIDs from: https://tidal.com/browse/playlist/{uuid}

🟠 Qobuz

Requirements

Qobuz doesn't have a public API. Use tools like qobuz-dl to extract app credentials.

VariableRequiredDescription
QOBUZ_APP_IDβœ…Qobuz app ID
QOBUZ_APP_SECRETβœ…Qobuz app secret
QOBUZ_USERNAMEFor user authEmail address
QOBUZ_PASSWORDFor user authPassword
QOBUZ_USER_AUTH_TOKENAlternativeExisting auth token (skips username/password)
QOBUZ_PUBLIC_PLAYLIST_IDSFor public playlistsSpace-separated playlist IDs
QOBUZ_REQUEST_TIMEOUT_SECONDSOptionalRequest timeout (default: 10)
QOBUZ_MAX_RETRIESOptionalMax retries (default: 3)
QOBUZ_RETRY_BACKOFF_SECONDSOptionalRetry backoff (default: 1.0)

πŸ’‘ Public Playlist Mode: Find playlist IDs from: https://www.qobuz.com/playlist/{id}

Installation

Quick Start (One-time Run)

git clone https://github.com/Gyarbij/Plexist.git
cd Plexist
pip3 install -r requirements.txt
python3 plexist/plexist.py

Note: This runs once. Use Docker for continuous syncing.

Environment File (.env)

Create a .env file in the project root:

PLEX_URL=http://192.168.0.2:32400
PLEX_TOKEN=your-plex-token
LOG_LEVEL=INFO
LOG_FORMAT=plain

🐳 Docker Deployment

Multi-platform images available on:

Boolean Values

All boolean options accept flexible values (case-insensitive):

EnableDisable
1, y, yes, true, on0, n, no, false, off

Environment Variables Reference

Core Settings

VariableDefaultDescription
PLEX_URLβ€”Required. Your Plex server URL (include http:// or https://)
PLEX_TOKENβ€”Required. Your Plex authentication token
DB_PATH/app/data/plexist.dbSQLite database path (mount /app/data)
SECONDS_TO_WAIT84000Seconds between sync cycles
LOG_LEVELINFOLogging level (DEBUG, INFO, WARNING, ERROR)
LOG_FORMATplainLog format (plain or json)

Playlist Options

VariableDefaultDescription
ADD_PLAYLIST_POSTERyesAdd poster artwork to playlists
ADD_PLAYLIST_DESCRIPTIONyesAdd description to playlists
APPEND_INSTEAD_OF_SYNCnono = Full sync, yes = Append only (no removals)
SYNC_LIKED_TRACKSnoSync liked tracks to Plex 5-star ratings
SYNC_PAIRSβ€”Multi-service sync pairs (e.g., spotify:qobuz,tidal:plex)

Output Options

VariableDefaultDescription
WRITE_MISSING_AS_CSVnoWrite missing tracks to CSV file
WRITE_MISSING_AS_JSONnoWrite missing tracks to JSON file

Performance Tuning

VariableDefaultDescription
MAX_REQUESTS_PER_SECOND5Rate limit for Plex API requests
MAX_CONCURRENT_REQUESTS4Maximum concurrent Plex connections

πŸ’‘ For slower servers (Synology NAS, Raspberry Pi, older hardware):
Lower these values to 2 each to reduce CPU load and avoid connection pool warnings.

Docker Run

docker run -d \
  --name plexist \
  --restart unless-stopped \
  -e PLEX_URL=http://192.168.0.2:32400 \
  -e PLEX_TOKEN=your-plex-token \
  -e SECONDS_TO_WAIT=84000 \
  -e LOG_LEVEL=INFO \
  -e LOG_FORMAT=plain \
  -e WRITE_MISSING_AS_CSV=no \
  -e WRITE_MISSING_AS_JSON=no \
  -e ADD_PLAYLIST_POSTER=yes \
  -e ADD_PLAYLIST_DESCRIPTION=yes \
  -e APPEND_INSTEAD_OF_SYNC=no \
  -e SYNC_LIKED_TRACKS=no \
  -e MAX_REQUESTS_PER_SECOND=5 \
  -e MAX_CONCURRENT_REQUESTS=4 \
  -e SPOTIFY_CLIENT_ID=your-client-id \
  -e SPOTIFY_CLIENT_SECRET=your-client-secret \
  -e SPOTIFY_USER_ID=your-user-id \
  -v plexist-data:/app/data \
  gyarbij/plexist:latest
  # Or use: ghcr.io/gyarbij/plexist:latest
Full Docker Run with All Services
docker run -d \
  --name plexist \
  --restart unless-stopped \
  # === Core Settings ===
  -e PLEX_URL=http://192.168.0.2:32400 \
  -e PLEX_TOKEN=your-plex-token \
  -e DB_PATH=/app/data/plexist.db \
  -e SECONDS_TO_WAIT=84000 \
  -e LOG_LEVEL=INFO \
  -e LOG_FORMAT=plain \
  # === Playlist Options ===
  -e WRITE_MISSING_AS_CSV=no \
  -e WRITE_MISSING_AS_JSON=no \
  -e ADD_PLAYLIST_POSTER=yes \
  -e ADD_PLAYLIST_DESCRIPTION=yes \
  -e APPEND_INSTEAD_OF_SYNC=no \
  -e SYNC_LIKED_TRACKS=no \
  # === Performance ===
  -e MAX_REQUESTS_PER_SECOND=5 \
  -e MAX_CONCURRENT_REQUESTS=4 \
  # === Spotify ===
  -e SPOTIFY_CLIENT_ID=your-client-id \
  -e SPOTIFY_CLIENT_SECRET=your-client-secret \
  -e SPOTIFY_USER_ID=your-user-id \
  -e SPOTIFY_REDIRECT_URI=http://localhost:8888/callback \
  -e SPOTIFY_CACHE_PATH=/app/data/.spotify_cache \
  # === Deezer ===
  -e DEEZER_USER_ID=your-user-id \
  -e DEEZER_PLAYLIST_ID=playlist-id-1 playlist-id-2 \
  # === Apple Music ===
  -e APPLE_MUSIC_TEAM_ID=your-team-id \
  -e APPLE_MUSIC_KEY_ID=your-key-id \
  -e APPLE_MUSIC_PRIVATE_KEY=/app/data/AuthKey.p8 \
  -e APPLE_MUSIC_USER_TOKEN=your-user-token \
  -e APPLE_MUSIC_STOREFRONT=us \
  # === Tidal ===
  -e TIDAL_ACCESS_TOKEN=your-access-token \
  -e TIDAL_REFRESH_TOKEN=your-refresh-token \
  -e TIDAL_TOKEN_EXPIRY=2026-12-31T23:59:59 \
  # === Qobuz ===
  -e QOBUZ_APP_ID=your-app-id \
  -e QOBUZ_APP_SECRET=your-app-secret \
  -e QOBUZ_USERNAME=your-email \
  -e QOBUZ_PASSWORD=your-password \
  # === Volume ===
  -v plexist-data:/app/data \
  gyarbij/plexist:latest

⚠️ Note: Remove the comments (# ...) before running the command.

Docker Compose

Copy assets/example.compose.yaml to compose.yaml, then customize it:

services:
  plexist:
    image: gyarbij/plexist:latest  # Or: ghcr.io/gyarbij/plexist:latest
    container_name: plexist
    restart: unless-stopped
    environment:
      # === Core Settings ===
      PLEX_URL: http://192.168.0.2:32400
      PLEX_TOKEN: your-plex-token
      DB_PATH: /app/data/plexist.db
      SECONDS_TO_WAIT: 84000
      LOG_LEVEL: INFO
      LOG_FORMAT: plain

      # === Playlist Options ===
      WRITE_MISSING_AS_CSV: no
      WRITE_MISSING_AS_JSON: no
      ADD_PLAYLIST_POSTER: yes
      ADD_PLAYLIST_DESCRIPTION: yes
      APPEND_INSTEAD_OF_SYNC: no
      SYNC_LIKED_TRACKS: no
      # SYNC_PAIRS: spotify:qobuz,tidal:plex  # Multi-service sync (optional)

      # === Performance ===
      MAX_REQUESTS_PER_SECOND: 5
      MAX_CONCURRENT_REQUESTS: 4

      # === MusicBrainz (optional) ===
      # MUSICBRAINZ_API_KEY: your-musicbrainz-api-key

      # === Spotify (remove if not used) ===
      SPOTIFY_CLIENT_ID: your-client-id
      SPOTIFY_CLIENT_SECRET: your-client-secret
      SPOTIFY_USER_ID: your-user-id
      # SPOTIFY_REDIRECT_URI: http://localhost:8888/callback
      # SPOTIFY_CACHE_PATH: /app/data/.spotify_cache

      # === Deezer (remove if not used) ===
      # DEEZER_USER_ID: your-user-id
      # DEEZER_PLAYLIST_ID: playlist-id-1 playlist-id-2

      # === Apple Music (remove if not used) ===
      # APPLE_MUSIC_TEAM_ID: your-team-id
      # APPLE_MUSIC_KEY_ID: your-key-id
      # APPLE_MUSIC_PRIVATE_KEY: /app/data/AuthKey.p8
      # APPLE_MUSIC_USER_TOKEN: your-user-token
      # APPLE_MUSIC_STOREFRONT: us

      # === Tidal (remove if not used) ===
      # TIDAL_ACCESS_TOKEN: your-access-token
      # TIDAL_REFRESH_TOKEN: your-refresh-token
      # TIDAL_TOKEN_EXPIRY: 2026-12-31T23:59:59

      # === Qobuz (remove if not used) ===
      # QOBUZ_APP_ID: your-app-id
      # QOBUZ_APP_SECRET: your-app-secret
      # QOBUZ_USERNAME: your-email
      # QOBUZ_PASSWORD: your-password

    volumes:
      - plexist-data:/app/data  # Named volume avoids UID permission issues

volumes:
  plexist-data:

Run with:

docker compose up -d

Data Persistence

The SQLite database is stored at /app/data/plexist.db inside the container. Use a named volume (recommended) for automatic permission handling:

volumes:
  - plexist-data:/app/data

Note: The container runs as non-root user (UID 65532). Named volumes handle permissions automatically. For local development outside Docker, set DB_PATH environment variable to a writable location (e.g., DB_PATH=./data/plexist.db).

Minimal Compose Example (Spotify Only)
services:
  plexist:
    image: gyarbij/plexist:latest
    container_name: plexist
    restart: unless-stopped
    environment:
      PLEX_URL: http://192.168.0.2:32400
      PLEX_TOKEN: your-plex-token
      SPOTIFY_CLIENT_ID: your-client-id
      SPOTIFY_CLIENT_SECRET: your-client-secret
      SPOTIFY_USER_ID: your-user-id
    volumes:
      - plexist-data:/app/data

volumes:
  plexist-data:
Using .env File with Compose

compose.yaml:

services:
  plexist:
    image: gyarbij/plexist:latest
    container_name: plexist
    restart: unless-stopped
    env_file:
      - .env
    volumes:
      - plexist-data:/app/data

volumes:
  plexist-data:

.env:

PLEX_URL=http://192.168.0.2:32400
PLEX_TOKEN=your-plex-token
SPOTIFY_CLIENT_ID=your-client-id
SPOTIFY_CLIENT_SECRET=your-client-secret
SPOTIFY_USER_ID=your-user-id

Testing

# Install dev dependencies
pip3 install -r requirements-dev.txt

# Run tests
pytest

Contributing

See CONTRIBUTING.md for guidelines.

License

See LICENSE for details.