Skip to content

Releases: izzoa/plexmix

v0.11.0 - Web UI Redesign & Cancellable Jobs

01 Jun 19:39

Choose a tag to compare

✨ New Features

Web UI Redesign

  • A slim glass icon-rail and a ⌘K command palette — search pages, run actions, fire a quick vibe, and g+key navigation.
  • The Generator is reimagined as an "AI-thinking" showpiece: a live particle vector-field, a four-phase progress tracker bound to the real generation pipeline, a streaming terminal log, and a results playlist with removable tracks.
  • New Settings → Appearance: theme toggle plus Density (comfortable/compact) and Accent-intensity (subtle/balanced/vivid), persisted across reloads.

Cancellable Long-Running Jobs

  • Cancel any running job from the UI — library sync, embedding generation, MusicBrainz enrichment, audio analysis, Doctor fixes, and tagging — each behind a confirmation, with the work actually stopping and a cancelled outcome shown.
  • New global "Cancel running task" command (⌘K) that stops everything in flight.

🔧 Changed

  • The web UI now defaults to light mode (previously dark). Your choice is remembered and can be toggled from the rail, the top bar, or Settings → Appearance.
  • Refreshed default AI models to a balanced, cost-aware tier: Gemini gemini-3.5-flash, OpenAI gpt-5.4-mini, Claude claude-sonnet-4-6, Cohere command-a-03-2025. Flagships (gpt-5.5, claude-opus-4-8, gemini-3.1-pro-preview, command-a-plus-05-2026) remain selectable. Embeddings are unchanged — existing FAISS indexes do not need regenerating.
  • The temperature setting now has no effect for OpenAI GPT-5, Gemini 3, and Claude Opus 4.7+ (those families reject a custom temperature); it still applies to Claude Sonnet/Haiku, Cohere, custom, and local providers.

🐛 Fixed

  • OpenAI GPT-5 requests now use max_completion_tokens and omit temperature — OpenAI playlist generation and the Settings "Test OpenAI" button no longer fail with a 400.
  • Gemini 3 requests constrain thinking (thinking_level=low) and use the model's default temperature.
  • Claude Opus 4.7+/4.8 requests omit sampling parameters (temperature/top_p/top_k) those models reject.

🗑️ Removed

  • End-of-life gemini-2.0-flash-001 (Google shutdown 2026-06-01) and the invalid claude-opus-4-1-20250414 model ID.

📦 Install / Upgrade

pip install --upgrade plexmix
# Docker
docker pull ghcr.io/izzoa/plexmix:0.11.0

Breaking Changes: None.

✅ CI verified locally: Black, Ruff, and Mypy clean; 704 passed, 4 skipped (coverage 47.7%).

Full Changelog: v0.10.0...v0.11.0

v0.10.0 - Hardened AI Provider Error Handling

29 May 00:23

Choose a tag to compare

Highlights

A reliability release that fixes a costly failure mode in AI tagging and prevents its most common root cause.

Previously, a single unrecoverable error (e.g. a malformed API key) during AI tag generation repeated across every batch — on a large library that meant thousands of doomed API calls, empty tags written library-wide, and an unreadable wall of raw error output. Now the run stops immediately with a clear, actionable message, and keys are sanitized and validated before use.

Added

  • API key sanitization & validation across all sources (system keyring, environment variables, and saved settings): surrounding whitespace and newlines are stripped automatically, and a key containing characters that cannot be sent over HTTP fails fast with a clear "re-enter your key in Settings" message instead of a cryptic transport error mid-run.

Fixed

  • Fail-fast on unrecoverable provider errors: AI tag generation now stops immediately on a fatal error (invalid API key, or an HTTP-edge/proxy rejection) instead of retrying every batch and writing empty tags across the entire library. Tags generated before the failure are preserved.
  • Human-readable provider errors: opaque failures — including the HTML Error 400 (Bad Request) page returned by an API gateway or proxy — are translated into a single actionable message pointing at the API key or network path.

Improvements (technical)

  • New shared FatalProviderError + classify_provider_error() helper (src/plexmix/ai/errors.py) with an explicit retryable/fatal mapping:
    • Retryable: connection/timeout, 408, 429, 5xx
    • Fatal: 400, 401, 403, 404, 422 (and any other 4xx)
  • Fail-fast handling wired into all five tag-generation entry points: sync pipeline, plexmix tags generate, and the tagging/doctor UI states.

Testing

  • 41 new tests (credential sanitization, provider error classification, sync fail-fast).
  • Full suite: 677 passing, coverage 47%.

Breaking Changes: None

v0.9.0 - MusicBrainz Integration & Test Coverage Push

16 Mar 18:20

Choose a tag to compare

New Features

MusicBrainz Metadata Enrichment

  • Community-curated genres: Enrich tracks with MusicBrainz genre tags (shoegaze, dream pop, post-punk, etc.) for more accurate semantic search and playlist generation
  • Canonical artist IDs (MBIDs): Resolve artist name variants (e.g., "The Beatles" vs "Beatles") for better playlist deduplication
  • Recording type detection: Identify live recordings, remixes, covers, and studio tracks
  • SQLite cache: 90-day TTL cache avoids redundant API calls; transient errors are never cached

New CLI Commands

```bash
plexmix musicbrainz enrich [--force] # Enrich tracks with MusicBrainz metadata
plexmix musicbrainz info # Show enrichment stats
plexmix musicbrainz clear-cache # Clear expired cache entries
plexmix sync --musicbrainz # Enrich during sync
```

Web UI Enhancements

  • MusicBrainz settings tab: Enable/disable, confidence threshold slider, contact email
  • Library page: "Enrich with MusicBrainz" button with progress tracking
  • Doctor page: Detects missing MusicBrainz enrichment with one-click fix
  • Doctor page: "Regenerate All" tags button with confirmation modal
  • Dashboard: MusicBrainz enriched tracks stat tile
  • Last sync time: Human-readable 24hr format (e.g., "Mar 14, 2026 16:50")

License

  • Changed from MIT to AGPL-3.0-only

Integration Points

MusicBrainz genres and recording types are woven into the existing pipeline:

  • Embedding text: MB genres appended for better semantic similarity search
  • AI tag prompts: MB genres included for improved tag generation accuracy
  • Playlist generation: Artist MBIDs used for diversity deduplication (handles name variants)

Bug Fixes

  • Changelog not loading in Docker (`.dockerignore` fix)
  • MusicBrainz transient API errors no longer cached as definitive no-match results
  • SQLite thread-affinity fixed in MusicBrainz enrichment (DB created inside executor thread)
  • MusicBrainz enrichment properly gated on `enabled` setting in sync pipeline and UI
  • Free disk space in Docker CI to prevent build failures

Test Coverage

639 tests total (+119 new), 47% coverage

  • 32 embedding pipeline tests (provider generation, dimension mismatch, end-to-end search, tagging service integration)
  • 29 sync error recovery tests (network timeouts, corrupt metadata, embedding/tag failures, audio analysis integration)
  • 29 MusicBrainz tests (client, service, cache, database)

Docker Environment Variables

Variable Description
`MUSICBRAINZ_ENRICH_ON_SYNC` Enable MusicBrainz enrichment during sync (`true`/`false`)
`MUSICBRAINZ_CONTACT_EMAIL` Contact email (required by MusicBrainz TOS)

v0.8.5 - Bug fixes and changelog modal

16 Mar 13:06

Choose a tag to compare

Fixed

  • Tagging Edit button: Fix KeyError: 'id' when clicking Edit on the tagging page — passes individual field values instead of the whole dict to avoid Reflex event serialization issues
  • Table row hover text: Fix unreadable text when hovering over song rows in library and tagging pages — adds explicit color: var(--pm-gray-12) with proper CSS specificity so text adapts to the hover background in both light and dark modes

Added

  • Changelog modal: Version badge in the sidebar is now centered and clickable — opens a scrollable dialog showing the full release history rendered from CHANGELOG.md via rx.markdown

v0.8.4 - Docker latest tag fix

16 Mar 12:48

Choose a tag to compare

Fixed

  • Docker :latest tag now correctly points to the slim (default) image instead of the -local image
  • Reordered CI workflow jobs so the slim image is pushed last — GHCR shows the most recently pushed manifest as the default package

Details

No application code changes. This is a CI-only fix that ensures docker pull ghcr.io/izzoa/plexmix:latest pulls the slim image (without local ML models) as intended.

Users who want the heavier image with local models should use :latest-local or a versioned -local tag (e.g., :0.8.4-local).

v0.8.3 - Fix Frontend Freezing During Background Tasks

16 Mar 12:35

Choose a tag to compare

Bug Fixes

  • Frontend freezing during heavy tasks — Sync, tag generation, and embedding generation were running blocking API calls and time.sleep() directly on the asyncio event loop, starving WebSocket heartbeats and causing "Cannot connect to server: timeout" errors
  • Wrapped all blocking work in run_in_executor:
    • start_sync — Plex sync engine operations
    • generate_embeddings — Embedding API calls (library page)
    • start_tagging — AI tag generation + DB saves (tagging page)
    • regenerate_missing_tags — AI tag generation (doctor page)
    • _generate_embeddings_for_tracks — Embedding API calls + FAISS index building (doctor page)
  • Replaced deprecated asyncio.get_event_loop() with asyncio.get_running_loop()

Note: Audio analysis and playlist generation already used run_in_executor correctly — this brings the remaining handlers up to the same standard.

Testing

549 tests passing

v0.8.2 - Dashboard Hotfix

15 Mar 17:51

Choose a tag to compare

Fix

  • Dashboard SyntaxError — Fix on_mount keyword argument placed before positional child components in dashboard.py, which prevented the dashboard page from loading

This is a hotfix for v0.8.1. Users on v0.8.1 (Docker or PyPI) should upgrade.

v0.8.1 - Dashboard Fix & Version Display

15 Mar 17:40

Choose a tag to compare

Bug Fixes

  • Dashboard 0/0/0/0 on fresh load — Added on_mount fallback so stats and config status load reliably after the index page's client-side redirect
  • Last Sync showing "Never" — Now uses sync_history table instead of MAX(last_played), which was always NULL for users who sync but don't play tracks through Plex
  • CI fixes — Resolved mypy errors for optional dependency imports (reflex, torch, transformers, sentence-transformers) and Ruff lint warnings

New Features

  • Version number in sidebar — App version (v0.8.1) now displayed in the lower-left of the desktop sidebar and mobile slide-out menu

Testing

549 tests passing

v0.8.0 - Persistent TaskStore & Service Layer

15 Mar 14:42

Choose a tag to compare

New Features

Persistent TaskStore Architecture

  • Background tasks (sync, tagging, audio analysis, embeddings, doctor fixes) now survive browser disconnects — close your browser, reopen it, and progress resumes seamlessly
  • Client-pull polling via hidden DOM buttons replaces server-push progress updates, eliminating all "disconnected client" warnings
  • TaskEntry dataclass tracks status (running/completed/failed/cancelled), progress percentage, messages, and extensible metadata
  • Global exclusivity per job type prevents concurrent DB-contending operations (e.g., two syncs can't run simultaneously)
  • Session recovery: poll_task_progress() on page load automatically reconnects to any in-progress tasks

Multi-User Forward Compatibility

  • TaskStore keyed by (user_id, job_type) with "default" for single-user mode
  • Ready for future multi-user scenarios without architectural changes

Infrastructure Improvements

Shared Service Layer

  • New service modules (sync_service.py, playlist_service.py, tagging_service.py, audio_service.py) eliminate duplicated provider/connection setup across CLI and UI
  • Centralized constants module (config/constants.py) for batch sizes, pagination, retry defaults, and diversity constraints

Code Splitting

  • Split SettingsState (1,068 → 638 lines) into core state + _settings_testing.py + _settings_downloads.py
  • Split settings.py page (1,045 → 364 lines) into core page + _settings_sections.py
  • Extracted reusable form field components (form_field, year_range_field, help_text)

CLI Refactoring

  • CLI commands (sync, create, tags, embeddings, audio) refactored to use shared service layer

Testing

  • 29 new tests for TaskStore and JobManager lifecycle, progress, cancel/pause, and exclusivity
  • 549 tests total, all passing

Files Changed

  • 93 files changed, 12,821 insertions, 5,540 deletions
  • 33 new files (service layer, CLI modules, components, tests)

Breaking Changes

None

v0.6.7 - Parallel Audio Analysis

13 Mar 18:41

Choose a tag to compare

New Features

Parallel Audio Analysis

Audio analysis now processes multiple tracks concurrently using a thread pool with a sliding-window pattern. This provides a significant speedup on multi-core systems.

Configuration:

  • AUDIO_WORKERS=4 (env var) or audio.workers: 4 in config.yaml
  • Default: 4 workers
  • Set to 1 to restore sequential behavior

Applied everywhere

Parallel analysis is used in all three flows:

  • Library page — "Analyze Audio" button
  • Sync-time — automatic analysis when AUDIO_ANALYZE_ON_SYNC=true
  • Doctor page — "Analyze" maintenance action

Pause, resume, stop, and ETA all work correctly with concurrent workers.