{"id":34109910,"url":"https://github.com/izzoa/plexmix","last_synced_at":"2026-03-13T01:02:16.383Z","repository":{"id":317731663,"uuid":"1068450713","full_name":"izzoa/plexmix","owner":"izzoa","description":"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.","archived":false,"fork":false,"pushed_at":"2026-03-12T15:18:52.000Z","size":17671,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-12T19:54:39.188Z","etag":null,"topics":["ai","dj","music","plex","plex-api"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/izzoa.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-10-02T12:05:00.000Z","updated_at":"2026-03-12T15:19:29.000Z","dependencies_parsed_at":"2025-10-02T18:37:02.983Z","dependency_job_id":"4d0181bb-f0b5-4740-9510-05c51b0b3692","html_url":"https://github.com/izzoa/plexmix","commit_stats":null,"previous_names":["izzoa/plexmix"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/izzoa/plexmix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzoa%2Fplexmix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzoa%2Fplexmix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzoa%2Fplexmix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzoa%2Fplexmix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izzoa","download_url":"https://codeload.github.com/izzoa/plexmix/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izzoa%2Fplexmix/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30450872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T21:31:01.033Z","status":"ssl_error","status_checked_at":"2026-03-12T21:30:43.161Z","response_time":114,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","dj","music","plex","plex-api"],"created_at":"2025-12-14T18:40:20.383Z","updated_at":"2026-03-13T01:02:16.344Z","avatar_url":"https://github.com/izzoa.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"logo.svg\" alt=\"PlexMix Logo\" width=\"300\"/\u003e\n\n  # PlexMix\n\n  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n  [![PyPI version](https://badge.fury.io/py/plexmix.svg)](https://badge.fury.io/py/plexmix)\n  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n\n  **AI-powered Plex playlist generator using mood-based queries**\n\u003c/div\u003e\n\nPlexMix 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.\n\n## Features\n\n- ✨ **Simple Setup** - Only requires a Google API key to get started\n- 🎵 **Smart Sync** - Syncs Plex music library with incremental updates\n- 🤖 **AI-Powered** - Uses Google Gemini, OpenAI GPT, Anthropic Claude, Cohere Command, or fully local Gemma/Mistral models\n- 🔌 **Bring-Your-Own LLM** - Point PlexMix at any OpenAI-compatible local endpoint (Ollama, LM Studio, llama.cpp)\n- 🏷️ **AI Tagging** - Automatically generates tags, environments, and instruments for tracks\n- 🎵 **Audio Analysis** - Extract tempo, key, energy, danceability via Essentia DSP (optional)\n- 🔍 **Semantic Search** - FAISS vector similarity search for intelligent track matching\n- 🎨 **Mood-Based** - Generate playlists from natural language descriptions\n- ⚡ **Fast** - Local database with optimized indexes and full-text search\n- 🎯 **Flexible** - Filter by genre, year, rating, artist, environment, instrument, tempo, energy, key, and danceability\n- 🐳 **Docker Ready** - Deploy with Docker Compose for containerized setups\n- 🛡️ **Resilient** - Automatic database recovery if deleted or corrupted\n\n## Quick Start\n\n### Option 1: Command Line Interface (Recommended)\n\n```bash\n# Install from PyPI\npip install plexmix\n\n# Run setup wizard\nplexmix config init\n\n# Sync your Plex library (incremental, generates embeddings automatically)\nplexmix sync\n\n# Generate AI tags for tracks (enhances search quality)\nplexmix tags generate\n\n# Create a playlist\nplexmix create \"upbeat morning energy\"\n\n# With filters\nplexmix create \"chill evening vibes\" --genre jazz --year-min 2010 --limit 30\n\n# Filter by environment and instrument\nplexmix create \"focus music\" --environment study --instrument piano\n\n# Filter by audio features (requires audio analysis)\nplexmix create \"dance party\" --tempo-min 120 --energy high --danceable 0.7\n\n# Use alternative AI provider\nplexmix create \"workout motivation\" --provider openai\n\n# If you encounter issues (e.g., \"0 candidate tracks\")\nplexmix doctor\n\n# Regenerate all tags and embeddings from scratch (WARNING: destructive)\nplexmix sync regenerate\n```\n\n### Option 2: Docker\n\nPre-built multi-platform images (amd64 + arm64) are published to GHCR on every release in two variants:\n\n| Image Tag | Size | Use Case |\n|-----------|------|----------|\n| `ghcr.io/izzoa/plexmix:latest` | ~450MB | Cloud AI providers (Gemini, OpenAI, Claude, Cohere) |\n| `ghcr.io/izzoa/plexmix:latest-local` | ~4GB+ | Fully offline with local embeddings and local LLM (includes PyTorch) |\n\n```bash\n# Pull the slim image (recommended for most users)\ndocker pull ghcr.io/izzoa/plexmix:latest\n\n# Or pull the local/offline image (includes sentence-transformers + PyTorch)\ndocker pull ghcr.io/izzoa/plexmix:latest-local\n\n# Run with Docker Compose (clone the repo for docker-compose.yml)\ngit clone https://github.com/izzoa/plexmix.git\ncd plexmix\ncp .env.example .env\n# Edit .env with your PLEX_TOKEN, GOOGLE_API_KEY, etc.\ndocker compose up -d\n\n# Access the web UI at http://localhost:3000\n\n# Run CLI commands via Docker\ndocker compose run plexmix sync\ndocker compose run plexmix create \"chill evening vibes\"\n```\n\n### Option 3: Web User Interface (Alpha)\n\n\u003e **Note:** The Web UI is currently in Alpha status. The CLI is the recommended way to interact with PlexMix for production use.\n\n```bash\n# Install with UI extras\npip install \"plexmix[ui]\"\n\n# Or if using poetry\npoetry install -E ui\n\n# Launch the web UI\nplexmix ui\n\n# Optional: Specify host and port\nplexmix ui --host 0.0.0.0 --port 8000\n```\n\nThen open your browser to `http://localhost:3000`\n\n**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.\n\n```bash\nPLEXMIX_UI_PASSWORD=mysecret plexmix ui --host 0.0.0.0\n```\n\n#### Screenshots\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/dashboard-light.png\" alt=\"Dashboard - Light Mode\" width=\"45%\"/\u003e\n  \u003cimg src=\"docs/screenshots/dashboard-dark.png\" alt=\"Dashboard - Dark Mode\" width=\"45%\"/\u003e\n  \u003cp\u003e\u003cem\u003eDashboard with configuration status and library statistics\u003c/em\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/generator.png\" alt=\"Playlist Generator\" width=\"90%\"/\u003e\n  \u003cp\u003e\u003cem\u003eAI-powered playlist generator with mood-based queries\u003c/em\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/library.png\" alt=\"Library Manager\" width=\"90%\"/\u003e\n  \u003cp\u003e\u003cem\u003eBrowse and manage your music library with advanced filtering\u003c/em\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/screenshots/settings.png\" alt=\"Settings\" width=\"90%\"/\u003e\n  \u003cp\u003e\u003cem\u003eConfigure Plex, AI providers, and embeddings\u003c/em\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n#### Web UI Features\n\nThe web interface provides a modern, intuitive way to interact with PlexMix:\n\n- **📊 Dashboard** - Overview of library stats, configuration status, and quick actions\n- **⚙️ Settings** - Configure Plex, AI providers, and embeddings with real-time validation\n- **📚 Library Manager** - Browse, search, and sync your music library with live progress tracking\n- **🎵 Playlist Generator** - Create mood-based playlists with advanced filters and instant preview\n- **🏷️ AI Tagging** - Batch generate tags for tracks with progress monitoring\n- **📜 Playlist History** - View, export, and manage all generated playlists\n\n#### Key UI Features\n\n- **🌓 Dark/Light Mode** - Toggle between themes with automatic logo switching\n- **Real-time Progress** - Live updates for sync, tagging, and generation operations\n- **Form Validation** - Instant feedback on configuration settings\n- **Loading States** - Skeleton screens and spinners for smooth UX\n- **Error Handling** - User-friendly error messages with recovery options\n- **Responsive Design** - Works on desktop and tablet devices\n\n## Installation\n\n### From PyPI (Recommended)\n\n```bash\npip install plexmix\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/izzoa/plexmix.git\ncd plexmix\npoetry install\n```\n\n### With Local Embeddings / Local LLM (Optional)\n\n```bash\n# Install sentence-transformers + PyTorch for offline embeddings and local LLM\npip install \"plexmix[local]\"\n\n# Or with Poetry\npoetry install -E local\n```\n\n### With Audio Analysis (Optional)\n\n```bash\n# Install with Essentia for audio feature extraction\npip install \"plexmix[audio]\"\n\n# Or with Poetry\npoetry install -E audio\n```\n\n### With Docker\n\n```bash\n# Pre-built image from GHCR\ndocker pull ghcr.io/izzoa/plexmix:latest\n\n# Or build locally\ngit clone https://github.com/izzoa/plexmix.git\ncd plexmix\ndocker compose up -d\n```\n\n## Configuration\n\nPlexMix 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.\n\n### Required\n\n- **Plex Server**: URL and authentication token\n- **Google API Key** (or **Gemini API Key**): For Gemini AI and embeddings ([Get one here](https://makersuite.google.com/app/apikey))\n\n### Optional Alternative Providers\n\n- **OpenAI API Key**: For GPT models and text-embedding-3-small\n- **Anthropic API Key**: For Claude models (AI only, no embeddings)\n- **Cohere API Key**: For Command R7B and Embed v4 models\n- **Local Embeddings**: sentence-transformers (free, offline, no API key needed)\n- **Local LLM**: Run Gemma 3 1B/4B, Liquid LFM 1.2B, Yarn-Mistral 7B-128K, or hook into an Ollama/LM Studio endpoint\n\n### Getting a Plex Token\n\n1. Open Plex Web App\n2. Play any media item\n3. Click the three dots (...) → Get Info\n4. View XML\n5. Copy the `X-Plex-Token` from the URL\n\n## Usage\n\n### Configuration Commands\n\n```bash\n# Interactive setup wizard\nplexmix config init\n\n# Test Plex server connection\nplexmix config test\n\n# Show current configuration\nplexmix config show\n```\n\n**Troubleshooting Connection Issues:**\n\nIf you get a \"400 Bad Request\" error when connecting to Plex:\n\n1. **Check your Plex token** - Make sure there are no extra spaces when copying\n2. **Try HTTPS** - Use `https://` instead of `http://` if your server requires secure connections\n3. **Verify the URL** - Ensure the server address and port (default: 32400) are correct\n4. **Check server settings** - In Plex Server settings, look for network/authentication requirements\n5. **Test the connection** - Run `plexmix config test` to diagnose the issue\n\n**Common Plex Server URLs:**\n- Local: `http://localhost:32400`\n- Remote: `http://192.168.1.X:32400` (replace X with your server's IP)\n- Secure: `https://your-server:32400`\n\n### Sync Commands\n\nPlexMix offers three sync modes:\n\n```bash\n# Incremental sync (default) - Only syncs new/changed/deleted tracks\nplexmix sync\n\n# Same as above, but explicit\nplexmix sync incremental\n\n# Sync with audio feature analysis (requires essentia)\nplexmix sync --audio\n\n# Regenerate everything from scratch (WARNING: Deletes ALL tags and embeddings)\nplexmix sync regenerate\n\n# Legacy alias for incremental sync\nplexmix sync full\n\n# Sync without embeddings (faster, but you'll need to generate them later)\nplexmix sync --no-embeddings\n```\n\n**Sync Mode Comparison:**\n\n| Mode | Tracks | Tags | Embeddings | Use Case |\n|------|--------|------|------------|----------|\n| `incremental` (default) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Regular updates, new tracks added |\n| `incremental --audio` | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Sync + audio feature analysis |\n| `full` (alias) | ✅ Syncs changes only | ✅ Preserves existing | ✅ Preserves existing | Same as incremental (kept for compatibility) |\n| `regenerate` | ✅ Syncs everything | ⚠️ **DELETES ALL** | ⚠️ **DELETES ALL** | Starting fresh, fixing corrupt data |\n\n**When to use each:**\n- **`plexmix sync`** → Default for daily use, adding new music\n- **`plexmix sync regenerate`** → When you want to completely regenerate all AI data (tags, embeddings)\n\n### Database Health Check\n\n```bash\n# Diagnose and fix database issues\nplexmix doctor\n\n# Force regenerate all tags and embeddings (DEPRECATED: use 'plexmix sync regenerate' instead)\nplexmix doctor --force\n```\n\n**What does `plexmix doctor` do?**\n- Detects orphaned embeddings (embeddings that reference deleted tracks)\n- Shows database health status (track count, embeddings, orphans)\n- Interactively removes orphaned data\n- Regenerates missing embeddings\n- Rebuilds vector index\n\n**When to use:**\n- After \"No tracks found matching criteria\" errors\n- When playlist generation finds 0 candidates\n- After database corruption or manual track deletion\n- Periodic maintenance to keep database healthy\n\n**Note:** For complete regeneration of all tags and embeddings, use `plexmix sync regenerate` instead of `doctor --force`\n\n### Database Management\n\n```bash\n# Show database information and statistics\nplexmix db info\n\n# Reset database and embeddings (with automatic backup)\nplexmix db reset\n\n# Reset without backup (not recommended)\nplexmix db reset --no-backup\n\n# Skip confirmation prompt\nplexmix db reset --force\n```\n\n**What gets deleted:**\n- SQLite database (`~/.plexmix/plexmix.db`)\n- FAISS embeddings index (`~/.plexmix/embeddings.index`)\n- All synced music metadata\n- User-applied tags (moods, environments, instruments)\n- Playlist history\n- AI-generated embeddings\n\n**What gets preserved:**\n- Your music files on Plex server (unchanged)\n- Plex server metadata (unchanged)\n- PlexMix configuration (`.env`, `config.yaml`)\n- API keys\n\n**When to use:**\n- Complete fresh start\n- Switching embedding providers\n- Database corruption that `doctor` can't fix\n- Testing or development\n\n**After reset:**\n1. Run `plexmix sync` to re-sync your library\n2. (Optional) Run `plexmix tags generate` to re-tag tracks\n\nBy default, a timestamped backup is created in `~/.plexmix/backups/` before deletion.\n\n**Database Command Reference:**\n\n| Command | Purpose | When to Use |\n|---------|---------|-------------|\n| `plexmix db info` | Show database stats | Check database health, view track/embedding counts |\n| `plexmix db reset` | Delete and reset database | Fresh start, switching providers, unfixable corruption |\n| `plexmix sync` | Incremental sync | Regular updates, new tracks |\n| `plexmix sync regenerate` | Regenerate all data | Regenerate tags/embeddings, fix data quality |\n| `plexmix doctor` | Fix orphaned data | After errors, periodic maintenance |\n\n### Tag Generation\n\n```bash\n# Generate AI tags for all untagged tracks\nplexmix tags generate\n\n# Use alternative AI provider\nplexmix tags generate --provider openai\n\n# Use the offline/local provider\nplexmix tags generate --provider local\n\n# Skip embedding regeneration (faster, but tags won't be in search)\nplexmix tags generate --no-regenerate-embeddings\n```\n\n### Embedding Generation\n\n```bash\n# Generate embeddings for tracks without them\nplexmix embeddings generate\n\n# Regenerate all embeddings from scratch\nplexmix embeddings generate --regenerate\n```\n\n**What are tags?**\nAI-generated metadata (per track) that enhances semantic search:\n- **Tags** (3-5): Mood descriptors like energetic, melancholic, upbeat, chill, intense\n- **Environments** (1-3): Best-fit contexts like work, study, focus, relax, party, workout, sleep, driving, social\n- **Instruments** (1-3): Most prominent instruments like piano, guitar, saxophone, drums, bass, synth, vocals, strings\n\nAll metadata is automatically included in embeddings for more accurate mood-based playlist generation.\n\n### Audio Feature Analysis (Optional)\n\nPlexMix can extract DSP-level audio features from your tracks using [Essentia](https://essentia.upf.edu/). This enriches embeddings and enables audio-aware playlist filters.\n\n```bash\n# Install with audio support\npip install \"plexmix[audio]\"\n\n# Analyze all tracks that don't have audio features yet\nplexmix audio analyze\n\n# Re-analyze all tracks (overwrites existing features)\nplexmix audio analyze --force\n\n# Show audio analysis statistics\nplexmix audio info\n\n# Analyze during sync\nplexmix sync --audio\n```\n\n**Extracted features:**\n- **Tempo** (BPM) with confidence score\n- **Key \u0026 Scale** (e.g., C major, A minor)\n- **Energy Level** (low / medium / high)\n- **Danceability** (0.0 - 1.0)\n- **Loudness** (dB)\n- **Spectral Centroid** and **Zero Crossing Rate**\n- **MFCCs** (13-coefficient timbral fingerprint)\n\nAudio features are automatically incorporated into embeddings for richer semantic search and can be used as playlist filters (see below).\n\n### Playlist Generation\n\n```bash\n# Basic playlist (prompts for track count)\nplexmix create \"happy upbeat summer vibes\"\n\n# Specify track count\nplexmix create \"rainy day melancholy\" --limit 25\n\n# Filter by genre\nplexmix create \"energetic workout\" --genre rock --limit 40\n\n# Filter by year range\nplexmix create \"90s nostalgia\" --year-min 1990 --year-max 1999\n\n# Filter by environment (work, study, focus, relax, party, workout, sleep, driving, social)\nplexmix create \"workout energy\" --environment workout\n\n# Filter by instrument (piano, guitar, saxophone, drums, etc.)\nplexmix create \"piano jazz\" --instrument piano\n\n# Use specific AI provider\nplexmix create \"chill study session\" --provider claude\n\n# Force the offline/local provider\nplexmix create \"ambient focus\" --provider local\n\n# Filter by audio features (requires audio analysis)\nplexmix create \"high energy dance\" --tempo-min 120 --tempo-max 140\nplexmix create \"chill lounge\" --energy low\nplexmix create \"piano ballads in C major\" --key C --instrument piano\nplexmix create \"dance floor bangers\" --danceable 0.7\n\n# Custom playlist name\nplexmix create \"morning coffee\" --name \"Perfect Morning Mix\"\n\n# Adjust candidate pool multiplier (default: 25x playlist length)\nplexmix create \"diverse mix\" --limit 20 --pool-multiplier 50\n\n# Don't create in Plex (save locally only)\nplexmix create \"test playlist\" --no-create-in-plex\n```\n\n## Architecture\n\nPlexMix uses a multi-stage pipeline for intelligent playlist generation:\n\n1. **AI Tagging** (One-time setup) → Tracks receive:\n   - 3-5 descriptive tags (mood, energy, tempo, emotion)\n   - 1-3 environments (work, study, focus, relax, party, workout, sleep, driving, social)\n   - 1-3 instruments (piano, guitar, saxophone, drums, bass, synth, vocals, strings, etc.)\n\n2. **Audio Analysis** (Optional) → Tracks receive DSP features:\n   - Tempo (BPM), key, scale, energy level, danceability, loudness, spectral features, MFCCs\n\n3. **Playlist Generation Pipeline**:\n   - **SQL Filters** → Apply optional filters (genre, year, rating, artist, environment, instrument, tempo, energy, key, danceability)\n   - **Candidate Pool** → Search using FAISS vector similarity (default: 25x playlist length)\n   - **Diversity Selection** → Apply algorithmic diversity rules:\n     - Max 3 tracks per artist\n     - Max 2 tracks per album\n     - No duplicate track/artist combinations\n   - **Final Playlist** → Return curated, diverse track list\n\n### Technology Stack\n\n- **Language**: Python 3.10+\n- **CLI**: Typer with Rich console output\n- **Database**: SQLite with FTS5 full-text search\n- **Vector Search**: FAISS (CPU) with cosine similarity\n- **AI Providers**: Google Gemini (default), OpenAI GPT, Anthropic Claude, Cohere, Local Gemma/Yarn presets or any OpenAI-compatible endpoint\n- **Embeddings**: Google Gemini (3072d), OpenAI (1536d), Cohere (1024d), Local (384-768d)\n- **Audio Analysis**: Essentia (optional) for DSP feature extraction\n- **Plex Integration**: PlexAPI\n- **Deployment**: Docker \u0026 Docker Compose support\n\n### Project Structure\n\n```\nplexmix/\n├── src/plexmix/\n│   ├── ai/               # AI provider implementations\n│   │   ├── base.py       # Abstract base class\n│   │   ├── gemini_provider.py\n│   │   ├── openai_provider.py\n│   │   ├── claude_provider.py\n│   │   ├── cohere_provider.py\n│   │   ├── local_provider.py   # Managed Hugging Face + custom endpoint support\n│   │   └── tag_generator.py  # AI-based tag generation\n│   ├── audio/            # Audio feature analysis (optional)\n│   │   └── analyzer.py   # Essentia-based DSP feature extraction\n│   ├── cli/              # Command-line interface\n│   │   └── main.py       # Typer CLI app\n│   ├── config/           # Configuration management\n│   │   ├── settings.py   # Pydantic settings\n│   │   └── credentials.py # Keyring + env var credential storage\n│   ├── database/         # Database layer\n│   │   ├── models.py     # Pydantic models\n│   │   ├── sqlite_manager.py # SQLite CRUD (incl. audio_features)\n│   │   └── vector_index.py   # FAISS index\n│   ├── plex/             # Plex integration\n│   │   ├── client.py     # PlexAPI wrapper (extracts file paths)\n│   │   └── sync.py       # Sync engine (audio-aware embeddings)\n│   ├── playlist/         # Playlist generation\n│   │   └── generator.py  # Core generation logic (audio filters)\n│   ├── ui/               # Web UI (Reflex)\n│   │   ├── app.py        # Main Reflex app\n│   │   ├── pages/        # UI pages\n│   │   ├── states/       # State management\n│   │   ├── components/   # Reusable components\n│   │   └── utils/        # UI utilities\n│   └── utils/            # Utilities\n│       ├── embeddings.py # Embedding providers (audio-enriched)\n│       └── logging.py    # Logging setup\n├── Dockerfile            # Container image definition\n├── docker-compose.yml    # Multi-service orchestration\n└── tests/                # Test suite\n    └── ui/               # UI tests\n```\n\n## Database Schema\n\nPlexMix stores all music metadata locally:\n\n- **artists**: Artist information\n- **albums**: Album details with artist relationships\n- **tracks**: Track metadata with full-text search, AI-generated tags (3-5), environments (1-3), and instruments (1-3)\n- **embeddings**: Vector embeddings for semantic search (includes all AI-generated metadata + audio features)\n- **audio_features**: DSP-extracted features per track (tempo, key, scale, energy, danceability, loudness, spectral centroid, MFCCs, zero crossing rate)\n- **playlists**: Generated playlist metadata\n- **sync_history**: Synchronization audit log\n\n## AI Provider Comparison\n\n| Provider | Model | Context Window | Default Temp | Speed | Quality | Cost | Best For |\n|----------|-------|----------------|--------------|-------|---------|------|----------|\n| OpenAI | gpt-5-mini | 400K tokens | 0.7 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | High-quality responses, reasoning |\n| Anthropic | claude-sonnet-4-5 | 200K tokens | 0.7 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰💰 High | Advanced reasoning, analysis |\n| Cohere | command-r-plus-08-2024 | 128K tokens | 0.3 | ⚡⚡ Moderate | ⭐⭐⭐⭐⭐ Outstanding | 💰💰 Medium | Multilingual, complex tasks |\n| **Google Gemini** ⭐ | gemini-2.5-flash | 1M tokens | 0.7 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | General use, RAG, large contexts |\n| OpenAI | gpt-5-nano | 400K tokens | 0.7 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Speed-optimized, efficient |\n| Cohere | command-r7b-12-2024 | 128K tokens | 0.3 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | RAG, tool use, agents |\n| Cohere | command-r-08-2024 | 128K tokens | 0.3 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Balanced performance |\n| Anthropic | claude-haiku-4-5 | 200K tokens | 0.7 | ⚡⚡⚡ Fast | ⭐⭐⭐⭐ Excellent | 💰 Low | Fast responses, efficiency |\n\n**Legend:**\n- ⭐ Default/recommended option\n- Speed: ⚡ Slow, ⚡⚡ Moderate, ⚡⚡⚡ Fast\n- Quality: ⭐ Basic → ⭐⭐⭐⭐⭐ Outstanding\n- Cost: 💰 Low, 💰💰 Medium, 💰💰💰 High\n\n## Embedding Provider Comparison\n\n| Provider | Model | Dimensions | Quality | Speed | Cost | API Key | Best For |\n|----------|-------|------------|---------|-------|------|---------|----------|\n| **Google Gemini** ⭐ | gemini-embedding-001 | 3072 | ⭐⭐⭐⭐⭐ Outstanding | ⚡⚡ Moderate | 💰 Low | Required | High-dimensional, accurate semantic search |\n| Local | mixedbread-ai/mxbai-embed-large-v1 | 1024 | ⭐⭐⭐⭐ Excellent | ⚡⚡ Moderate | 💰 Free | None | Highest-quality offline retrieval when you can store larger vectors |\n| Local | google/embeddinggemma-300m | 768 (Matryoshka) | ⭐⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Flexible local embeddings with truncation to 128/256/512d |\n| Cohere | embed-v4 | 256/512/1024/1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰 Low | Required | Flexible dimensions (Matryoshka), multimodal |\n| OpenAI | text-embedding-3-small | 1536 | ⭐⭐⭐⭐ Excellent | ⚡⚡⚡ Fast | 💰💰 Medium | Required | Balanced performance, OpenAI ecosystem |\n| Local | nomic-ai/nomic-embed-text-v1.5 | 768 (Matryoshka) | ⭐⭐⭐ Excellent | ⚡⚡ Fast | 💰 Free | None | Open-source local embeddings with Matryoshka support |\n| Local | sentence-transformers/all-MiniLM-L6-v2 | 384 | ⭐⭐⭐ Good | ⚡⚡⚡ Fast | 💰 Free | None | Offline use on modest hardware |\n\n**Key Features:**\n- **Gemini**: Highest dimensions (3072d) for maximum semantic precision\n- **OpenAI**: Industry standard, excellent ecosystem integration\n- **Cohere**: Configurable dimensions (256/512/1024/1536), supports images with v4\n- **Local**: Completely free, offline, private, no internet required, with multiple Hugging Face options (MiniLM, MXBAI, EmbeddingGemma, Nomic) to balance speed vs. recall\n\n\\* EmbeddingGemma and Nomic embeddings support Matryoshka truncation if you need smaller vectors (128/256/512d) without retraining.\n\n#### How the “Local” Provider Works\n\nWhen 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.\n\nSet `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.\n\n### Local LLM Presets \u0026 Custom Endpoints\n\nYou 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:\n\n- **Managed downloads** (same workflow as local embeddings) with curated Hugging Face repos:\n  - `google/gemma-3-1b` — fast, CPU-friendly drafts (8K context / ~768 new tokens)\n  - `liquid/lfm2-1.2b` — lightweight music-focused reasoning (32K context)\n  - `google/gemma-3-4b` — higher-quality 4B param model (32K context)\n  - `NousResearch/Yarn-Mistral-7b-128k` — 7B param 128K context for huge playlists (GPU recommended)\n- **Custom endpoints** that speak the OpenAI Chat Completions API (Ollama, LM Studio, llama.cpp server, OpenRouter running on your LAN, etc.)\n\nWhen you select \"Local (Offline)\" as the AI provider you can toggle between **Managed (Downloaded)** and **Custom Endpoint** modes:\n\n1. **Managed (Downloaded)**\n   - Click *Download / Warm Up Model* to prefetch weights into your Hugging Face cache\n   - Models are loaded in a background worker and reused across tagging/playlist runs\n   - Set `PLEXMIX_LOCAL_LLM_DEVICE` to `cpu`, `cuda`, or `mps` to force device placement (defaults to `auto`)\n\n2. **Custom Endpoint**\n   - Point PlexMix at any OpenAI-compatible URL (e.g., `http://localhost:11434/v1/chat/completions` for Ollama)\n   - Optionally provide a bearer token; PlexMix will include it as `Authorization: Bearer \u003ctoken\u003e`\n   - Responses must return a JSON payload with `choices[0].message.content`\n\nFrom the CLI you can force the local provider as well:\n\n```bash\n# Use the configured local model for tagging\nplexmix tags generate --provider local\n\n# Run the playlist doctor flow with your offline LLM\nplexmix doctor --force\n```\n\nIf you ever want to nuke cached weights, delete the relevant directories under `~/.cache/huggingface`.\n\n**Dimension Trade-offs:**\n- Higher dimensions = Better semantic understanding but larger storage\n- Lower dimensions = Faster search but slightly less accurate\n- Cohere's Matryoshka embeddings allow dynamic dimension selection\n\n## Optimal Setup\n\n### Online (Best Latency \u0026 Reasoning)\n\n- **AI Provider:** `gemini-2.5-flash` (default). For more advanced reasoning, upgrade to `gpt-5-mini` or `claude-sonnet-4-5` if you have the budget.\n- **Embeddings:** `gemini-embedding-001` for maximum semantic precision, or `text-embedding-3-small` if you want faster generation with a slightly smaller vector size.\n- **Network Tips:** Keep API keys in `~/.plexmix/credentials` and run `plexmix config init` to verify connectivity. Use `plexmix ui --reload` during development to check the status cards.\n\n### Hybrid (Cloud AI + Local Embeddings)\n\n- **AI Provider:** Keep using `gemini-2.5-flash` (or `gpt-5-mini`) for playlist prompts so you get the latest reasoning updates.\n- **Embeddings:** Run `mixedbread-ai/mxbai-embed-large-v1` locally so FAISS never leaves your machine while still benefiting from high-quality vectors.\n- **Workflow Tips:** Regenerate embeddings locally after every sync, but keep the AI provider online. This gives you the best of both worlds—fast semantic search without exposing track metadata, plus cloud-scale LLM quality.\n\n### Fully Local (Offline-Friendly)\n\n- **AI Provider:** `OpenRouter` (planned) or a self-hosted LLM (future). Until then, use Gemini with cached responses if you need to stay mostly offline.\n- **Embeddings:** `mixedbread-ai/mxbai-embed-large-v1` (1024d) for the best similarity recall while keeping everything on disk.\n- **Device:** Set `PLEXMIX_LOCAL_EMBEDDING_DEVICE=cpu` (or `cuda` if you have a local GPU) so sentence-transformers always uses the right hardware.\n- **Storage Tips:** Keep FAISS index on SSD (`~/.plexmix/embeddings.index`) and prune unused tracks to reduce RAM usage when generating playlists.\n\n## Docker Deployment\n\nPlexMix publishes multi-platform Docker images (amd64 + arm64) to GitHub Container Registry on every release in two variants:\n\n| Tag | Includes | Size | Best For |\n|-----|----------|------|----------|\n| `:latest` / `:0.5.2` | Cloud AI SDKs, Reflex UI, FAISS | ~450MB | Users with API keys (Gemini, OpenAI, Claude, Cohere) |\n| `:latest-local` / `:0.5.2-local` | Everything above + PyTorch, sentence-transformers | ~4GB+ | Fully offline: local embeddings + local LLM, no API keys needed |\n\n### Quick Start\n\n```bash\n# Option A: Slim image (cloud AI providers)\ndocker pull ghcr.io/izzoa/plexmix:latest\n\n# Option B: Local/offline image (includes PyTorch for local embeddings + LLM)\ndocker pull ghcr.io/izzoa/plexmix:latest-local\n\n# Use Docker Compose (clone repo for compose file)\ncp .env.example .env\n# Edit .env with your credentials (PLEX_TOKEN, GOOGLE_API_KEY, etc.)\ndocker compose up -d\n```\n\n### Sample `docker-compose.yml`\n\n```yaml\nservices:\n  plexmix:\n    image: ghcr.io/izzoa/plexmix:latest\n    ports:\n      - \"3000:3000\"   # Web UI\n      - \"8000:8000\"   # Reflex backend\n    volumes:\n      - plexmix-data:/data\n      # Mount your music library read-only for audio analysis\n      # - /path/to/music:/music:ro\n    environment:\n      - PLEX_URL=http://host.docker.internal:32400\n      - PLEX_TOKEN=${PLEX_TOKEN}\n      - GOOGLE_API_KEY=${GOOGLE_API_KEY:-}\n      # Optional alternative AI providers\n      # - OPENAI_API_KEY=${OPENAI_API_KEY:-}\n      # - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n      # - COHERE_API_KEY=${COHERE_API_KEY:-}\n      # Optional: protect the web UI with a password\n      # - PLEXMIX_UI_PASSWORD=changeme\n      # Audio path remapping (translate Plex paths → container paths)\n      # - AUDIO_PATH_PREFIX_FROM=/data/music\n      # - AUDIO_PATH_PREFIX_TO=/music\n    restart: unless-stopped\n\nvolumes:\n  plexmix-data:\n```\n\n### Environment Variables\n\nAll credentials can be passed as environment variables (no keyring required in containers):\n\n| Variable | Description |\n|----------|-------------|\n| `PLEX_URL` | Plex server URL (default: `http://host.docker.internal:32400`) |\n| `PLEX_TOKEN` | Plex authentication token |\n| `GOOGLE_API_KEY` / `GEMINI_API_KEY` | Google Gemini API key |\n| `OPENAI_API_KEY` | OpenAI API key (optional) |\n| `ANTHROPIC_API_KEY` | Anthropic API key (optional) |\n| `COHERE_API_KEY` | Cohere API key (optional) |\n| `PLEXMIX_DATA_DIR` | Data directory override (default: `/data` in Docker, `~/.plexmix` locally) |\n| `PLEXMIX_UI_HOST` | UI bind address (default: `0.0.0.0` in Docker, `127.0.0.1` locally) |\n| `PLEXMIX_UI_PASSWORD` | Optional password to protect the web UI |\n| `AUDIO_ENABLED` | Enable audio analysis (default: `false`) |\n| `AUDIO_ANALYZE_ON_SYNC` | Run audio analysis during sync (default: `false`) |\n| `AUDIO_PATH_PREFIX_FROM` | Plex file path prefix to replace (for path remapping) |\n| `AUDIO_PATH_PREFIX_TO` | Local file path prefix replacement (for path remapping) |\n\n### Running CLI Commands\n\n```bash\n# Sync library\ndocker compose run plexmix sync\n\n# Generate tags\ndocker compose run plexmix tags generate\n\n# Create a playlist\ndocker compose run plexmix create \"chill evening vibes\"\n\n# Check database health\ndocker compose run plexmix db info\n```\n\n### Audio Path Remapping\n\nPlex 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:\n\n```yaml\n# docker-compose.yml\nservices:\n  plexmix:\n    volumes:\n      - /path/to/music:/music:ro    # Mount your music library\n    environment:\n      - AUDIO_PATH_PREFIX_FROM=/data/music   # Plex's path prefix\n      - AUDIO_PATH_PREFIX_TO=/music          # Local path prefix\n```\n\nOr in `~/.plexmix/config.yaml`:\n\n```yaml\naudio:\n  enabled: true\n  path_prefix_from: /data/music\n  path_prefix_to: /music\n```\n\n### Data Persistence\n\nAll 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.\n\n## Development\n\n### Setup Development Environment\n\n```bash\n# Clone repository\ngit clone https://github.com/izzoa/plexmix.git\ncd plexmix\n\n# Install with development dependencies\npoetry install\n\n# Run tests\npoetry run pytest\n\n# Format code\npoetry run black src/\n\n# Lint\npoetry run ruff src/\n\n# Type check\npoetry run mypy src/\n```\n\n### Running Tests\n\n```bash\npoetry run pytest\npoetry run pytest --cov=plexmix --cov-report=html\n```\n\n## Troubleshooting\n\n### \"No music libraries found\"\n- Ensure your Plex server has a music library\n- Verify your Plex token is correct\n- Check server URL is accessible\n\n### \"Failed to generate embeddings\"\n- Verify API keys are configured correctly\n- Check internet connection\n- Try local embeddings: `--embedding-provider local`\n\n### \"No tracks found matching criteria\"\n- **First, try:** `plexmix doctor` to check for database issues\n- Ensure library is synced: `plexmix sync`\n- Check filters aren't too restrictive\n- Verify embeddings were generated\n\n### \"0 candidate tracks\" or \"No orphaned embeddings\"\n- This usually means embeddings reference old track IDs\n- **Solution:** Run `plexmix doctor` to detect and fix orphaned embeddings\n- The doctor will clean up orphaned data and regenerate embeddings\n\n### Performance Tips\n\n- Use local embeddings for faster offline operation\n- Run sync during off-peak hours for large libraries\n- Adjust candidate pool multiplier based on library size (default: 25x playlist length)\n  - Smaller libraries: Use lower multiplier (10-15x) for faster generation\n  - Larger libraries: Use higher multiplier (30-50x) for better diversity\n- Use filters to narrow search space\n\n## FAQ\n\n### How does PlexMix work?\n\nPlexMix 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.\n\n### Do I need an API key?\n\nYes, 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](https://makersuite.google.com/app/apikey). Alternative providers (OpenAI, Anthropic, local embeddings) are optional.\n\n### How much does it cost to run?\n\n**Google Gemini (default)**:\n- Embedding generation: ~$0.10-0.30 for 10,000 tracks (one-time)\n- Tag generation: ~$0.20-0.50 for 10,000 tracks (one-time)\n- Playlist creation: ~$0.01 per playlist (ongoing)\n\n**Alternatives**:\n- Local embeddings are completely free (no API key needed)\n- OpenAI and Anthropic have similar costs\n\n### How long does initial sync take?\n\n- **Metadata sync**: 5-15 minutes for 10,000 tracks\n- **Tag generation**: 30-60 minutes for 10,000 tracks\n- **Embedding generation**: 15-30 minutes for 10,000 tracks\n\nTotal: ~1-2 hours for a large library. You can interrupt and resume at any time.\n\n### Can I use this without internet?\n\nPartially. After initial sync and tag/embedding generation, you can:\n- ✅ Browse your database offline\n- ✅ Use local embeddings (install with `pip install \"plexmix[local]\"`)\n- ✅ Run audio analysis locally (with Essentia installed)\n- ✅ Generate playlists with local LLM (install with `pip install \"plexmix[local]\"`)\n- ✅ Generate tags with local LLM\n- ❌ Use cloud AI providers (requires API key + internet)\n\n### What's the difference between tags, environments, and instruments?\n\n- **Tags** (3-5): Mood and vibe descriptors like \"energetic\", \"melancholic\", \"upbeat\", \"chill\"\n- **Environments** (1-3): Best contexts for listening like \"work\", \"study\", \"workout\", \"party\"\n- **Instruments** (1-3): Most prominent instruments like \"piano\", \"guitar\", \"saxophone\", \"drums\"\n\nAll three are automatically generated by AI and improve playlist quality.\n\n### Why am I getting \"0 candidate tracks\"?\n\nThis usually means:\n1. **No embeddings generated**: Run `plexmix embeddings generate`\n2. **Database out of sync**: Run `plexmix doctor` to fix\n3. **Filters too restrictive**: Remove some filters and try again\n4. **Empty library**: Ensure `plexmix sync` completed successfully\n\n### Can I use multiple Plex libraries?\n\nNot yet. Currently PlexMix supports one music library at a time. Multi-library support is on the roadmap.\n\n### Does this modify my Plex server?\n\nOnly when creating playlists. PlexMix:\n- ✅ Reads metadata from Plex (read-only)\n- ✅ Creates playlists in Plex (if enabled with `--create-in-plex`)\n- ❌ Does NOT modify tracks, albums, or artists\n- ❌ Does NOT delete anything from Plex\n\n### What happens if I delete tracks from Plex?\n\nRun `plexmix sync` to update your local database. The incremental sync will:\n- Detect deleted tracks from Plex\n- Remove them from the database\n- Clean up orphaned embeddings\n- Update the vector index\n\nOr use `plexmix doctor` to clean up orphaned data.\n\n### Can I backup my database?\n\nYes! 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.\n\n### How do I update PlexMix?\n\n```bash\npip install --upgrade plexmix\n```\n\nAfter updating, run `plexmix sync --no-embeddings` to apply any database migrations.\n\n### Can I contribute?\n\nAbsolutely! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. We welcome bug reports, feature requests, and pull requests.\n\n## Roadmap\n\n- [x] Docker support\n- [x] Audio feature analysis integration (Essentia)\n- [ ] Multi-library support\n- [ ] Playlist templates\n- [ ] Smart shuffle and ordering\n- [ ] Export/import playlists (M3U, JSON)\n\n## Contributing\n\nContributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n1. Fork the repository\n2. Create a feature branch\n3. Make your changes\n4. Add tests\n5. Submit a pull request\n\n## License\n\nMIT License - see [LICENSE](LICENSE) for details\n\n## Acknowledgments\n\n- Built with [Typer](https://typer.tiangolo.com/) and [Rich](https://rich.readthedocs.io/)\n- Plex integration via [python-plexapi](https://github.com/pkkid/python-plexapi)\n- Vector search powered by [FAISS](https://github.com/facebookresearch/faiss)\n- Audio analysis powered by [Essentia](https://essentia.upf.edu/)\n- AI providers: Google, OpenAI, Anthropic, Cohere\n\n---\n\n**Made with ❤️ for music lovers**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizzoa%2Fplexmix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizzoa%2Fplexmix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizzoa%2Fplexmix/lists"}