Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

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, MusicBrainz MBID proxy matching, and Plex's native matcher before 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, then Plex native matching)
  • 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). Plex destinations use the full Plex pipeline: cached matching, playlist posters/descriptions, missing-track CSV/JSON exports and SYNC_LIKED_TRACKS.

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

Spotify's Web API only serves user playlists through a user-authorized token, and apps created since late 2024 stay in Development Mode. Plexist therefore signs you in with OAuth once, then caches the refresh token in /app/data.

Requirements

  • A Spotify Premium account (required for the owner of a Development Mode app)
  • An app in the Spotify Developer Dashboard:
    1. Create app → note the Client ID and Client Secret
    2. Add the redirect URI http://127.0.0.1:8888/callback (Spotify rejects localhost; use the loopback IP or an https:// URL). It must match SPOTIFY_REDIRECT_URI exactly.
    3. If the Spotify account you sync is not the app owner, add it under Settings → User Management (Development Mode allows up to 5 users). Accounts that are not on this list get 403 Forbidden.

First-Run Authorization

  1. Start the container. The log prints an https://accounts.spotify.com/authorize?... URL.
  2. Open it in a browser while logged in to the Spotify account you want to sync and approve access.
  3. The browser is redirected to http://127.0.0.1:8888/callback?code=... — the page will not load; that is expected. Copy the full URL from the address bar.
  4. Provide it to Plexist either way:
    • set SPOTIFY_AUTH_RESPONSE=<pasted URL> and restart the container, or
    • write it to /app/data/spotify_auth_response.txt inside the data volume (Plexist polls this file for a few minutes after printing the URL).
  5. The token is cached at /app/data/.spotify_cache and refreshed automatically. Remove SPOTIFY_AUTH_RESPONSE afterwards — authorization codes are single-use.

Running outside Docker with a terminal attached? Plexist prompts for the URL interactively instead.

VariableRequiredDescription
SPOTIFY_CLIENT_IDYour Spotify app Client ID
SPOTIFY_CLIENT_SECRETYour Spotify app Client Secret
SPOTIFY_REDIRECT_URIOptionalRedirect URI registered for the app (default: http://127.0.0.1:8888/callback)
SPOTIFY_AUTH_RESPONSEFirst runThe redirected URL (or just the code) from the authorization step
SPOTIFY_CACHE_PATHOptionalToken cache file (default: .spotify_cache next to DB_PATH)
SPOTIFY_USER_IDOptionalOnly sync playlists owned by this user ID (followed playlists are skipped). Leave unset to sync every playlist in the library.

Liked tracks (SYNC_LIKED_TRACKS) use the same token; no extra setup is needed.

Troubleshooting

SymptomFix
Spotify authorization required on every cycleComplete the first-run steps above; check that /app/data is a persistent volume so the cache survives restarts.
HTTP 403 / ForbiddenThe authorized account is not the app owner or not in User Management, the owner account is not Premium, or the redirect URI does not match the dashboard.
Spotify rejected the authorization responseThe pasted code was already used or expired. Clear SPOTIFY_AUTH_RESPONSE, delete spotify_auth_response.txt, and authorize again with the newly logged URL.
Redirect URI errors in the browserMake sure the dashboard entry uses http://127.0.0.1:8888/callback, not localhost.
🟣 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_IDApple Developer Team ID
APPLE_MUSIC_KEY_IDMusicKit Key ID
APPLE_MUSIC_PRIVATE_KEYKey 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.

💡 Matching: Library items are fetched with their catalog counterpart (include=catalog), so ISRCs are available for exact Plex/MusicBrainz matching. Music videos in playlists are skipped.

🔵 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_IDQobuz app ID
QOBUZ_APP_SECRETQobuz 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_URLRequired. Your Plex server URL (include http:// or https://)
PLEX_TOKENRequired. 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_PAIRSMulti-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 \
  -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_REDIRECT_URI=http://127.0.0.1:8888/callback \
  -e SPOTIFY_AUTH_RESPONSE= \
  -e SPOTIFY_USER_ID=your-user-id \
  -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_REDIRECT_URI: http://127.0.0.1:8888/callback  # Must match the Developer Dashboard
      # SPOTIFY_AUTH_RESPONSE:  # First run only: paste the redirected URL from the logged authorize link
      # SPOTIFY_USER_ID: your-user-id  # Optional: only sync playlists owned by this user
      # 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
    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

Testing

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

# Lint
ruff check .

# 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)
apple-musicapple-music-playlistapple-music-plexdeezerdeezer-playlistdeezer-utilplex-deezerplex-playlistplex-spotifyplex-utillsplexistqobuz-playlistspotifyspotify-playlistspotify-utiltidal-playlist

语言 Languages

Python99.7%
Dockerfile0.3%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
111
Total Commits
峰值: 63次/周
Less
More

核心贡献者 Contributors