Fixes#158 -- fresh docker-compose install shows login instead of setup
wizard because Next.js rewrites were compiled with 127.0.0.1:3006 baked
in. Added NEXT_PUBLIC_BACKEND_URL as a Dockerfile build arg, defaulting
to http://backend:3006 in docker-compose.yml so the frontend container
proxies to the backend service correctly.
Audiobookshelf sync fixes:
- syncAudiobook returns boolean; skipped books no longer count as synced
- Sync notification now surfaces failed/skipped counts
- downloadCover has 10s fetch timeout (was unbounded)
- book.size changed to book.media?.size for audio-only size
Added sync button on audiobooks page so users can trigger a sync without
going through settings or the enrichment system.
Bump to v1.7.2
BullMQ enrichment migration
- Rewrote enrichment pipeline on BullMQ v5: artist, track, podcast workers with
pause/resume/stop support and Bull Board visibility
- Essentia publishes audio:analysis:complete events; CLAP subscribes reactively
instead of polling (eliminates scan delay between phases)
- Thread-safe DB pool in CLAP worker, all DB calls in run_in_executor
- Fixed psycopg2.pool submodule import crash on BullMQ vibe worker startup
- Silenced EnrichmentStateService disconnect error on already-closed Redis conn
Enrichment fixes
- lastfmTags NULL caused mood-tags phase to silently skip all tracks; migration
backfills NULL -> '{}' and sets column default
- Cover art fetch errors for temp-MBID albums (temp-* passed to Cover Art Archive)
- VIBE-VOCAB vocabulary JSON not copied to Docker image (TypeScript omits .json)
- Wired cleanupOldResolved() to run daily; added missing enrichment status indexes
- Fixed circuit breaker reset, orphan cleanup, podcast entityType, hang detection
Soulseek
- Track search-page downloads in activity tracker
- Use async fs.promises.access instead of synchronous existsSync
- Verify file exists on disk before emitting download:complete (#110)
Docker image size: 28.4 GB -> 12.2 GB
- Removed all CUDA/NVIDIA dependencies; switched to CPU-only PyTorch/TensorFlow
- tensorflow-cpu + essentia-tensorflow --no-deps (avoids GPU TF transitive dep)
- Fixed .dockerignore: **/node_modules and **/.next now excluded from build context
PWA / mobile
- Background audio session loss on iOS/Android: SilenceKeepalive singleton,
tryResume() in MediaSession play handler, direct track load on 'ended' event,
visibilitychange/pageshow foreground recovery
- Lock orientation to portrait for Android device lock (#117)
Discovery
- Retry All re-importing albums already in library: apply same three-level filter
as GET /current before creating download jobs; delete stale UnavailableAlbum
records for already-present albums. Closes#34
CI / release
- linux/arm64 added to release and nightly Docker builds (#87)
- Isolated release CI from nightly GHA cache (cache-from/cache-to removed from
docker-publish.yml to guarantee clean release builds)
- Redis vm.overcommit_memory=1 sysctl added to prod and server compose files
Other fixes
- Cross-artist album fallback by title+year prevents library splitting (#50)
- Retry temp-MBID artists after 24h not 7 days; hide temp MBIDs from API (#112)
- 3-attempt ECONNRESET retry on all Deezer getPlaylist call sites (#119)
- check response.ok on health probe — fetch does not throw on 5xx (#104)
- Z-index stacking hierarchy established (MiniPlayer through OverlayPlayer)
- API token display overflow on iPhone (min-w-0/overflow-hidden on flex container)
Search:
- Full-text search with DB triggers for Artist/Album/Track searchVector
- Harden queryToTsquery for special chars, fix NaN LIMIT propagation
- Extract ILIKE fallback methods, normalize cache keys
- Parallelize discovery endpoint (Last.fm + iTunes concurrent)
- Remove double debounce in frontend search
- Separate similar artists endpoint (musically similar, not name-similar)
- Alias resolution banner ("Showing results for X, searched Y")
- Soulseek polling interval leak fix (useRef + AbortController)
- Typed API responses for discover and similar artists
- "/" keyboard shortcut to focus search bar
Streaming:
- Fix HTTP Range suffix parsing for Firefox/Safari (#84)
- Extract parseRangeHeader utility, use in audioStreaming + podcasts
- Clamp range end to file boundary per RFC 7233
Library:
- Server-side sorting for artists, albums, tracks endpoints
- Case-insensitive artist matching in playlists and discovery (#64)
Audio Analyzer:
- Replace 5s poll loop with Redis BRPOP (zero CPU while idle)
- Remove TensorFlow import from main process (~300MB RAM saved)
- Lazy worker pool creation (starts on first job, not at boot)
- Configurable MODEL_IDLE_TIMEOUT to unload models after idle period
- DB reconciliation on BRPOP timeout for missed queue items
- Lighter Dockerfile healthcheck (pgrep instead of TF import)
Cleanup:
- Delete dead useDebouncedValue hook, enrichment worker, mood bucket worker
- Move sort maps to module scope
- Bump version to 1.4.2
Thanks to @Allram for identifying and fixing the Range parsing issue (#84)
Closes#84, closes#64
Changes the opt-in model to opt-out:
- Analyzers now run by default with `docker compose up -d`
- Users who want lite mode copy docker-compose.override.yml.lite
to docker-compose.override.yml (saves ~3-4GB RAM)
This is more user-friendly for an open source project where
most users want the full experience out of the box.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Profiles:
- none: Minimal, metadata only (~2GB RAM)
- analysis: MusicCNN for BPM/key/mood (~5GB RAM)
- vibe: Both analyzers for similarity search (~7GB RAM)
- full: Alias for vibe
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add shared secret authentication to the /api/analysis/vibe/failure
endpoint to prevent external abuse. The CLAP analyzer now sends
X-Internal-Secret header which the backend validates against
INTERNAL_API_SECRET environment variable.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Apply chunking universally, not just for large files
- 60s from the middle captures the vibe while avoiding intros/outros
- Reduces memory requirement from 6GB to 3GB
- Faster processing for all tracks regardless of length
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add chunked audio loading for files >50MB - extracts middle 60 seconds
instead of loading entire file into memory
- Use get_audio_embedding_from_data for pre-loaded audio control
- Increase CLAP container memory limit from 4GB to 6GB
- Add librosa for audio loading with offset/duration support
Large lossless FLAC files (e.g., 97MB) now process successfully
without causing container OOM crashes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use CachedImage (memoized) instead of raw Image in library grids
- Add GPU acceleration hints (translateZ) to card containers
- Remove expensive shadow-lg from library card image containers
- Add hover scale transform to match homepage cards
- Fix pagination scroll to use correct scroll container (#main-content)
- Scroll after page change, not on click
- Remove standalone /artists and /albums pages (use /library tabs)
- Add NEXT_PUBLIC_BUILD_TYPE env var for nightly vs release builds
- Bump version to 1.3.6
Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed regression in Soulseek search where general queries returned "No tracks found" due to trailing spaces.
- Fixed ranking logic where empty track titles incorrectly boosted scores for all results.
- Fixed "Skip Track" fallback setting not working (Fixes#68).
- Fixed login "Internal Server Error" on NAS devices (Fixes#75).
- Fixed Wikimedia Commons image proxy 429 errors by updating User-Agent.
- Added selective enrichment controls for Artists, Mood Tags, and Audio Analysis.