- itunes.py: zero-auth fallback for album artwork when MusicBrainz/Cover Art
Archive has no match — kicks in automatically during music enrichment,
toggleable in Settings (on by default, no key required).
- acoustid.py: audio fingerprint identification (Chromaprint fpcalc + the
AcoustID API) for tracks with missing/unknown artist or album — nothing
to text-search on, so this identifies from the actual audio content
instead. Requires a free user-supplied API key from acoustid.org/api-key,
off by default. New /music/identify endpoint + admin trigger button.
- Dockerfile: added libchromaprint-tools for fpcalc.
- Settings page: new Music Metadata & Artwork section with both toggles.
- ffmpeg progress streamed via -progress pipe:1, persisted on the job as a percent
- retry/retry-all now mark the old row as superseded instead of deleting it,
preserving the failed/cancelled audit trail
- frontend shows the running job in its own panel above the list with a live
progress bar instead of burying it as just another row
Admin can now Approve a pending request as Movie or TV, which looks up
the title via Radarr/Sonarr's own lookup API, adds the best match with
search-on-add enabled, and tracks the resulting radarr_id/sonarr_id on
the request. A live status line (searching/queued/downloading/downloaded,
polled every 15s) shows next to each approved request.
- New admin Library Scan page walks the NAS mounts directly for movies,
TV, and music not already in Radarr/Sonarr/local storage, and imports
them with storage_type=scan (fully integrated: streamable, transcodable,
listed in Admin) — Radarr/Sonarr import flows are untouched.
- New Music section (nav tab, album-grid browse, per-album track list,
persistent bottom player with queue/seek) backed by a Track model and
range-request audio streaming endpoint.
- MusicBrainz + Cover Art Archive integration (free, no API key) to fill
in album art for scanned tracks, paced to their 1 req/sec rate limit.
- Mounted the NAS music share into CT104 (new mp2 LXC mountpoint,
CIFS read-only) alongside the existing video mount.
- Optional Bluetooth/output-device picker on the player bar via the Audio
Output Devices API (setSinkId) for already-paired speakers — Chrome/Edge
only, since browsers can't pair new BT devices directly.
- Removed the "Who's Watching" sub-profile picker entirely: ProfileProvider now
auto-resolves the single profile tied to the account immediately after login
- Backend: removed POST/DELETE /profiles (no more creating/deleting extra
profiles — lifecycle is entirely driven by user create/delete, already wired
in the admin Users CRUD); kept GET (internal) and PATCH (edit your own
profile's avatar/kids-mode/rating cap)
- Deleted ProfileSelect page and the now-unused ProtectedRoute component
- Navbar: removed the "switch profile" action, account avatar is now just a
static display
- api.js: dropped the now-dead X-Profile-Id header logic (backend already
falls back to the account's single profile when the header is absent)
- New /admin/users page: create/delete users, toggle admin role, guarded against
self-demotion/self-deletion
- Backend: GET/POST/PATCH/DELETE /api/admin/users, admin-only
- Settings moved from /admin/settings to /settings (all logged-in users); the
password-change section shows for everyone, integration/API-key sections only
render (and only fetch) for admins, since those endpoints stay admin-gated
- Navbar: Settings link now visible to all users; added Users nav link for admins