Skip to content

Plex+Playlist=Plexist, An application for recreating Apple Music, Deezer, Spotify, Tidal and QoBuz playlist with Plex (because Plex music playlist are a croc of tihs)

License

Notifications You must be signed in to change notification settings

Gyarbij/Plexist

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

Feature Description
Playlist Sync Recreates your streaming playlists in Plex using files from your library
Multi-Service Sync Sync playlists between any services (e.g., Spotify → Qobuz, Tidal → Plex)
Auto Updates Keeps playlists in sync with your streaming services
New Playlists Automatically creates Plex playlists when added to your streaming service
Liked Tracks Syncs favorited tracks to Plex as 5-star ratings (appears in "Liked Tracks" smart playlist)
ISRC + MBID Matching Uses 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

Service Read (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)

Variable Description
PLEX_URL Your Plex server URL (e.g., http://192.168.0.69:32400)
PLEX_TOKEN Your Plex authentication token — How to find it

Matching & Cache Options

Variable Default Description
MUSICBRAINZ_ENABLED 1 Enable ISRC → MusicBrainz → MBID proxy matching
MUSICBRAINZ_CACHE_TTL_DAYS 90 Cache duration for successful ISRC lookups
MUSICBRAINZ_NEGATIVE_CACHE_TTL_DAYS 7 Cache duration for ISRCs not found in MusicBrainz
MUSICBRAINZ_API_KEY (optional) Optional MusicBrainz API key (sent as a Bearer token)
PLEX_EXTENDED_CACHE_ENABLED 1 Enable extended cache indexes for faster matching
PLEX_DURATION_BUCKET_SECONDS 5 Duration 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 tier Example devices MAX_REQUESTS_PER_SECOND MAX_CONCURRENT_REQUESTS Notes
Low-power Raspberry Pi 3/4, older mini PCs 4–6 2–3 Favor stability over speed; keep concurrency low.
Entry NAS Synology/QNAP (Celeron/Atom) 6–8 3–4 Increase only if CPU stays <70% and Plex remains responsive.
Mid-range Modern desktop CPU (4–8 cores) 10–15 6–8 Good default for most home servers.
High-end 12–24 core workstation/server 15–25 8–12 Watch Plex responsiveness during large syncs.
Cloud VM Azure T4 or similar 12–18 6–10 GPU doesn’t help this workload; tune based on CPU/RAM.
Large server 32+ cores, ample RAM 20–30 12–16 Use 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
Variable Required Description
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_URI For liked tracks OAuth redirect URI (e.g., http://localhost:8888/callback)
SPOTIFY_CACHE_PATH Optional Path 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
Variable Required Description
DEEZER_USER_ID One of these Syncs all playlists for user
DEEZER_PLAYLIST_ID One of these Space-separated playlist IDs
DEEZER_ACCESS_TOKEN For write operations OAuth 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

Variable Required Description
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_TOKEN For library access Music User Token
APPLE_MUSIC_PUBLIC_PLAYLIST_IDS For public playlists Space-separated playlist IDs
APPLE_MUSIC_STOREFRONT For public playlists Storefront code (e.g., us, gb)
APPLE_MUSIC_DEVELOPER_TOKEN_TTL_SECONDS Optional Token TTL (default: 43200)
APPLE_MUSIC_REQUEST_TIMEOUT_SECONDS Optional Request timeout (default: 10)
APPLE_MUSIC_MAX_RETRIES Optional Max retries (default: 3)
APPLE_MUSIC_RETRY_BACKOFF_SECONDS Optional Retry 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()}")
Variable Required Description
TIDAL_ACCESS_TOKEN For user playlists OAuth access token
TIDAL_REFRESH_TOKEN For user playlists OAuth refresh token
TIDAL_TOKEN_EXPIRY For user playlists Expiry datetime (ISO format)
TIDAL_PUBLIC_PLAYLIST_IDS For public playlists Space-separated playlist UUIDs
TIDAL_REQUEST_TIMEOUT_SECONDS Optional Request timeout (default: 10)
TIDAL_MAX_RETRIES Optional Max retries (default: 3)
TIDAL_RETRY_BACKOFF_SECONDS Optional Retry 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.

Variable Required Description
QOBUZ_APP_ID Qobuz app ID
QOBUZ_APP_SECRET Qobuz app secret
QOBUZ_USERNAME For user auth Email address
QOBUZ_PASSWORD For user auth Password
QOBUZ_USER_AUTH_TOKEN Alternative Existing auth token (skips username/password)
QOBUZ_PUBLIC_PLAYLIST_IDS For public playlists Space-separated playlist IDs
QOBUZ_REQUEST_TIMEOUT_SECONDS Optional Request timeout (default: 10)
QOBUZ_MAX_RETRIES Optional Max retries (default: 3)
QOBUZ_RETRY_BACKOFF_SECONDS Optional Retry 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):

Enable Disable
1, y, yes, true, on 0, n, no, false, off

Environment Variables Reference

Core Settings

Variable Default Description
PLEX_URL Required. Your Plex server URL (include http:// or https://)
PLEX_TOKEN Required. Your Plex authentication token
DB_PATH /app/data/plexist.db SQLite database path (mount /app/data)
SECONDS_TO_WAIT 84000 Seconds between sync cycles
LOG_LEVEL INFO Logging level (DEBUG, INFO, WARNING, ERROR)
LOG_FORMAT plain Log format (plain or json)

Playlist Options

Variable Default Description
ADD_PLAYLIST_POSTER yes Add poster artwork to playlists
ADD_PLAYLIST_DESCRIPTION yes Add description to playlists
APPEND_INSTEAD_OF_SYNC no no = Full sync, yes = Append only (no removals)
SYNC_LIKED_TRACKS no Sync liked tracks to Plex 5-star ratings
SYNC_PAIRS Multi-service sync pairs (e.g., spotify:qobuz,tidal:plex)

Output Options

Variable Default Description
WRITE_MISSING_AS_CSV no Write missing tracks to CSV file
WRITE_MISSING_AS_JSON no Write missing tracks to JSON file

Performance Tuning

Variable Default Description
MAX_REQUESTS_PER_SECOND 5 Rate limit for Plex API requests
MAX_CONCURRENT_REQUESTS 4 Maximum 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.

About

Plex+Playlist=Plexist, An application for recreating Apple Music, Deezer, Spotify, Tidal and QoBuz playlist with Plex (because Plex music playlist are a croc of tihs)

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors 4

  •  
  •  
  •  
  •