PlexMix makes AI-generated music playlists from only your local library's content. Syncs your Plex music library to a local SQLite database, generates semantic embeddings for tracks, and uses AI to create personalized playlists based on mood descriptions.
5
stars
163
commits
Python
primary language
Jul 12, 2026
updated
PlexMix syncs your Plex music library to a local SQLite database, generates semantic embeddings for tracks, and uses AI to create personalized playlists based on mood descriptions.
g+key), / to search, Esc to blur# Install from PyPI
pip install plexmix
# Run setup wizard
plexmix config init
# Sync your Plex library (incremental, generates embeddings automatically)
plexmix sync
# Generate AI tags for tracks (enhances search quality)
plexmix tags generate
# Create a playlist
plexmix create "upbeat morning energy"
# With filters
plexmix create "chill evening vibes" --genre jazz --year-min 2010 --limit 30
# Filter by environment and instrument
plexmix create "focus music" --environment study --instrument piano
# Filter by audio features (requires audio analysis)
plexmix create "dance party" --tempo-min 120 --energy high --danceable 0.7
# Smart ordering modes
plexmix create "workout mix" --shuffle energy_curve
plexmix create "variety hour" --shuffle alternating_artists
# Avoid tracks from recent playlists
plexmix create "fresh picks" --avoid-recent 5
# Use alternative AI provider
plexmix create "workout motivation" --provider openai
# Retag tracks older than 30 days
plexmix tags generate --retag-stale 30
# Export/import playlists
plexmix playlist list
plexmix playlist export 1 --format json
plexmix playlist import my_playlist.json
# If you encounter issues (e.g., "0 candidate tracks")
plexmix doctor
# Regenerate all tags and embeddings from scratch (WARNING: destructive)
plexmix sync regenerate
Pre-built multi-platform images (amd64 + arm64) are published to GHCR on every release in two variants:
| Image Tag | Size | Use Case |
|---|---|---|
ghcr.io/izzoa/plexmix:latest | ~470MB | Cloud AI providers + audio analysis (Gemini, OpenAI, Claude, Cohere) |
ghcr.io/izzoa/plexmix:latest-local | ~4GB+ | Everything above + local embeddings and local LLM (includes PyTorch) |
# Pull the slim image (recommended for most users)
docker pull ghcr.io/izzoa/plexmix:latest
# Or pull the local/offline image (includes sentence-transformers + PyTorch)
docker pull ghcr.io/izzoa/plexmix:latest-local
# Run with Docker Compose (clone the repo for docker-compose.yml)
git clone https://github.com/izzoa/plexmix.git
cd plexmix
cp .env.example .env
# Edit .env with your PLEX_TOKEN, GOOGLE_API_KEY, etc.
docker compose up -d
# Access the web UI at http://localhost:3000
# Run CLI commands via Docker
docker compose run plexmix sync
docker compose run plexmix create "chill evening vibes"
Note: The Web UI is currently in Alpha status. The CLI is the recommended way to interact with PlexMix for production use.
# Install with UI extras
pip install "plexmix[ui]"
# Or if using poetry
poetry install -E ui
# Launch the web UI
plexmix ui
# Optional: Specify host and port
plexmix ui --host 0.0.0.0 --port 8000
Then open your browser to http://localhost:3000
Password Protection: Set PLEXMIX_UI_PASSWORD to require a password before accessing the UI. This is recommended when binding to 0.0.0.0 or exposing the UI on a network.
PLEXMIX_UI_PASSWORD=mysecret plexmix ui --host 0.0.0.0

Dashboard — glass icon rail, ⌘K command palette, and a working light/dark theme
The Generator showpiece — describe a vibe and an animated AI-thinking pipeline curates a playlist from your own library
Library — search, filter, sync, and bulk-manage thousands of tracks
Settings — connections, providers, embeddings, and the new Appearance controls (theme, density, accent)
The web interface is built around a slim glass icon rail and a ⌘K command palette:
g+key hints, plus a command palette (⌘K or /) to jump to any page, run an action, or fire a quick vibeg+key navigation, ⌘K / / to open the palette, ↑↓ + Enter to pick a command, Esc to closepip install plexmix
git clone https://github.com/izzoa/plexmix.git
cd plexmix
poetry install
# Install sentence-transformers + PyTorch for offline embeddings and local LLM
pip install "plexmix[local]"
# Or with Poetry
poetry install -E local
# Install with Essentia for audio feature extraction
pip install "plexmix[audio]"
# Or with Poetry
poetry install -E audio
Combine any extras in a single install:
# Install with UI + audio + local LLM support
pip install "plexmix[ui,audio,local]"
# Or with Poetry
poetry install -E ui -E audio -E local
# Pre-built image from GHCR
docker pull ghcr.io/izzoa/plexmix:latest
# Or build locally
git clone https://github.com/izzoa/plexmix.git
cd plexmix
docker compose up -d
PlexMix uses Google Gemini by default for both AI playlist generation and embeddings, requiring only a single API key! Credentials can be stored in the system keyring or passed as environment variables.
X-Plex-Token from the URL# Interactive setup wizard
plexmix config init
# Test Plex server connection
plexmix config test
# Show current configuration
plexmix config show
Troubleshooting Connection Issues:
If you get a "400 Bad Request" error when connecting to Plex:
https:// instead of http:// if your server requires secure connectionsplexmix config test to diagnose the issueCommon Plex Server URLs:
http://localhost:32400http://192.168.1.X:32400 (replace X with your server's IP)https://your-server:32400PlexMix offers three sync modes:
# Incremental sync (default) - Only syncs new/changed/deleted tracks
plexmix sync
# Same as above, but explicit
plexmix sync incremental
# Sync with audio feature analysis (requires essentia)
plexmix sync --audio
# Sync with MusicBrainz metadata enrichment
plexmix sync --musicbrainz
# Regenerate everything from scratch (WARNING: Deletes ALL tags and embeddings)
plexmix sync regenerate
# Legacy alias for incremental sync
plexmix sync full
# Sync without embeddings (faster, but you'll need to generate them later)
plexmix sync --no-embeddings
Sync Mode Comparison:
| Mode | Tracks | Tags | Embeddings | Use Case |
|---|---|---|---|---|
incremental (default) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Regular updates, new tracks added |
incremental --audio | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Sync + audio feature analysis |
full (alias) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Same as incremental (kept for compatibility) |
regenerate | ✅ Syncs everything | ⚠️ DELETES ALL | ⚠️ DELETES ALL | Starting fresh, fixing corrupt data |
When to use each:
plexmix sync → Default for daily use, adding new musicplexmix sync regenerate → When you want to completely regenerate all AI data (tags, embeddings)# Diagnose and fix database issues
plexmix doctor
# Force regenerate all tags and embeddings (DEPRECATED: use 'plexmix sync regenerate' instead)
plexmix doctor --force
What does plexmix doctor do?
When to use:
Note: For complete regeneration of all tags and embeddings, use plexmix sync regenerate instead of doctor --force
# Show database information and statistics
plexmix db info
# Reset database and embeddings (with automatic backup)
plexmix db reset
# Reset without backup (not recommended)
plexmix db reset --no-backup
# Skip confirmation prompt
plexmix db reset --force
What gets deleted:
~/.plexmix/plexmix.db)~/.plexmix/embeddings.index)What gets preserved:
.env, config.yaml)When to use:
doctor can't fixAfter reset:
plexmix sync to re-sync your libraryplexmix tags generate to re-tag tracksBy default, a timestamped backup is created in ~/.plexmix/backups/ before deletion.
Database Command Reference:
| Command | Purpose | When to Use |
|---|---|---|
plexmix db info | Show database stats | Check database health, view track/embedding counts |
plexmix db reset | Delete and reset database | Fresh start, switching providers, unfixable corruption |
plexmix sync | Incremental sync | Regular updates, new tracks |
plexmix sync regenerate | Regenerate all data | Regenerate tags/embeddings, fix data quality |
plexmix doctor | Fix orphaned data | After errors, periodic maintenance |
# Generate AI tags for all untagged tracks
plexmix tags generate
# Also retag tracks whose tags are older than 30 days
plexmix tags generate --retag-stale 30
# Use alternative AI provider
plexmix tags generate --provider openai
# Use the offline/local provider
plexmix tags generate --provider local
# Skip embedding regeneration (faster, but tags won't be in search)
plexmix tags generate --no-regenerate-embeddings
# Generate embeddings for tracks without them
plexmix embeddings generate
# Regenerate all embeddings from scratch
plexmix embeddings generate --regenerate
What are tags? AI-generated metadata (per track) that enhances semantic search:
All metadata is automatically included in embeddings for more accurate mood-based playlist generation.
PlexMix can extract DSP-level audio features from your tracks using Essentia. This enriches embeddings and enables audio-aware playlist filters.
# Install with audio support
pip install "plexmix[audio]"
# Analyze all tracks that don't have audio features yet
plexmix audio analyze
# Re-analyze all tracks (overwrites existing features)
plexmix audio analyze --force
# Show audio analysis statistics
plexmix audio info
# Analyze during sync
plexmix sync --audio
Extracted features:
Audio features are automatically incorporated into embeddings for richer semantic search and can be used as playlist filters (see below).
# Basic playlist (prompts for track count)
plexmix create "happy upbeat summer vibes"
# Specify track count
plexmix create "rainy day melancholy" --limit 25
# Filter by genre
plexmix create "energetic workout" --genre rock --limit 40
# Filter by year range
plexmix create "90s nostalgia" --year-min 1990 --year-max 1999
# Filter by environment (work, study, focus, relax, party, workout, sleep, driving, social)
plexmix create "workout energy" --environment workout
# Filter by instrument (piano, guitar, saxophone, drums, etc.)
plexmix create "piano jazz" --instrument piano
# Use specific AI provider
plexmix create "chill study session" --provider claude
# Force the offline/local provider
plexmix create "ambient focus" --provider local
# Filter by audio features (requires audio analysis)
plexmix create "high energy dance" --tempo-min 120 --tempo-max 140
plexmix create "chill lounge" --energy low
plexmix create "piano ballads in C major" --key C --instrument piano
plexmix create "dance floor bangers" --danceable 0.7
# Custom playlist name
plexmix create "morning coffee" --name "Perfect Morning Mix"
# Track ordering modes
plexmix create "workout" --shuffle energy_curve # Arc-shaped energy progression
plexmix create "party" --shuffle alternating_artists # Maximize artist diversity
plexmix create "random mix" --shuffle random # Fully random order
# Avoid tracks from recent playlists
plexmix create "fresh discoveries" --avoid-recent 10
# Adjust candidate pool multiplier (default: 25x playlist length)
plexmix create "diverse mix" --limit 20 --pool-multiplier 50
# Don't create in Plex (save locally only)
plexmix create "test playlist" --no-create-in-plex
# List all saved playlists
plexmix playlist list
# Export a playlist to JSON
plexmix playlist export 1 --format json --output my_playlist.json
# Export to M3U format
plexmix playlist export 1 --format m3u
# Import a playlist from file
plexmix playlist import my_playlist.json
PlexMix uses a multi-stage pipeline for intelligent playlist generation:
AI Tagging (One-time setup) → Tracks receive:
Audio Analysis (Optional) → Tracks receive DSP features:
Playlist Generation Pipeline:
similarity (default): Closest semantic match firstrandom: Fully randomized orderalternating_artists: Round-robin artist interleaving for maximum varietyenergy_curve: Arc-shaped energy progression using audio featuresplexmix/
├── src/plexmix/
│ ├── ai/ # AI provider implementations
│ │ ├── base.py # Abstract base class
│ │ ├── gemini_provider.py
│ │ ├── openai_provider.py
│ │ ├── claude_provider.py
│ │ ├── cohere_provider.py
│ │ ├── local_provider.py # Managed Hugging Face + custom endpoint (SSE streaming)
│ │ └── tag_generator.py # AI-based tag generation
│ ├── audio/ # Audio feature analysis (optional)
│ │ └── analyzer.py # Essentia-based DSP feature extraction
│ ├── cli/ # Command-line interface (modular)
│ │ ├── main.py # Typer CLI app (entrypoint)
│ │ ├── create_cmd.py # Playlist creation with shuffle/avoid-recent
│ │ ├── tags_cmd.py # Tag generation with --retag-stale
│ │ ├── playlist_cmd.py # Playlist export/import/list
│ │ └── ... # sync, config, db, doctor, embeddings, audio, ui
│ ├── config/ # Configuration management
│ │ ├── settings.py # Pydantic settings
│ │ └── credentials.py # Keyring + env var credential storage
│ ├── database/ # Database layer
│ │ ├── models.py # Pydantic models (incl. PlaylistTemplate)
│ │ ├── sqlite_manager.py # SQLite CRUD with versioned migrations
│ │ └── vector_index.py # FAISS IndexIDMap (incremental updates)
│ ├── plex/ # Plex integration
│ │ ├── client.py # PlexAPI wrapper (token validation, file paths)
│ │ └── sync.py # Sync engine (audio-aware embeddings)
│ ├── playlist/ # Playlist generation
│ │ └── generator.py # Core logic (4 ordering modes, audio filters)
│ ├── services/ # Shared service layer
│ │ ├── providers.py # Centralized provider construction
│ │ └── registry.py # Provider/model/dimension registry
│ ├── ui/ # Web UI (Reflex)
│ │ ├── pages/ # UI pages (8 pages)
│ │ ├── states/ # State management
│ │ ├── components/ # Reusable components (navbar, track_table, etc.)
│ │ ├── job_manager.py # Centralized background task lifecycle
│ │ └── utils/ # UI utilities
│ └── utils/ # Utilities
│ ├── embeddings.py # Embedding providers (audio-enriched, dimension verify)
│ └── logging.py # Logging setup
├── Dockerfile # Container image definition
├── docker-compose.yml # Multi-service orchestration
└── tests/ # Test suite (520+ tests)
└── ui/ # UI tests
PlexMix stores all music metadata locally:
tags_generated_at timestampgeneration_config JSON for rerun capability| Provider | Model | Context Window | Default Temp | Speed | Quality | Cost | Best For |
|---|---|---|---|---|---|---|---|
| Google Gemini ⭐ | gemini-3.5-flash | 1M tokens | n/a¹ | ⚡⚡⚡ Fast | ⭐⭐⭐⭐⭐ Outstanding | 💰 Low | General use, RAG, large contexts (default) |
| OpenAI | gpt-5.4-mini | 400K tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | High-quality reasoning |
| Anthropic | claude-sonnet-4-6 | 200K tokens | 0.7 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰💰 High | Advanced reasoning, analysis |
| OpenAI | gpt-5.5 | 400K tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Frontier | 💰💰💰 High | Most complex tasks |
| Anthropic | claude-opus-4-8 | 1M tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Frontier | 💰💰💰 High | Highest-capability reasoning |
| Cohere | command-a-03-2025 | 256K tokens | 0.3 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | Multilingual, agentic, RAG |
| OpenAI | gpt-5.4-nano | 400K tokens | n/a¹ | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Speed-optimized, efficient |
| Anthropic | claude-haiku-4-5 | 200K tokens | 0.7 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Fast responses, efficiency |
Legend:
| Provider | Model | Dimensions | Quality | Speed | Cost | API Key | Best For |
|---|---|---|---|---|---|---|---|
| Google Gemini ⭐ | gemini-embedding-001 | 3072 | ⭐⭐⭐⭐⭐ Outstanding | ⚡⚡ Moderate | 💰 Low | Required | High-dimensional, accurate semantic search |
| Local | mixedbread-ai/mxbai-embed-large-v1 | 1024 | ⭐⭐⭐⭐ Excellent | ⚡⚡ Moderate | 💰 Free | None | Highest-quality offline retrieval when you can store larger vectors |
| Local | google/embeddinggemma-300m | 768 (Matryoshka) | ⭐⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Flexible local embeddings with truncation to 128/256/512d |
| Cohere | embed-v4 | 256/512/1024/1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰 Low | Required | Flexible dimensions (Matryoshka), multimodal |
| OpenAI | text-embedding-3-small | 1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰💰 Medium | Required | Balanced performance, OpenAI ecosystem |
| Local | nomic-ai/nomic-embed-text-v1.5 | 768 (Matryoshka) | ⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Open-source local embeddings with Matryoshka support |
| Local | sentence-transformers/all-MiniLM-L6-v2 | 384 | ⭐⭐⭐ Good | ⚡⚡⚡ Fast | 💰 Free | None | Offline use on modest hardware |
Key Features:
* EmbeddingGemma and Nomic embeddings support Matryoshka truncation if you need smaller vectors (128/256/512d) without retraining.
When you choose local on the Settings page, PlexMix instantiates the selected Hugging Face sentence-transformers model directly in-process—no HTTP endpoints, API keys, or port configuration are needed. The model weights download once into your Hugging Face cache (e.g., ~/.cache/huggingface) and subsequent embedding calls run entirely on your machine, which keeps everything offline and private.
Set PLEXMIX_LOCAL_EMBEDDING_DEVICE (default cpu) if you want to force a specific device (e.g., cpu to avoid macOS MPS instability, or cuda when running on a GPU server). The UI and CLI will reuse that cached model/device combination whenever local embeddings are needed.
You can now generate playlists with fully local LLMs—no outbound network traffic required. The AI tab in the UI (or plexmix config init) lets you choose between:
google/gemma-3-1b — fast, CPU-friendly drafts (8K context / ~768 new tokens)liquid/lfm2-1.2b — lightweight music-focused reasoning (32K context)google/gemma-3-4b — higher-quality 4B param model (32K context)NousResearch/Yarn-Mistral-7b-128k — 7B param 128K context for huge playlists (GPU recommended)When you select "Local (Offline)" as the AI provider you can toggle between Managed (Downloaded) and Custom Endpoint modes:
Managed (Downloaded)
PLEXMIX_LOCAL_LLM_DEVICE to cpu, cuda, or mps to force device placement (defaults to auto)Custom Endpoint
http://localhost:11434/v1/chat/completions for Ollama)Authorization: Bearer <token>choices[0].message.contentFrom the CLI you can force the local provider as well:
# Use the configured local model for tagging
plexmix tags generate --provider local
# Run the playlist doctor flow with your offline LLM
plexmix doctor --force
If you ever want to nuke cached weights, delete the relevant directories under ~/.cache/huggingface.
Dimension Trade-offs:
gemini-3.5-flash (default). For more advanced reasoning, upgrade to claude-sonnet-4-6, gpt-5.5, or claude-opus-4-8 if you have the budget.gemini-embedding-001 for maximum semantic precision, or text-embedding-3-small if you want faster generation with a slightly smaller vector size.~/.plexmix/credentials and run plexmix config init to verify connectivity. Use plexmix ui --reload during development to check the status cards.gemini-3.5-flash (or gpt-5.4-mini) for playlist prompts so you get the latest reasoning updates.mixedbread-ai/mxbai-embed-large-v1 locally so FAISS never leaves your machine while still benefiting from high-quality vectors.local with managed models (Gemma 3 1B/4B, Liquid LFM 1.2B, Yarn-Mistral 7B) or point at any Ollama/LM Studio endpoint. SSE streaming keeps the UI responsive.mixedbread-ai/mxbai-embed-large-v1 (1024d) for the best similarity recall while keeping everything on disk.PLEXMIX_LOCAL_EMBEDDING_DEVICE=cpu (or cuda if you have a local GPU) so sentence-transformers always uses the right hardware.~/.plexmix/embeddings.index) and prune unused tracks to reduce RAM usage when generating playlists.PlexMix publishes multi-platform Docker images (amd64 + arm64) to GitHub Container Registry on every release in two variants:
| Tag | Includes | Size | Best For |
|---|---|---|---|
:latest / :0.11.0 | Cloud AI SDKs, Reflex UI, FAISS, Essentia | ~470MB | Users with API keys (Gemini, OpenAI, Claude, Cohere) |
:latest-local / :0.11.0-local | Everything above + PyTorch, sentence-transformers | ~4GB+ | Fully offline: local embeddings + local LLM, no API keys needed |
# Option A: Slim image (cloud AI providers)
docker pull ghcr.io/izzoa/plexmix:latest
# Option B: Local/offline image (includes PyTorch for local embeddings + LLM)
docker pull ghcr.io/izzoa/plexmix:latest-local
# Use Docker Compose (clone repo for compose file)
cp .env.example .env
# Edit .env with your credentials (PLEX_TOKEN, GOOGLE_API_KEY, etc.)
docker compose up -d
docker-compose.ymlservices:
plexmix:
image: ghcr.io/izzoa/plexmix:latest
ports:
- "3000:3000" # Web UI
- "8000:8000" # Reflex backend
volumes:
- plexmix-data:/data
# Mount your music library read-only for audio analysis
# - /path/to/music:/music:ro
environment:
- PLEX_URL=http://host.docker.internal:32400
- PLEX_TOKEN=${PLEX_TOKEN}
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
# Optional alternative AI providers
# - OPENAI_API_KEY=${OPENAI_API_KEY:-}
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
# - COHERE_API_KEY=${COHERE_API_KEY:-}
# Optional: protect the web UI with a password
# - PLEXMIX_UI_PASSWORD=changeme
# Audio path remapping (translate Plex paths → container paths)
# - AUDIO_PATH_PREFIX_FROM=/data/music
# - AUDIO_PATH_PREFIX_TO=/music
restart: unless-stopped
volumes:
plexmix-data:
If you map Docker ports to different external ports, set PLEXMIX_API_URL so the frontend can reach the backend:
ports:
- "3152:3000" # Web UI on port 3152
- "8154:8000" # Reflex backend on port 8154
environment:
- PLEXMIX_API_URL=http://your-host-ip:8154
All credentials can be passed as environment variables (no keyring required in containers):
| Variable | Description |
|---|---|
PLEX_URL | Plex server URL (default: http://host.docker.internal:32400) |
PLEX_TOKEN | Plex authentication token |
GOOGLE_API_KEY / GEMINI_API_KEY | Google Gemini API key |
OPENAI_API_KEY | OpenAI API key (optional) |
ANTHROPIC_API_KEY | Anthropic API key (optional) |
COHERE_API_KEY | Cohere API key (optional) |
PLEXMIX_DATA_DIR | Data directory override (default: /data in Docker, ~/.plexmix locally) |
PLEXMIX_UI_HOST | UI bind address (default: 0.0.0.0 in Docker, 127.0.0.1 locally) |
PLEXMIX_UI_PORT | Frontend port (default: 3000) |
PLEXMIX_BACKEND_PORT | Backend port (default: 8000) |
PLEXMIX_API_URL | Public backend URL for WebSocket connections (set when external ports differ, e.g. http://myhost:8154) |
PLEXMIX_ALLOWED_HOSTS | Comma-separated list of allowed hostnames for custom domain access (e.g. plexmix.example.com) |
PLEXMIX_UI_PASSWORD | Optional password to protect the web UI |
AUDIO_ENABLED | Enable audio analysis (default: false) |
AUDIO_ANALYZE_ON_SYNC | Run audio analysis during sync (default: false) |
AUDIO_PATH_PREFIX_FROM | Plex file path prefix to replace (for path remapping) |
AUDIO_PATH_PREFIX_TO | Local file path prefix replacement (for path remapping) |
MUSICBRAINZ_ENRICH_ON_SYNC | Enrich tracks with MusicBrainz data during sync (default: false) |
MUSICBRAINZ_CONTACT_EMAIL | Contact email for MusicBrainz API (recommended by their TOS) |
# Sync library
docker compose run plexmix sync
# Generate tags
docker compose run plexmix tags generate
# Create a playlist
docker compose run plexmix create "chill evening vibes"
# Check database health
docker compose run plexmix db info
Plex reports file paths from its own perspective (e.g., /data/music/Artist/track.flac inside Plex's container). When PlexMix runs in Docker or on a different machine, those paths won't match. Use path remapping to translate Plex paths to local paths for audio analysis:
# docker-compose.yml
services:
plexmix:
volumes:
- /path/to/music:/music:ro # Mount your music library
environment:
- AUDIO_PATH_PREFIX_FROM=/data/music # Plex's path prefix
- AUDIO_PATH_PREFIX_TO=/music # Local path prefix
Or in ~/.plexmix/config.yaml:
audio:
enabled: true
path_prefix_from: /data/music
path_prefix_to: /music
All data (SQLite database, FAISS index, logs) is stored in a named Docker volume (plexmix-data) mapped to /data inside the container. This persists across container restarts.
# Clone repository
git clone https://github.com/izzoa/plexmix.git
cd plexmix
# Install with development dependencies
poetry install
# Run tests
poetry run pytest
# Format code
poetry run black src/
# Lint
poetry run ruff src/
# Type check
poetry run mypy src/
poetry run pytest
poetry run pytest --cov=plexmix --cov-report=html
--embedding-provider localplexmix config); leading/trailing spaces and newlines are now stripped automatically, but an otherwise-wrong key still needs correcting.plexmix doctor to check for database issuesplexmix syncplexmix doctor to detect and fix orphaned embeddingsPlexMix syncs your Plex music library to a local SQLite database, generates AI-powered tags (mood, instruments, environments) for each track, creates semantic embeddings, and uses vector similarity search combined with LLM intelligence to generate playlists from natural language mood descriptions.
Yes, but only one! Google Gemini is the default provider for both AI and embeddings. You can get a free API key at Google AI Studio. Alternative providers (OpenAI, Anthropic, local embeddings) are optional.
Google Gemini (default):
Alternatives:
Total: ~1-2 hours for a large library. You can interrupt and resume at any time.
Partially. After initial sync and tag/embedding generation, you can:
pip install "plexmix[local]")pip install "plexmix[local]")All three are automatically generated by AI and improve playlist quality.
This usually means:
plexmix embeddings generateplexmix doctor to fixplexmix sync completed successfullyNot yet. Currently PlexMix supports one music library at a time. Multi-library support is on the roadmap.
Only when creating playlists. PlexMix:
--create-in-plex)Run plexmix sync to update your local database. The incremental sync will:
Or use plexmix doctor to clean up orphaned data.
Yes! Your database is stored at ~/.plexmix/plexmix.db. Simply copy this file and the ~/.plexmix/embeddings.index file to backup all your data, tags, and embeddings.
pip install --upgrade plexmix
After updating, run plexmix sync --no-embeddings to apply any database migrations.
Absolutely! See CONTRIBUTING.md for guidelines. We welcome bug reports, feature requests, and pull requests.
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
This project is licensed under the GNU Affero General Public License v3.0 - see LICENSE for details.
Made with ❤️ for music lovers
163 commits
Python
95.0%
CSS
4.9%
PlexMix makes AI-generated music playlists from only your local library's content. Syncs your Plex music library to a local SQLite database, generates semantic embeddings for tracks, and uses AI to create personalized playlists based on mood descriptions.
5
stars
163
commits
Python
primary language
Jul 12, 2026
updated
PlexMix syncs your Plex music library to a local SQLite database, generates semantic embeddings for tracks, and uses AI to create personalized playlists based on mood descriptions.
g+key), / to search, Esc to blur# Install from PyPI
pip install plexmix
# Run setup wizard
plexmix config init
# Sync your Plex library (incremental, generates embeddings automatically)
plexmix sync
# Generate AI tags for tracks (enhances search quality)
plexmix tags generate
# Create a playlist
plexmix create "upbeat morning energy"
# With filters
plexmix create "chill evening vibes" --genre jazz --year-min 2010 --limit 30
# Filter by environment and instrument
plexmix create "focus music" --environment study --instrument piano
# Filter by audio features (requires audio analysis)
plexmix create "dance party" --tempo-min 120 --energy high --danceable 0.7
# Smart ordering modes
plexmix create "workout mix" --shuffle energy_curve
plexmix create "variety hour" --shuffle alternating_artists
# Avoid tracks from recent playlists
plexmix create "fresh picks" --avoid-recent 5
# Use alternative AI provider
plexmix create "workout motivation" --provider openai
# Retag tracks older than 30 days
plexmix tags generate --retag-stale 30
# Export/import playlists
plexmix playlist list
plexmix playlist export 1 --format json
plexmix playlist import my_playlist.json
# If you encounter issues (e.g., "0 candidate tracks")
plexmix doctor
# Regenerate all tags and embeddings from scratch (WARNING: destructive)
plexmix sync regenerate
Pre-built multi-platform images (amd64 + arm64) are published to GHCR on every release in two variants:
| Image Tag | Size | Use Case |
|---|---|---|
ghcr.io/izzoa/plexmix:latest | ~470MB | Cloud AI providers + audio analysis (Gemini, OpenAI, Claude, Cohere) |
ghcr.io/izzoa/plexmix:latest-local | ~4GB+ | Everything above + local embeddings and local LLM (includes PyTorch) |
# Pull the slim image (recommended for most users)
docker pull ghcr.io/izzoa/plexmix:latest
# Or pull the local/offline image (includes sentence-transformers + PyTorch)
docker pull ghcr.io/izzoa/plexmix:latest-local
# Run with Docker Compose (clone the repo for docker-compose.yml)
git clone https://github.com/izzoa/plexmix.git
cd plexmix
cp .env.example .env
# Edit .env with your PLEX_TOKEN, GOOGLE_API_KEY, etc.
docker compose up -d
# Access the web UI at http://localhost:3000
# Run CLI commands via Docker
docker compose run plexmix sync
docker compose run plexmix create "chill evening vibes"
Note: The Web UI is currently in Alpha status. The CLI is the recommended way to interact with PlexMix for production use.
# Install with UI extras
pip install "plexmix[ui]"
# Or if using poetry
poetry install -E ui
# Launch the web UI
plexmix ui
# Optional: Specify host and port
plexmix ui --host 0.0.0.0 --port 8000
Then open your browser to http://localhost:3000
Password Protection: Set PLEXMIX_UI_PASSWORD to require a password before accessing the UI. This is recommended when binding to 0.0.0.0 or exposing the UI on a network.
PLEXMIX_UI_PASSWORD=mysecret plexmix ui --host 0.0.0.0

Dashboard — glass icon rail, ⌘K command palette, and a working light/dark theme
The Generator showpiece — describe a vibe and an animated AI-thinking pipeline curates a playlist from your own library
Library — search, filter, sync, and bulk-manage thousands of tracks
Settings — connections, providers, embeddings, and the new Appearance controls (theme, density, accent)
The web interface is built around a slim glass icon rail and a ⌘K command palette:
g+key hints, plus a command palette (⌘K or /) to jump to any page, run an action, or fire a quick vibeg+key navigation, ⌘K / / to open the palette, ↑↓ + Enter to pick a command, Esc to closepip install plexmix
git clone https://github.com/izzoa/plexmix.git
cd plexmix
poetry install
# Install sentence-transformers + PyTorch for offline embeddings and local LLM
pip install "plexmix[local]"
# Or with Poetry
poetry install -E local
# Install with Essentia for audio feature extraction
pip install "plexmix[audio]"
# Or with Poetry
poetry install -E audio
Combine any extras in a single install:
# Install with UI + audio + local LLM support
pip install "plexmix[ui,audio,local]"
# Or with Poetry
poetry install -E ui -E audio -E local
# Pre-built image from GHCR
docker pull ghcr.io/izzoa/plexmix:latest
# Or build locally
git clone https://github.com/izzoa/plexmix.git
cd plexmix
docker compose up -d
PlexMix uses Google Gemini by default for both AI playlist generation and embeddings, requiring only a single API key! Credentials can be stored in the system keyring or passed as environment variables.
X-Plex-Token from the URL# Interactive setup wizard
plexmix config init
# Test Plex server connection
plexmix config test
# Show current configuration
plexmix config show
Troubleshooting Connection Issues:
If you get a "400 Bad Request" error when connecting to Plex:
https:// instead of http:// if your server requires secure connectionsplexmix config test to diagnose the issueCommon Plex Server URLs:
http://localhost:32400http://192.168.1.X:32400 (replace X with your server's IP)https://your-server:32400PlexMix offers three sync modes:
# Incremental sync (default) - Only syncs new/changed/deleted tracks
plexmix sync
# Same as above, but explicit
plexmix sync incremental
# Sync with audio feature analysis (requires essentia)
plexmix sync --audio
# Sync with MusicBrainz metadata enrichment
plexmix sync --musicbrainz
# Regenerate everything from scratch (WARNING: Deletes ALL tags and embeddings)
plexmix sync regenerate
# Legacy alias for incremental sync
plexmix sync full
# Sync without embeddings (faster, but you'll need to generate them later)
plexmix sync --no-embeddings
Sync Mode Comparison:
| Mode | Tracks | Tags | Embeddings | Use Case |
|---|---|---|---|---|
incremental (default) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Regular updates, new tracks added |
incremental --audio | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Sync + audio feature analysis |
full (alias) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Same as incremental (kept for compatibility) |
regenerate | ✅ Syncs everything | ⚠️ DELETES ALL | ⚠️ DELETES ALL | Starting fresh, fixing corrupt data |
When to use each:
plexmix sync → Default for daily use, adding new musicplexmix sync regenerate → When you want to completely regenerate all AI data (tags, embeddings)# Diagnose and fix database issues
plexmix doctor
# Force regenerate all tags and embeddings (DEPRECATED: use 'plexmix sync regenerate' instead)
plexmix doctor --force
What does plexmix doctor do?
When to use:
Note: For complete regeneration of all tags and embeddings, use plexmix sync regenerate instead of doctor --force
# Show database information and statistics
plexmix db info
# Reset database and embeddings (with automatic backup)
plexmix db reset
# Reset without backup (not recommended)
plexmix db reset --no-backup
# Skip confirmation prompt
plexmix db reset --force
What gets deleted:
~/.plexmix/plexmix.db)~/.plexmix/embeddings.index)What gets preserved:
.env, config.yaml)When to use:
doctor can't fixAfter reset:
plexmix sync to re-sync your libraryplexmix tags generate to re-tag tracksBy default, a timestamped backup is created in ~/.plexmix/backups/ before deletion.
Database Command Reference:
| Command | Purpose | When to Use |
|---|---|---|
plexmix db info | Show database stats | Check database health, view track/embedding counts |
plexmix db reset | Delete and reset database | Fresh start, switching providers, unfixable corruption |
plexmix sync | Incremental sync | Regular updates, new tracks |
plexmix sync regenerate | Regenerate all data | Regenerate tags/embeddings, fix data quality |
plexmix doctor | Fix orphaned data | After errors, periodic maintenance |
# Generate AI tags for all untagged tracks
plexmix tags generate
# Also retag tracks whose tags are older than 30 days
plexmix tags generate --retag-stale 30
# Use alternative AI provider
plexmix tags generate --provider openai
# Use the offline/local provider
plexmix tags generate --provider local
# Skip embedding regeneration (faster, but tags won't be in search)
plexmix tags generate --no-regenerate-embeddings
# Generate embeddings for tracks without them
plexmix embeddings generate
# Regenerate all embeddings from scratch
plexmix embeddings generate --regenerate
What are tags? AI-generated metadata (per track) that enhances semantic search:
All metadata is automatically included in embeddings for more accurate mood-based playlist generation.
PlexMix can extract DSP-level audio features from your tracks using Essentia. This enriches embeddings and enables audio-aware playlist filters.
# Install with audio support
pip install "plexmix[audio]"
# Analyze all tracks that don't have audio features yet
plexmix audio analyze
# Re-analyze all tracks (overwrites existing features)
plexmix audio analyze --force
# Show audio analysis statistics
plexmix audio info
# Analyze during sync
plexmix sync --audio
Extracted features:
Audio features are automatically incorporated into embeddings for richer semantic search and can be used as playlist filters (see below).
# Basic playlist (prompts for track count)
plexmix create "happy upbeat summer vibes"
# Specify track count
plexmix create "rainy day melancholy" --limit 25
# Filter by genre
plexmix create "energetic workout" --genre rock --limit 40
# Filter by year range
plexmix create "90s nostalgia" --year-min 1990 --year-max 1999
# Filter by environment (work, study, focus, relax, party, workout, sleep, driving, social)
plexmix create "workout energy" --environment workout
# Filter by instrument (piano, guitar, saxophone, drums, etc.)
plexmix create "piano jazz" --instrument piano
# Use specific AI provider
plexmix create "chill study session" --provider claude
# Force the offline/local provider
plexmix create "ambient focus" --provider local
# Filter by audio features (requires audio analysis)
plexmix create "high energy dance" --tempo-min 120 --tempo-max 140
plexmix create "chill lounge" --energy low
plexmix create "piano ballads in C major" --key C --instrument piano
plexmix create "dance floor bangers" --danceable 0.7
# Custom playlist name
plexmix create "morning coffee" --name "Perfect Morning Mix"
# Track ordering modes
plexmix create "workout" --shuffle energy_curve # Arc-shaped energy progression
plexmix create "party" --shuffle alternating_artists # Maximize artist diversity
plexmix create "random mix" --shuffle random # Fully random order
# Avoid tracks from recent playlists
plexmix create "fresh discoveries" --avoid-recent 10
# Adjust candidate pool multiplier (default: 25x playlist length)
plexmix create "diverse mix" --limit 20 --pool-multiplier 50
# Don't create in Plex (save locally only)
plexmix create "test playlist" --no-create-in-plex
# List all saved playlists
plexmix playlist list
# Export a playlist to JSON
plexmix playlist export 1 --format json --output my_playlist.json
# Export to M3U format
plexmix playlist export 1 --format m3u
# Import a playlist from file
plexmix playlist import my_playlist.json
PlexMix uses a multi-stage pipeline for intelligent playlist generation:
AI Tagging (One-time setup) → Tracks receive:
Audio Analysis (Optional) → Tracks receive DSP features:
Playlist Generation Pipeline:
similarity (default): Closest semantic match firstrandom: Fully randomized orderalternating_artists: Round-robin artist interleaving for maximum varietyenergy_curve: Arc-shaped energy progression using audio featuresplexmix/
├── src/plexmix/
│ ├── ai/ # AI provider implementations
│ │ ├── base.py # Abstract base class
│ │ ├── gemini_provider.py
│ │ ├── openai_provider.py
│ │ ├── claude_provider.py
│ │ ├── cohere_provider.py
│ │ ├── local_provider.py # Managed Hugging Face + custom endpoint (SSE streaming)
│ │ └── tag_generator.py # AI-based tag generation
│ ├── audio/ # Audio feature analysis (optional)
│ │ └── analyzer.py # Essentia-based DSP feature extraction
│ ├── cli/ # Command-line interface (modular)
│ │ ├── main.py # Typer CLI app (entrypoint)
│ │ ├── create_cmd.py # Playlist creation with shuffle/avoid-recent
│ │ ├── tags_cmd.py # Tag generation with --retag-stale
│ │ ├── playlist_cmd.py # Playlist export/import/list
│ │ └── ... # sync, config, db, doctor, embeddings, audio, ui
│ ├── config/ # Configuration management
│ │ ├── settings.py # Pydantic settings
│ │ └── credentials.py # Keyring + env var credential storage
│ ├── database/ # Database layer
│ │ ├── models.py # Pydantic models (incl. PlaylistTemplate)
│ │ ├── sqlite_manager.py # SQLite CRUD with versioned migrations
│ │ └── vector_index.py # FAISS IndexIDMap (incremental updates)
│ ├── plex/ # Plex integration
│ │ ├── client.py # PlexAPI wrapper (token validation, file paths)
│ │ └── sync.py # Sync engine (audio-aware embeddings)
│ ├── playlist/ # Playlist generation
│ │ └── generator.py # Core logic (4 ordering modes, audio filters)
│ ├── services/ # Shared service layer
│ │ ├── providers.py # Centralized provider construction
│ │ └── registry.py # Provider/model/dimension registry
│ ├── ui/ # Web UI (Reflex)
│ │ ├── pages/ # UI pages (8 pages)
│ │ ├── states/ # State management
│ │ ├── components/ # Reusable components (navbar, track_table, etc.)
│ │ ├── job_manager.py # Centralized background task lifecycle
│ │ └── utils/ # UI utilities
│ └── utils/ # Utilities
│ ├── embeddings.py # Embedding providers (audio-enriched, dimension verify)
│ └── logging.py # Logging setup
├── Dockerfile # Container image definition
├── docker-compose.yml # Multi-service orchestration
└── tests/ # Test suite (520+ tests)
└── ui/ # UI tests
PlexMix stores all music metadata locally:
tags_generated_at timestampgeneration_config JSON for rerun capability| Provider | Model | Context Window | Default Temp | Speed | Quality | Cost | Best For |
|---|---|---|---|---|---|---|---|
| Google Gemini ⭐ | gemini-3.5-flash | 1M tokens | n/a¹ | ⚡⚡⚡ Fast | ⭐⭐⭐⭐⭐ Outstanding | 💰 Low | General use, RAG, large contexts (default) |
| OpenAI | gpt-5.4-mini | 400K tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | High-quality reasoning |
| Anthropic | claude-sonnet-4-6 | 200K tokens | 0.7 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰💰 High | Advanced reasoning, analysis |
| OpenAI | gpt-5.5 | 400K tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Frontier | 💰💰💰 High | Most complex tasks |
| Anthropic | claude-opus-4-8 | 1M tokens | n/a¹ | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Frontier | 💰💰💰 High | Highest-capability reasoning |
| Cohere | command-a-03-2025 | 256K tokens | 0.3 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | Multilingual, agentic, RAG |
| OpenAI | gpt-5.4-nano | 400K tokens | n/a¹ | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Speed-optimized, efficient |
| Anthropic | claude-haiku-4-5 | 200K tokens | 0.7 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Fast responses, efficiency |
Legend:
| Provider | Model | Dimensions | Quality | Speed | Cost | API Key | Best For |
|---|---|---|---|---|---|---|---|
| Google Gemini ⭐ | gemini-embedding-001 | 3072 | ⭐⭐⭐⭐⭐ Outstanding | ⚡⚡ Moderate | 💰 Low | Required | High-dimensional, accurate semantic search |
| Local | mixedbread-ai/mxbai-embed-large-v1 | 1024 | ⭐⭐⭐⭐ Excellent | ⚡⚡ Moderate | 💰 Free | None | Highest-quality offline retrieval when you can store larger vectors |
| Local | google/embeddinggemma-300m | 768 (Matryoshka) | ⭐⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Flexible local embeddings with truncation to 128/256/512d |
| Cohere | embed-v4 | 256/512/1024/1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰 Low | Required | Flexible dimensions (Matryoshka), multimodal |
| OpenAI | text-embedding-3-small | 1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰💰 Medium | Required | Balanced performance, OpenAI ecosystem |
| Local | nomic-ai/nomic-embed-text-v1.5 | 768 (Matryoshka) | ⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Open-source local embeddings with Matryoshka support |
| Local | sentence-transformers/all-MiniLM-L6-v2 | 384 | ⭐⭐⭐ Good | ⚡⚡⚡ Fast | 💰 Free | None | Offline use on modest hardware |
Key Features:
* EmbeddingGemma and Nomic embeddings support Matryoshka truncation if you need smaller vectors (128/256/512d) without retraining.
When you choose local on the Settings page, PlexMix instantiates the selected Hugging Face sentence-transformers model directly in-process—no HTTP endpoints, API keys, or port configuration are needed. The model weights download once into your Hugging Face cache (e.g., ~/.cache/huggingface) and subsequent embedding calls run entirely on your machine, which keeps everything offline and private.
Set PLEXMIX_LOCAL_EMBEDDING_DEVICE (default cpu) if you want to force a specific device (e.g., cpu to avoid macOS MPS instability, or cuda when running on a GPU server). The UI and CLI will reuse that cached model/device combination whenever local embeddings are needed.
You can now generate playlists with fully local LLMs—no outbound network traffic required. The AI tab in the UI (or plexmix config init) lets you choose between:
google/gemma-3-1b — fast, CPU-friendly drafts (8K context / ~768 new tokens)liquid/lfm2-1.2b — lightweight music-focused reasoning (32K context)google/gemma-3-4b — higher-quality 4B param model (32K context)NousResearch/Yarn-Mistral-7b-128k — 7B param 128K context for huge playlists (GPU recommended)When you select "Local (Offline)" as the AI provider you can toggle between Managed (Downloaded) and Custom Endpoint modes:
Managed (Downloaded)
PLEXMIX_LOCAL_LLM_DEVICE to cpu, cuda, or mps to force device placement (defaults to auto)Custom Endpoint
http://localhost:11434/v1/chat/completions for Ollama)Authorization: Bearer <token>choices[0].message.contentFrom the CLI you can force the local provider as well:
# Use the configured local model for tagging
plexmix tags generate --provider local
# Run the playlist doctor flow with your offline LLM
plexmix doctor --force
If you ever want to nuke cached weights, delete the relevant directories under ~/.cache/huggingface.
Dimension Trade-offs:
gemini-3.5-flash (default). For more advanced reasoning, upgrade to claude-sonnet-4-6, gpt-5.5, or claude-opus-4-8 if you have the budget.gemini-embedding-001 for maximum semantic precision, or text-embedding-3-small if you want faster generation with a slightly smaller vector size.~/.plexmix/credentials and run plexmix config init to verify connectivity. Use plexmix ui --reload during development to check the status cards.gemini-3.5-flash (or gpt-5.4-mini) for playlist prompts so you get the latest reasoning updates.mixedbread-ai/mxbai-embed-large-v1 locally so FAISS never leaves your machine while still benefiting from high-quality vectors.local with managed models (Gemma 3 1B/4B, Liquid LFM 1.2B, Yarn-Mistral 7B) or point at any Ollama/LM Studio endpoint. SSE streaming keeps the UI responsive.mixedbread-ai/mxbai-embed-large-v1 (1024d) for the best similarity recall while keeping everything on disk.PLEXMIX_LOCAL_EMBEDDING_DEVICE=cpu (or cuda if you have a local GPU) so sentence-transformers always uses the right hardware.~/.plexmix/embeddings.index) and prune unused tracks to reduce RAM usage when generating playlists.PlexMix publishes multi-platform Docker images (amd64 + arm64) to GitHub Container Registry on every release in two variants:
| Tag | Includes | Size | Best For |
|---|---|---|---|
:latest / :0.11.0 | Cloud AI SDKs, Reflex UI, FAISS, Essentia | ~470MB | Users with API keys (Gemini, OpenAI, Claude, Cohere) |
:latest-local / :0.11.0-local | Everything above + PyTorch, sentence-transformers | ~4GB+ | Fully offline: local embeddings + local LLM, no API keys needed |
# Option A: Slim image (cloud AI providers)
docker pull ghcr.io/izzoa/plexmix:latest
# Option B: Local/offline image (includes PyTorch for local embeddings + LLM)
docker pull ghcr.io/izzoa/plexmix:latest-local
# Use Docker Compose (clone repo for compose file)
cp .env.example .env
# Edit .env with your credentials (PLEX_TOKEN, GOOGLE_API_KEY, etc.)
docker compose up -d
docker-compose.ymlservices:
plexmix:
image: ghcr.io/izzoa/plexmix:latest
ports:
- "3000:3000" # Web UI
- "8000:8000" # Reflex backend
volumes:
- plexmix-data:/data
# Mount your music library read-only for audio analysis
# - /path/to/music:/music:ro
environment:
- PLEX_URL=http://host.docker.internal:32400
- PLEX_TOKEN=${PLEX_TOKEN}
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
# Optional alternative AI providers
# - OPENAI_API_KEY=${OPENAI_API_KEY:-}
# - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
# - COHERE_API_KEY=${COHERE_API_KEY:-}
# Optional: protect the web UI with a password
# - PLEXMIX_UI_PASSWORD=changeme
# Audio path remapping (translate Plex paths → container paths)
# - AUDIO_PATH_PREFIX_FROM=/data/music
# - AUDIO_PATH_PREFIX_TO=/music
restart: unless-stopped
volumes:
plexmix-data:
If you map Docker ports to different external ports, set PLEXMIX_API_URL so the frontend can reach the backend:
ports:
- "3152:3000" # Web UI on port 3152
- "8154:8000" # Reflex backend on port 8154
environment:
- PLEXMIX_API_URL=http://your-host-ip:8154
All credentials can be passed as environment variables (no keyring required in containers):
| Variable | Description |
|---|---|
PLEX_URL | Plex server URL (default: http://host.docker.internal:32400) |
PLEX_TOKEN | Plex authentication token |
GOOGLE_API_KEY / GEMINI_API_KEY | Google Gemini API key |
OPENAI_API_KEY | OpenAI API key (optional) |
ANTHROPIC_API_KEY | Anthropic API key (optional) |
COHERE_API_KEY | Cohere API key (optional) |
PLEXMIX_DATA_DIR | Data directory override (default: /data in Docker, ~/.plexmix locally) |
PLEXMIX_UI_HOST | UI bind address (default: 0.0.0.0 in Docker, 127.0.0.1 locally) |
PLEXMIX_UI_PORT | Frontend port (default: 3000) |
PLEXMIX_BACKEND_PORT | Backend port (default: 8000) |
PLEXMIX_API_URL | Public backend URL for WebSocket connections (set when external ports differ, e.g. http://myhost:8154) |
PLEXMIX_ALLOWED_HOSTS | Comma-separated list of allowed hostnames for custom domain access (e.g. plexmix.example.com) |
PLEXMIX_UI_PASSWORD | Optional password to protect the web UI |
AUDIO_ENABLED | Enable audio analysis (default: false) |
AUDIO_ANALYZE_ON_SYNC | Run audio analysis during sync (default: false) |
AUDIO_PATH_PREFIX_FROM | Plex file path prefix to replace (for path remapping) |
AUDIO_PATH_PREFIX_TO | Local file path prefix replacement (for path remapping) |
MUSICBRAINZ_ENRICH_ON_SYNC | Enrich tracks with MusicBrainz data during sync (default: false) |
MUSICBRAINZ_CONTACT_EMAIL | Contact email for MusicBrainz API (recommended by their TOS) |
# Sync library
docker compose run plexmix sync
# Generate tags
docker compose run plexmix tags generate
# Create a playlist
docker compose run plexmix create "chill evening vibes"
# Check database health
docker compose run plexmix db info
Plex reports file paths from its own perspective (e.g., /data/music/Artist/track.flac inside Plex's container). When PlexMix runs in Docker or on a different machine, those paths won't match. Use path remapping to translate Plex paths to local paths for audio analysis:
# docker-compose.yml
services:
plexmix:
volumes:
- /path/to/music:/music:ro # Mount your music library
environment:
- AUDIO_PATH_PREFIX_FROM=/data/music # Plex's path prefix
- AUDIO_PATH_PREFIX_TO=/music # Local path prefix
Or in ~/.plexmix/config.yaml:
audio:
enabled: true
path_prefix_from: /data/music
path_prefix_to: /music
All data (SQLite database, FAISS index, logs) is stored in a named Docker volume (plexmix-data) mapped to /data inside the container. This persists across container restarts.
# Clone repository
git clone https://github.com/izzoa/plexmix.git
cd plexmix
# Install with development dependencies
poetry install
# Run tests
poetry run pytest
# Format code
poetry run black src/
# Lint
poetry run ruff src/
# Type check
poetry run mypy src/
poetry run pytest
poetry run pytest --cov=plexmix --cov-report=html
--embedding-provider localplexmix config); leading/trailing spaces and newlines are now stripped automatically, but an otherwise-wrong key still needs correcting.plexmix doctor to check for database issuesplexmix syncplexmix doctor to detect and fix orphaned embeddingsPlexMix syncs your Plex music library to a local SQLite database, generates AI-powered tags (mood, instruments, environments) for each track, creates semantic embeddings, and uses vector similarity search combined with LLM intelligence to generate playlists from natural language mood descriptions.
Yes, but only one! Google Gemini is the default provider for both AI and embeddings. You can get a free API key at Google AI Studio. Alternative providers (OpenAI, Anthropic, local embeddings) are optional.
Google Gemini (default):
Alternatives:
Total: ~1-2 hours for a large library. You can interrupt and resume at any time.
Partially. After initial sync and tag/embedding generation, you can:
pip install "plexmix[local]")pip install "plexmix[local]")All three are automatically generated by AI and improve playlist quality.
This usually means:
plexmix embeddings generateplexmix doctor to fixplexmix sync completed successfullyNot yet. Currently PlexMix supports one music library at a time. Multi-library support is on the roadmap.
Only when creating playlists. PlexMix:
--create-in-plex)Run plexmix sync to update your local database. The incremental sync will:
Or use plexmix doctor to clean up orphaned data.
Yes! Your database is stored at ~/.plexmix/plexmix.db. Simply copy this file and the ~/.plexmix/embeddings.index file to backup all your data, tags, and embeddings.
pip install --upgrade plexmix
After updating, run plexmix sync --no-embeddings to apply any database migrations.
Absolutely! See CONTRIBUTING.md for guidelines. We welcome bug reports, feature requests, and pull requests.
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
This project is licensed under the GNU Affero General Public License v3.0 - see LICENSE for details.
Made with ❤️ for music lovers
163 commits
Python
95.0%
CSS
4.9%