Commit graph

437 commits

Author SHA1 Message Date
Broque Thomas
81617b06aa Reset watchlist scan timestamps on clear/period change
When clearing the wishlist or changing the discovery lookback period, reset watchlist_artists.last_scan_timestamp to NULL so subsequent scans can re-discover older releases that were previously filtered by an earlier scan timestamp. clear_wishlist now deletes wishlist_tracks, updates last_scan_timestamp for all watchlist artists, and logs the number of tracks cleared and artists reset. set_discovery_lookback_period also resets last_scan_timestamp and reports how many artists were reset. Minor whitespace cleanups in watchlist_scanner and web_server included.
2026-02-23 16:47:04 -08:00
Broque Thomas
7eee2be38c Add release_date to Track and UI
Add a release_date field to the Track dataclass for both iTunes and Spotify clients (iTunes: parsed from releaseDate, Spotify: from album.release_date). Propagate release_date into enhanced search results in web_server and into the client-side script so album objects include release_date when available. Also broaden playlistId matching in the missing-tracks process to include 'enhanced_search_track_'. Removed SQLite SHM/WAL files from the repo (cleanup of DB temporary files). These changes enable showing and using track release dates across the app.
2026-02-23 16:25:23 -08:00
Broque Thomas
f1fe72ceb2 Add track selection UI and backend mapping
Add per-track selection checkboxes, select-all control and a selection count to download modals (missing/YouTube/Tidal/artist-album). Implement JS helpers (toggleAllTrackSelections, updateTrackSelectionCount) to manage checkbox state, row dimming, button disabling, and to filter/stamp selected tracks with _original_index before sending to the backend. Update start/add-to-wishlist flows to use only selectedTracks and disable controls once analysis starts. Backend _run_full_missing_tracks_process now reads _original_index to preserve original table indices in analysis results. CSS updates (mobile.css and style.css) add styling for checkbox columns, responsive hiding logic for headers/columns, selection visuals (.track-deselected), and small layout/width tweaks.
2026-02-23 15:54:58 -08:00
Broque Thomas
2ba48f917d Add server-side M3U export and UI setting
Introduce M3U export feature with UI control and server-side saving. Adds a new m3u_export config option (enabled flag) and a checkbox in the settings UI. The web endpoint /api/save-playlist-m3u now checks the m3u_export setting (unless force=true), builds the target folder using a new _compute_m3u_folder() helper (leveraging existing template logic with sensible fallbacks), sanitizes filenames, and writes .m3u files into the computed folder. Frontend JS loads/saves the new setting, supplies album/artist metadata when auto-saving, and both autoSave and manual export now POST M3U data to the server (manual export uses force=true). Also changed browser download filename extension to .m3u and added minor logging/response behavior.
2026-02-23 11:59:16 -08:00
Broque Thomas
666e65ed3b Add repair scanning, tracklist fallbacks, UI polling
Introduce deferred album repair scanning and robust tracklist resolution plus UI/presentation tweaks.

- core/repair_worker.py: Add lazy MusicBrainz and AudioDB client accessors, per-batch folder queues, and register_folder/process_batch to defer folder scans until a batch completes. Implement cascading tracklist resolution (_resolve_album_tracklist) using Spotify/iTunes IDs, Spotify track→album lookup, album search, MusicBrainz release lookup, and AudioDB→MusicBrainz fallback. Add helpers to read Spotify track IDs, MusicBrainz album IDs, album/artist tags from files, MB/AudioDB fetchers, placeholder-ID filtering, and rename associated .lrc files when renaming audio files. Cache/locking and background-threaded scanning included. Improves resilience to missing/placeholder IDs and avoids circular imports.

- web_server.py: Register album folders for repair in post-processing and trigger repair_worker.process_batch when batches complete (multiple completion paths) so scans run after downloads finish.

- webui/static/script.js: Reduce unnecessary background work by skipping many fetches when the tab is hidden, and refresh dashboard-specific data on visibility change to ensure UI updates after OAuth or tab switch.

- webui/static/style.css: Replace glassmorphic backdrop-filters with opaque dark gradients for sidebar and main content to improve GPU rendering and visual consistency.

Overall: Adds reliable post-download album repair scanning using multiple metadata sources and reduces unnecessary client polling and heavy CSS effects for better performance and robustness.
2026-02-23 10:18:31 -08:00
Broque Thomas
129f69fce9 Add Blasphemy Mode to delete FLAC after MP3
Introduce an optional "Blasphemy Mode" that deletes the original FLAC after a verified MP3 copy is created.

- config: add lossy_copy.delete_original (default: false).
- webui/index.html & static script: add checkbox and warning in settings UI and persist the setting.
- web_server.py: make _create_lossy_copy return the MP3 path when it deletes the FLAC (otherwise None); validate the MP3 using mutagen before removing the FLAC; rename associated .lrc files if present; update post-processing to use the final processed path in logs and wishlist checks and to consider .mp3 variants when FLAC may have been removed.

Behavior is off by default and includes safety checks and logging to avoid accidental deletion of originals.
2026-02-22 23:23:52 -08:00
Broque Thomas
24bfc2462d Add Spotify & iTunes workers; update repair worker
Add full-featured SpotifyWorker and iTunesWorker background workers to enrich artists, albums, and tracks with external metadata using batch cascading searches, fuzzy name matching, ID validation, and DB backfills. Update RepairWorker to re-read the transfer path from the database each scan, resolve host paths when running in Docker, and trigger immediate rescans when the transfer path changes; remove the static config_manager dependency. Also include supporting changes to the database layer and web UI/server (stats, controls, and styles) to integrate the new workers and reflect updated worker status.
2026-02-22 22:29:10 -08:00
Broque Thomas
2aa529f8e4 Use new Spotify /items endpoint with fallback
Add _get_playlist_items_page to call the new playlists/{id}/items endpoint (Feb 2026 API migration) and fall back to spotipy.playlist_items (old /tracks) on 403/404. Update _get_playlist_tracks and web_server.get_playlist_tracks to use the new helper to avoid 403 errors for Development Mode apps while preserving compatibility with Extended Quota Mode.
2026-02-22 19:15:11 -08:00
Broque Thomas
58df2ba33c Scope fuzzy key match to username and add lock
Tighten fuzzy matching in post-processing when no exact context is found: acquire matched_context_lock for thread-safe access and limit candidate keys to those starting with the current username prefix ("{task_username}::") while still matching the file basename. This prevents cross-album/username metadata contamination during mass downloads (e.g., multiple albums with identical track basenames) and reduces erroneous fuzzy matches.
2026-02-22 16:21:28 -08:00
Broque Thomas
eedd21f9aa Fallback MusicBrainz IDs from AudioDB
Use MusicBrainz IDs returned by AudioDB as fallbacks when MB lookup is missing. The patch maps strMusicBrainzID/strMusicBrainzAlbumID/strMusicBrainzArtistID to MUSICBRAINZ_RECORDING_ID, MUSICBRAINZ_RELEASE_ID, and MUSICBRAINZ_ARTIST_ID respectively (only if those tags are not already present), sets recording_mbid and artist_mbid where applicable, and adds informational prints for debugging.
2026-02-21 18:02:09 -08:00
Broque Thomas
8ac00f124a Update web_server.py 2026-02-21 17:50:18 -08:00
Broque Thomas
b0fdf6b220 Embed MusicBrainz release MBID in audio tags
Request recording 'releases' from MusicBrainz and, when available, capture the first release MBID into id_tags['MUSICBRAINZ_RELEASE_ID']. Persist this release (album) MBID across tag formats by adding TXXX ('MusicBrainz Album Id'), a generic tag 'MUSICBRAINZ_ALBUMID', and an iTunes MP4 freeform key. This allows tracks to be associated with their MusicBrainz release/album.
2026-02-21 17:19:59 -08:00
Broque Thomas
aa59ab7cd0 Verify using actual processed path and handle stream moves
Stop recomputing expected file paths during verification and instead use the exact path computed/moved during post-processing (context['_final_processed_path']). If that context value is missing, assume success to avoid false failures and mark the task completed.

Also store the computed final path into context in _post_process_matched_download so verification uses the exact same path. Add logic to handle cases where the source vanished but a variant (quality-tagged) file exists in the destination — treat that as success, set the final path, download art and generate LRC.

In the post-processing worker: skip verification when the task is already completed or marked as stream_processed, check for stream_processed status while searching, increase file search retries from 3 to 5 with longer sleeps, and re-check stream_processed before marking a task failed. Improve log messages and error text to reduce false negatives caused by independent recomputation or concurrent stream processing.
2026-02-21 17:11:20 -08:00
Broque Thomas
c7e3169b82 Recover uncaptured failed tracks for wishlist
Add a recovery step during wishlist processing that scans in-memory download_tasks (under tasks_lock) for tasks marked 'failed' or 'not_found' but not present in permanently_failed_tracks, and appends normalized track entries (including retry_count, failure_reason, spotify_track, and cached candidates). This prevents tasks that were force-marked failed by stuck-detection logic from being silently skipped in wishlist sync. Also fix post-processing context lookup by rebuilding the context key with _make_context_key(task_username, task_filename) so it matches how context keys are stored.
2026-02-21 15:52:39 -08:00
Broque Thomas
ce474749d5 Add library repair worker and UI
Introduce a RepairWorker to scan the transfer folder and automatically detect/repair broken album track numbers (e.g. the "all tracks = 01" bug). The worker uses mutagen to read/write tags, fuzzy-matches titles against an album tracklist (Spotify/iTunes via a SpotifyClient), updates filenames and the tracks DB file_path when renamed, and caches album tracklists. It also adds DB schema support (repair_status, repair_last_checked, and an index).

Integrates the worker into the web server: initializes and starts the worker, and exposes /api/repair/status, /api/repair/pause and /api/repair/resume endpoints. Adds UI elements (button, tooltip), client-side JS to poll and control the worker, CSS for visuals/animations, and a new image asset (whisoul.png).
2026-02-21 15:00:23 -08:00
Broque Thomas
49a6c58ea8 Add Hydrabase P2P mirror worker
Introduce a Hydrabase P2P mirror worker and integrate it into the web UI and server flows. Adds core/hydrabase_worker.py: a background thread with a capped queue (1000), enqueue API, rate limiting, basic stats (sent/dropped/errors), and logic to send JSON requests over a provided WebSocket (responses received and discarded). Integrates the worker into web_server.py (import, startup init, status/pause/resume endpoints, and enqueues queries from multiple search endpoints when dev mode is enabled). Adds UI elements, JavaScript polling/toggle logic, and CSS styling for a Hydrabase status button in webui (index.html, static/script.js, static/style.css) to display and control worker state.
2026-02-21 02:15:43 -08:00
Broque Thomas
35fd7bb294 Add Hydrabase dev UI and WebSocket support
Introduce a developer-only Hydrabase testing UI and backend WebSocket integration. Adds a simple dev-mode toggle (password 'hydratest') and new API endpoints (/api/dev-mode, /api/hydrabase/connect, /api/hydrabase/disconnect, /api/hydrabase/status, /api/hydrabase/send) that use websocket-client to connect/send raw JSON to a Hydrabase instance. Frontend changes include a Hydrabase nav/page, payload editors, response panel, dev-mode UI in Settings, associated JS handlers, CSS styling, and an icon asset. Also add websocket-client to requirements.
2026-02-21 00:32:04 -08:00
Broque Thomas
cfa4a0c59f Add $artistletter and multi-disc support
Introduce $artistletter and $disc template variables across config, UI, and backend to support artist-first-letter tokens and multi-disc albums. Update web_server.py to include disc_number in template context, prefer user-controlled $disc in templates, and create configurable disc subfolders using a new file_organization.disc_label setting. Update example and active config, web UI to expose the new variable and disc label selector, and script.js to validate, load, and save the new settings and substitutions.
2026-02-20 22:53:15 -08:00
Broque Thomas
d2adf17ca5 Add lossy MP3 copy of downloaded FLACs
Introduce a configurable "lossy_copy" feature that creates an MP3 copy alongside downloaded FLAC files. Adds default config (example and runtime) and UI controls for enabling the feature and selecting an MP3 bitrate. Implements _create_lossy_copy in web_server.py which checks the FLAC extension, respects the configured bitrate (default 320 kbps), locates ffmpeg (including a local tools/ffmpeg fallback), performs conversion, and attempts to update the QUALITY tag via mutagen. The feature is invoked after post-processing/moving downloads. Logs and graceful failures (missing ffmpeg, timeouts, tag errors) are included.
2026-02-20 22:16:00 -08:00
Broque Thomas
d858a7c85f Add $quality template var (filename only)
Introduce a new $quality template variable that is only substituted into filenames to avoid splitting album folders when tracks of mixed qualities are present. Updates include:

- web_server.py: populate template contexts with 'quality' (from context['_audio_quality']), strip $quality from folder components, substitute it only in the filename, and clean up empty brackets/parentheses/dashes when the variable is empty.
- config/config.example.json and config/config.json: document the new variable in the file_organization template variables string.
- webui/index.html and webui/static/script.js: update UI help text and client-side template validation to include $quality.

This prevents folder fragmentation for albums with mixed-quality files while still allowing quality information in filenames.
2026-02-20 20:18:01 -08:00
Broque Thomas
d4d2568f32 Fix metadata cross-contamination when downloading concurrent albums 2026-02-20 17:13:53 -08:00
Broque Thomas
3644422ab8 Add FLAC bit depth filter to post-download quality gate 2026-02-20 15:05:45 -08:00
Broque Thomas
d1109d9fda fixed issue where post processing was skipped for 'save as playlist' downloads. 2026-02-20 13:21:23 -08:00
Broque Thomas
05758a7e8d include bit depth in post processing and metadata 2026-02-20 13:13:19 -08:00
Broque Thomas
7726e86a78 Fix false positive Soulseek connection test by checking network state 2026-02-20 11:32:52 -08:00
Broque Thomas
3048638d9b Preserve full album context when adding tracks to wishlist 2026-02-19 22:36:57 -08:00
Broque Thomas
759669b0c5 Fix downloads stalling when browser is closed 2026-02-19 22:18:11 -08:00
Broque Thomas
4d47ccf99a Fix downloads halting when browser is closed by moving post-processing trigger from browser polling to background monitor 2026-02-19 21:26:36 -08:00
Broque Thomas
3ce0955b1c fixed an issue where the wishlist deduplication was inconsistent between front and backend 2026-02-19 20:52:47 -08:00
Broque Thomas
cc5b13dded "Fix intermittent deadlock in download monitor that freezes entire download pipeline 2026-02-19 18:13:50 -08:00
Broque Thomas
50a37e5a70 move acoustID mismatches to a ss_quarantine folder inside the 'download' folder with a .quarantined extension. 2026-02-19 15:33:04 -08:00
Broque Thomas
3f0854e070 Fix AcoustID verification: MusicBrainz metadata fallback and quarantine reliability 2026-02-19 14:29:59 -08:00
Broque Thomas
c018c5fd98 Differentiate "not found" from download errors with distinct status and UI 2026-02-19 12:05:20 -08:00
Broque Thomas
c70cfd335a Fix missing formats key in error path and clean up format query 2026-02-19 10:50:53 -08:00
Broque Thomas
6c6651b879 Add format summary tags to library release cards, wishlist modal, and artist hero 2026-02-19 10:20:30 -08:00
Broque Thomas
3663a75769 Show track ownership indicators and file metadata in wishlist modal for all releases 2026-02-19 09:39:56 -08:00
Broque Thomas
8cf0950d3b Quality filter: use bitrate density instead of file size, cache Library/Discover pages, extend Beatport cache to 24h 2026-02-19 07:40:49 -08:00
Broque Thomas
4f8fff5daa Update web_server.py 2026-02-18 21:57:04 -08:00
Broque Thomas
c281dd0cda fix issue where soulsycn would 404 would opening an album on library page 2026-02-18 21:53:26 -08:00
Broque Thomas
8e89040b19 Embed Deezer & AudioDB metadata (BPM, mood, style, ISRC) in post-processing 2026-02-18 20:30:49 -08:00
Broque Thomas
7b6e94772e fixed an issue wher ecollaborating artists would have the album listed as their own on library and artist page. 2026-02-18 17:11:53 -08:00
Broque Thomas
2ab52a340b Add Deezer enrichment for artists, albums, and track
Add Deezer as a third metadata enrichment source. Enriches tracks with BPM and explicit flags, albums with
   record labels, explicit flags, and type classification (album/single/EP), and backfills artwork and genres across
  all entities. Includes background worker with priority queue, rate-limited API client, database migration, server
  endpoints, and UI button with purple-themed status tooltip.
2026-02-18 16:41:24 -08:00
Broque Thomas
1566def362 silence stale download log spam 2026-02-18 15:23:53 -08:00
Broque Thomas
1a4395cc95 Add AudioDB enrichment for artists, albums, and tracks
Integrated TheAudioDB as a metadata enrichment source. A background worker scans the library in priority order (artists → albums → tracks), matching entities via fuzzy name comparison and storing style, mood, and AudioDB IDs. Includes rate limiting, 30-day retry for not-found items, and a UI tooltip showing phase-based scan progress.
2026-02-18 14:08:01 -08:00
Broque Thomas
5e61a15f7f Add Spotify disconnect button and cache auth checks
Add a UI button to disconnect Spotify and fall back to iTunes/Apple Music without restarting.    Cache is_spotify_authenticated() with a 60s TTL to reduce redundant API calls (~46 call sites were each
  triggering a live sp.current_user() call). Fix status endpoint calling the auth check twice per poll,
  and ensure both OAuth callback handlers (port 8008 Flask route and port 8888 dedicated server)
  invalidate the status cache so the UI updates immediately after authentication.
2026-02-18 11:40:07 -08:00
Broque Thomas
c34905997b try one last time on potentially failed downloads 2026-02-17 20:42:53 -08:00
Broque Thomas
308f0f9711 Add retry logic and adaptive rate limiting to watchlist scan
Spotify's @rate_limited decorator now retries on 429/5xx with exponential backoff (up to 5 retries) instead of sleeping once and raising. Watchlist scan delays scale dynamically based on lookback setting and artist count to prevent sustained API pressure. A circuit breaker pauses the scan after consecutive rate-limit failures.
2026-02-17 16:45:09 -08:00
Broque Thomas
0f18b12967 Move bubble snapshots from disk to database 2026-02-17 16:12:22 -08:00
Broque Thomas
0951b0391d fixed issue where metadata wasn't completely wiped before processing. 2026-02-17 15:25:59 -08:00
Broque Thomas
0c40a922e6 Add batch remove to watchlist modal 2026-02-17 11:28:44 -08:00
Broque Thomas
139b8530f4 Add watchlist filter to library page 2026-02-17 11:21:41 -08:00
Broque Thomas
801274deb3 Add error reason tooltips to failed downloads in missing tracks modal
When a download fails in the Download Missing Tracks modal, hovering over the "Failed" status now shows a   tooltip explaining why. The backend already tracked error_message on tasks internally but never sent it to the frontend. This surfaces those reasons and enriches them with detailed context — search diagnostics break down what happened per query (no results, filtered out, search error), retry failures include source counts and likely cause, and timeout/stuck messages name the state and duration. The tooltip uses a fixed-position popup to avoid clipping by the modal's scroll container, with scroll and visibility-aware dismissal.
2026-02-17 10:28:12 -08:00
Broque Thomas
335c9dc977 Fix MP3 metadata being wiped by intermediate save after tag clear 2026-02-17 09:12:29 -08:00
Broque Thomas
71f362b79a Fix wishlist crash when artist context is a string instead of dict 2026-02-17 08:55:32 -08:00
Broque Thomas
0a518b2a09 Wipe existing tags before writing Spotify metadata proper
Was overwriting without clearing beforehand leading to leftover metadata from original source
2026-02-16 22:11:51 -08:00
Broque Thomas
b189d7230e Fix file descriptor leak from per-call event loop creation 2026-02-16 21:51:23 -08:00
Broque Thomas
e438563acc manage timedout status from slskd 2026-02-16 18:34:49 -08:00
Broque Thomas
c55bf57c48 adjust slskd search results to match whole word rather than substring for better matching. 2026-02-16 15:50:38 -08:00
Broque Thomas
1ed66c120e Fix Soulseek connection status showing false positive on test
The Soulseek "Test Connection" button could report success while the dashboard status remained              "Disconnected" because the status endpoint only checked if slskd was configured (is_configured()), not actually
reachable (check_connection()). Switched to a real connection check with TTL caching (matching the existing
Spotify/media server pattern), update the status cache after successful tests, and refresh the UI immediately on success.
2026-02-16 14:42:17 -08:00
Broque Thomas
f988ebf5f5 fixed an issue where backend and front could be out of sync on wishlist modal 2026-02-16 13:50:03 -08:00
Broque Thomas
d1259c4b62 Add multi-disc album support with automatic disc subfolder organization
Multi-disc albums (e.g., deluxe editions, double albums) now automatically organize
  tracks into Disc 1/, Disc 2/ subfolders within the album folder. Detection uses the
  disc_number field from Spotify's API — when an album has total_discs > 1, subfolders
  are created. Single-disc albums are completely unaffected.

  - Plumb disc_number through all download paths (enhanced, non-enhanced, download
    missing modal, wishlist)
  - Compute total_discs from album tracklist and store on album context
  - Modify path builder to insert Disc N/ subfolder for multi-disc albums
  - Preserve disc_number when tracks fail and get re-added to wishlist
  - Preserve disc_number when adding tracks to wishlist from library page
  - Add visual disc separators in Soulseek search result track lists
2026-02-16 13:10:03 -08:00
Broque Thomas
96f991a833 multi cd post processing is now possible. No longer treated as single cd. 2026-02-16 11:49:26 -08:00
Broque Thomas
f9b0316d0f fix str object has no attribute get error. 2026-02-16 10:13:56 -08:00
Broque Thomas
ff0cfd53c5 fix 404 error due to spotify api changes 2026-02-16 09:25:28 -08:00
Broque Thomas
1fd8e59a17 css changes and lazyload changes to increase site speed.
Also fixed a docker path bug, well a potential bug.
2026-02-16 09:08:32 -08:00
Broque Thomas
ff3f547612 update Spotify API search limits for February 2026 changes 2026-02-15 21:01:14 -08:00
Broque Thomas
aa91d851c3 fix cross-device file moves failing when source can't be deleted 2026-02-15 20:38:53 -08:00
Broque Thomas
5f12634f61 fix seasonal discovery showing "Various Artists" and using wrong download mode 2026-02-15 19:29:20 -08:00
Broque Thomas
79b0591b34 fix issue where itunes/spotify seasonal discovery pools were not isolated. 2026-02-15 15:56:43 -08:00
Broque Thomas
1503a6ef81 remove time suffix from slskd downloads where necessary to improve matching 2026-02-15 15:22:55 -08:00
Broque Thomas
0d66f884e6 fix - jellyfin servers can not select a user when determing which library to scan. 2026-02-15 14:09:44 -08:00
Broque Thomas
51515bc8a1 update spotify api in response to their bullshit 2026-02-15 11:46:19 -08:00
Broque Thomas
883b43b015 fix issue where wishlist would not delete entire album.
Also fixed issue where a warning was displayed.
2026-02-15 10:48:58 -08:00
Broque Thomas
a5c4783da6 clean up orphaned downloads before they gunk up the download folder. 2026-02-15 10:03:28 -08:00
Broque Thomas
e66ed32463 fix the 'fix' modal on discovery modal if track is incorrect 2026-02-14 21:32:10 -08:00
Broque Thomas
d907558abf Fixed issue where itunes only watchlist artists would update the 'last scanned' property after watchlist scan 2026-02-13 16:10:19 -08:00
Broque Thomas
40be502a98 Enhanced search: separate albums from singles/EPs and improve UX
Split the enhanced search dropdown into distinct Albums and Singles & EPs sections using the album_type field. Changed the zero-tracks error from a cryptic red error toast to a clear warning message. Fixed the loading text to show the actual music source name instead of hardcoded "Spotify".
2026-02-13 08:53:19 -08:00
Broque Thomas
ff4d3adab5 Auto-clean slskd completed downloads after batch processing
Automatically clear completed downloads from slskd after batch completion, matching the cleanup behavior already present in the frontend. Adds a 5-minute periodic sweep as a safety net when no batches are actively running.
2026-02-12 15:45:11 -08:00
Broque Thomas
20c7aa33a3 Add per-track ownership indicators to Library wishlist modal
When clicking a partially-complete album on the Library page, the wishlist modal now shows which tracks are owned (dimmed with checkmark) and which are missing (orange border). Ownership data lazy-loads after the modal opens to avoid blocking the UI, using a batch DB query for speed. Also fixes albums like DAMN. showing "14/15" when all available tracks are owned — the frontend now trusts the backend's "completed" status instead of doing raw track count math against potentially inaccurate Spotify metadata.
2026-02-12 13:09:42 -08:00
Broque Thomas
f7c929abec Library page: lazy-load artist discography with SSE streaming
Replace blocking DB matching in the Library artist detail view with a two-phase render pattern. The page now renders album cards instantly from Spotify/Itunes data , then streams per-release ownership results via SSE that update cards one-by-one.
2026-02-12 10:54:42 -08:00
Broque Thomas
d1516021ae remove debugging 2026-02-09 16:26:50 -08:00
Broque Thomas
83b18d3c74 fixed an issue where the incorrect art would display in the hero slider on discover page 2026-02-09 15:57:09 -08:00
Broque Thomas
51ffae4385 bump v1.6, update changelog, fix Spotify API deprecation
Update version to 1.6 in sidebar and API. Add changelog for local import, enhanced tagging, mobile layout, and performance improvements. Fix track popularity field access for upcoming Spotify API changes (February 2026).
2026-02-07 16:26:14 -08:00
Broque Thomas
d674b999e5 feat: Import Music from local staging folder
Added an Import feature that lets users process local audio files through the existing post-processing pipeline (metadata enrichment, cover art, lyrics, library organization). Files are placed in a configurable Staging folder and imported via two modes: Album mode (search/match files to a Spotify tracklist) and Singles mode (select individual files for processing). Includes auto-suggested albums based on staging folder contents and real-time per-track progress tracking.
2026-02-06 23:29:05 -08:00
Broque Thomas
48b188446b feat: discovery match cache, mobile sync layout fixes
- Add global discovery_match_cache table to cache successful track matches
    (title+artist+provider -> matched result) across all discovery sources
  - Cache check before API search in YouTube, ListenBrainz, Tidal, and Beatport
    discovery workers; cache write after high-confidence matches
  - Re-discovering playlists or overlapping tracks across sources skips API lookups
  - Fix Spotify tab sidebar forcing 2-column grid on mobile via inline JS styles
  - Add mobile responsive styles for Spotify playlist cards (stack layout vertically)
2026-02-06 09:24:50 -08:00
Broque Thomas
921bf077fd ensure itunes parity for metadata input 2026-02-05 22:40:41 -08:00
Broque Thomas
d08a2e91a2 feat: embed MusicBrainz, Spotify/iTunes IDs, ISRC, and merged genres into audio file tags
Enrich downloaded audio files with external identifiers and improved genre metadata in a single post-processing write. During metadata enhancement, the app now looks up the MusicBrainz recording and artist MBIDs, retrieves the ISRC and MusicBrainz genres from a follow-up detail lookup, merges them with Spotify's artist-level genres (deduplicated, capped at 5), and embeds everything alongside the Spotify/iTunes track, artist, and album IDs. All MusicBrainz API calls are serialized through the existing global rate limiter, making concurrent download workers safe without needing to pause the background worker. Includes a database migration adding Spotify/iTunes ID columns to the library tables.
2026-02-05 21:26:19 -08:00
Broque Thomas
d9efcbdf99 feat: AcoustID audio verification, MusicBrainz enrichment UI, v1.5
Add optional post-download audio fingerprint verification using AcoustID.
  Downloads are verified against expected track/artist using fuzzy string
  matching on AcoustID results. Mismatched files are quarantined and
  automatically added to the wishlist for retry.

  - AcoustID verification with title/artist fuzzy matching (not MBID comparison)
  - Quarantine system with JSON metadata sidecars for failed verifications
  - fpcalc binary auto-download for Windows, macOS (universal), and Linux
  - MusicBrainz enrichment worker with live status UI and track badges
  - Settings page AcoustID section with real-fingerprint connection test
  - Source reuse for album downloads to keep tracks from same Soulseek user
  - Enhanced search queries for better track matching
  - Bug fixes: wishlist tracking, album splitting, regex & handling, log rotation
2026-02-05 16:33:07 -08:00
Broque Thomas
fa808e5bba include acoustID in settings page. foundation for verification flow 2026-02-03 16:57:53 -08:00
Broque Thomas
cee5590718 feat(ui): add MusicBrainz enrichment status UI with real-time monitoring
MusicBrainz library enrichment with real-time
status monitoring and manual control.
Features:
- Status icon button in dashboard header with glassmorphic design
- Animated loading spinner during active enrichment
- Hover tooltip showing:
  - Worker status (Running/Paused/Idle)
  - Currently processing item
  - Artist matching progress with percentage
- Click-to-toggle pause/resume functionality
- Auto-polling every 2 seconds for live updates
Backend Changes:
- Added GET /api/musicbrainz/status endpoint
- Added POST /api/musicbrainz/pause endpoint
- Added POST /api/musicbrainz/resume endpoint
- Worker tracks current_item for UI display
- get_stats() returns enhanced status data
Frontend Changes:
- New MusicBrainz button component with tooltip
- Premium CSS styling with animations
- JavaScript polling and state management
- Positioned tooltip below button with centered arrow
Files Modified:
- web_server.py: API endpoints and worker initialization
- core/musicbrainz_worker.py: current_item tracking
- webui/index.html: Button and tooltip structure
- webui/static/style.css: Complete styling (240 lines)
- webui/static/script.js: Polling and interaction logic (115 lines)
2026-02-03 15:20:04 -08:00
Broque Thomas
6d9ee9f9c1 fix issue where worker would retry the same host if failed. 2026-02-02 13:49:56 -08:00
Broque Thomas
2d866cf0dd Fix failed tracks not being added to wishlist. 2026-02-02 13:42:54 -08:00
Broque Thomas
863f04c5ae Fix album splitting in media servers for multi-source downloads
- Files downloaded from different soulseek sources carried conflicting MusicBrainz Release IDs and disc number tags that were never overwritten during metadata enhancement, causing Navidrome/Plex to split a single album into multiple entries even when folder structure and album name were identical
  - disc_number is now written to file metadata (sourced from Spotify/iTunes API), overwriting stale tags from soulseek sources
  - MusicBrainz IDs are now stripped during metadata enhancement so media servers group by album name + artist instead of mismatched release IDs
  - The verification worker now applies _resolve_album_group() for album name consistency with the stream processor path
  - Fixed source reuse retry logic: used_sources is no longer reset between retries, so a source that errors during transfer is skipped on retry and the system falls through to a normal search instead of retrying the same failing source 3 times
2026-02-02 10:00:56 -08:00
Broque Thomas
af7279a3ea add 'rejected' to the list of errored states from slskd for album level source. 2026-02-01 16:43:49 -08:00
Broque Thomas
01ac392307 Fix post-processing race condition between Stream Processor and Verification Worker
Added per-context-key threading lock in _post_process_matched_download to serialize access when both the Stream    Processor and Verification Worker attempt to process the same downloaded file. Previously, both threads would race to   move the source file via shutil.move, causing FileNotFoundError for the loser. The lock ensures the first thread    completes the move before the second enters, where existing protection checks detect the file was already transferred    and return early.
2026-02-01 16:15:22 -08:00
Broque Thomas
4581603005 app log rotation with capped file size 2026-02-01 08:38:38 -08:00
Broque Thomas
60fdf4c82c Fix issue on windows where source files were removed before transfer. 2026-02-01 08:35:05 -08:00
Broque Thomas
35e65b2106 When downloading albums/EPs from Soulseek, after the first track downloads successfully, browse the source's folder and reuse it for subsequent tracks. This keeps entire albums coming from one source instead of scattering across many users.
- Single-worker mode for album batches (sequential downloads enable clean source reuse)
  - Browse API integration to list files in a source's directory
  - Failed source tracking per-batch to avoid retrying broken sources
  - Graduated quality scoring for upload speed, queue length, and free slots
  - Track number fallback fix (uses Spotify track number instead of hardcoded 1)
  - Duplicate completion guard to prevent double-decrement of active worker count
  - Dedicated logging for source reuse and post-processing diagnostics
2026-02-01 00:42:39 -08:00
Broque Thomas
7d8bb2b88a Revert "Add album-level Soulseek search and fix unbounded log growth"
This reverts commit 258fd7a8ae.
2026-01-31 18:42:37 -08:00
Broque Thomas
bbac51412d Revert "album level search for wishlist albums/eps"
This reverts commit 28ca3f5ce5.
2026-01-31 18:42:17 -08:00