Commit graph

763 commits

Author SHA1 Message Date
Broque Thomas
d329dd4fe8 Fix Tidal playlist endpoints redundantly re-fetching all playlists — use direct single-playlist fetch 2026-03-11 13:50:53 -07:00
Broque Thomas
e42f373f80 Isolate service client initialization so one failure doesn't break the entire app 2026-03-11 11:25:15 -07:00
Broque Thomas
bde2be1cfa Spotify rate limit re-trigger loop caused by periodic auth probes 2026-03-11 08:28:36 -07:00
Broque Thomas
0aa8950436 fix build a playlist functionality and update the ui 2026-03-11 07:52:58 -07:00
Broque Thomas
e5450d9f89 Help Docs Overhaul & Settings Fixes 2026-03-10 23:50:21 -07:00
Broque Thomas
c06fd044a1 Profile Permissions & Page Access Control 2026-03-10 23:23:43 -07:00
Broque Thomas
a159ac3fd6 Fix activity feed blinking and show live relative timestamps 2026-03-10 20:34:57 -07:00
Broque Thomas
b75f115a85 update latest updates 2026-03-10 15:50:31 -07:00
Broque Thomas
58a2f1ac4a Fix iTunes country setting not persisting on save 2026-03-10 13:27:30 -07:00
Broque Thomas
08cc10c909 update latest updates 2026-03-10 12:59:13 -07:00
Broque Thomas
7138125b9e Fix post-processing race condition logging errors on already-moved files 2026-03-10 11:30:55 -07:00
Broque Thomas
dbe0ce4db4 Fix playlist folder mode race condition where second thread deletes successfully moved file 2026-03-10 10:01:39 -07:00
Broque Thomas
6506c2f163 update latest updates 2026-03-10 09:28:55 -07:00
Broque Thomas
dc7140c459 Add Last.fm and Genius to on-demand enrichment, settings reload, and enrich dropdown parity 2026-03-10 09:12:36 -07:00
Broque Thomas
f8d23ec37c Add Last.fm and Genius API clients with settings integration 2026-03-09 22:35:10 -07:00
Broque Thomas
aa93458ed3 Configurable ListenBrainz API endpoint for self-hosted instances 2026-03-09 20:52:33 -07:00
Broque Thomas
bbccd3524f Explicit content filter with configurable toggle to skip explicit tracks during downloads 2026-03-09 20:39:40 -07:00
Broque Thomas
16e01f6039 Pause enrichment workers during discovery to prioritize user-initiated API calls 2026-03-09 19:42:48 -07:00
Broque Thomas
d1890c768c cleanup staging folder same as download folder 2026-03-09 17:10:35 -07:00
Broque Thomas
07a79e7af6 Full Cleanup automation: combined housekeeping sweep for quarantine, downloads, staging, and search history 2026-03-09 16:39:29 -07:00
Broque Thomas
6e16596b9d update recent updates 2026-03-09 16:07:43 -07:00
Broque Thomas
eac97a6c2b Smart Spotify rate limit detection with global ban, auto-suppression, and frontend modal 2026-03-09 16:05:33 -07:00
Broque Thomas
1ea262c749 fix cover images not appearing in music player modal 2026-03-09 14:55:23 -07:00
Broque Thomas
cc4502e5f8 Add server sync option to enhanced library write-tags flow 2026-03-09 14:17:56 -07:00
Broque Thomas
7411bd1eab Clarify update notification for Docker users that image will follow 2026-03-09 13:15:03 -07:00
Broque Thomas
b3d607752b Add version tracking to database backup manager & Fix radio mode next track closing modal and losing playback state 2026-03-09 13:10:25 -07:00
Broque Thomas
abec2965ac Fix playlist folder downloads marked as failed despite successful processing 2026-03-09 12:04:57 -07:00
Broque Thomas
aebaa900c9 recent updates updated 2026-03-09 11:53:23 -07:00
Broque Thomas
8d46d3746b Fix Docker upgrade crashes from stale volume mounts and partial DB migrations 2026-03-09 11:44:00 -07:00
Broque Thomas
ea1441d09d "Write Tags to File" in the Enhanced Library Manager 2026-03-09 11:17:01 -07:00
Broque Thomas
5f58432ca4 Redesigned media player with expanded Now Playing modal and smart radio 2026-03-09 10:13:42 -07:00
Broque Thomas
acc88441c2 update latest updates. 2026-03-08 23:39:40 -07:00
Broque Thomas
7c50f350c0 Add a library management interface to the artist detail page with inline metadata editing, per-service manual matching, bulk operations, and full track/album management. 2026-03-08 23:37:02 -07:00
Broque Thomas
5f94352b40 Add cancellation support to all discovery workers 2026-03-08 18:12:37 -07:00
Broque Thomas
3f4770a735 Fix quality scanner stop button, rate limiting, and redundant Spotify client 2026-03-08 12:58:14 -07:00
Broque Thomas
b0761a6e62 update version modal 2026-03-08 12:33:41 -07:00
Broque Thomas
5daa8c0596 Add rich stats to automation run history 2026-03-08 12:20:50 -07:00
Broque Thomas
266d044797 Add Backup Manager dashboard tool card with list, download, restore & delete 2026-03-08 10:46:03 -07:00
Broque Thomas
b1cb9f9964 Add scheduled database backup system automation 2026-03-08 08:24:49 -07:00
Broque Thomas
2ef0c75a25 Fix sync completion not reaching UI after WebSocket reconnect 2026-03-07 22:53:15 -08:00
Broque Thomas
8b3b82702a Add deep library scan automation for enrichment-safe sync 2026-03-07 18:39:45 -08:00
Broque Thomas
ddd7f2d9b5 Persist mirrored playlist discovery results & retry failed 2026-03-07 17:28:17 -08:00
Broque Thomas
e11ee8622e Fix discovery modal persistence, artist dict handling, and rate limiter scope 2026-03-07 13:05:49 -08:00
Broque Thomas
d97b3d1846 Fix automation timezone bug 2026-03-07 11:42:44 -08:00
Broque Thomas
dbe9745794 Add new automations for refreshing beatport, cleaning download folder and cleaning search history. 2026-03-07 11:11:43 -08:00
Broque Thomas
93a83f0417 Guard against string album values in wishlist spotify_data to prevent AttributeError 2026-03-07 10:17:06 -08:00
Broque Thomas
b34e348937 keep a history of automation runs 2026-03-07 09:39:08 -08:00
Broque Thomas
ba6aea5435 Add rich progress tracking to all automation cards with stall detection and timeout handling 2026-03-07 00:57:03 -08:00
Broque Thomas
41e895d254 Batch watchlist status checks to eliminate rate limit errors 2026-03-06 23:41:01 -08:00
Broque Thomas
cdcaa245d1 Add post-scan phase progress to watchlist automation card 2026-03-06 23:15:30 -08:00
Broque Thomas
c2af4152ab Add live status feed to database update automation card 2026-03-06 21:42:43 -08:00
Broque Thomas
4fa66b8981 Fix batch completion detection for post-processing race condition 2026-03-06 20:53:06 -08:00
Broque Thomas
156c37d907 Replace hardcoded post-download chain with system automations 2026-03-06 19:48:48 -08:00
Broque Thomas
8b6a2c0adc allow multiple notification calls per automation as well as a new signal fire utility 2026-03-06 18:10:15 -08:00
Broque Thomas
7647ac22ed Add Discovery Pool dashboard tool card and revamp modal with premium category-card design 2026-03-06 11:59:45 -08:00
Broque Thomas
c9d83d5d13 Fix Tidal OAuth PKCE verification on Flask callback route 2026-03-06 10:27:17 -08:00
Broque Thomas
d264ec70f3 Add sync match cache and fix discovery clear to purge cache 2026-03-06 09:41:44 -08:00
Broque Thomas
b775d290ae Update web_server.py 2026-03-06 08:41:28 -08:00
Broque Thomas
7485ba8aa2 fix issue with tracking auto sync playlist node 2026-03-06 08:01:17 -08:00
Broque Thomas
69d16ec402 Fix sync playlist node 2026-03-05 22:18:58 -08:00
Broque Thomas
3af8c941b0 speed up discovery node 2026-03-05 22:10:28 -08:00
Broque Thomas
db03735183 Update web_server.py 2026-03-05 22:04:33 -08:00
Broque Thomas
17babb1fc8 fix spotify playlist discover automation 2026-03-05 21:17:58 -08:00
Broque Thomas
9f416475e2 Live automation progress tracking with real-time output panels 2026-03-05 20:39:26 -08:00
Broque Thomas
d57b48a62a Playlist discovery pipeline with official metadata enforcement for automated sync 2026-03-05 18:32:18 -08:00
Broque Thomas
f99f873d60 Replace hardcoded wishlist/watchlist timers with system automations + add Pushbullet & Telegram notifications 2026-03-05 16:50:09 -08:00
Broque Thomas
da707dcf0a Full automation engine expansion with scheduling, triggers, actions, and UI polish 2026-03-05 15:13:32 -08:00
Broque Thomas
75f9b7364a User configurable youtube rate limiting and optional cookies for bot detection 2026-03-05 10:50:06 -08:00
Broque Thomas
aef056cfcb DB-first metadata updater 2026-03-05 10:03:39 -08:00
Broque Thomas
5938f6fec7 Fix download retry fallback not cycling through available sources 2026-03-04 20:16:24 -08:00
Broque Thomas
60f93d5858 hydrabase changes 2026-03-04 19:48:12 -08:00
Broque Thomas
f051292e0d update version modal 2026-03-04 18:32:03 -08:00
Broque Thomas
b30e1f60bd Add Mirrored Playlists — persistent cross-service playlist archive
Automatically mirrors every parsed playlist (Spotify, Tidal, YouTube, Beatport) to a local database so they're always accessible — even if a service subscription lapses or the browser closes.
  - New "Mirrored" tab on the Sync page with source-branded cards showing discovery/download status
  - Auto-mirrors on successful parse (upsert — re-parsing updates the existing mirror, no duplicates)
  - Click any mirrored playlist to browse its full track list, then run it through the discovery pipeline
  - Cards dynamically reflect live state: Discovering → Discovered → Downloading → Downloaded
  - Download modal rehydrates after page refresh — click a "Downloading..." card to resume viewing progress
  - All phase transitions (start, complete, cancel, error, modal close) keep card and backend state in sync
  - Profile-scoped via profile_id, consistent with other features
2026-03-04 18:27:18 -08:00
Broque Thomas
114af496c7 Track version 2026-03-04 11:32:40 -08:00
Broque Thomas
1bec9320b4 update version modal 2026-03-04 11:11:31 -08:00
Broque Thomas
2d6c55e294 Fix chromaprint crash on surround audio and Spotify worker status display 2026-03-04 11:00:34 -08:00
Broque Thomas
4fee005dee Add multi-profile support with Netflix-style profile picker
Allow multiple users to share a single SoulSync instance with isolated personal data. Each profile gets its own watchlist, wishlist, discovery pool, similar artists, and bubble snapshots — while sharing the same music library, database, and service credentials.

  - Netflix-style profile picker on startup when multiple profiles exist
  - Optional PIN protection per profile; admin PIN required when >1 profiles
  - Admin-only profile management (create, edit, rename, delete)
  - Profile avatar images via URL with colored-initial fallback
  - Zero-downtime SQLite migration — all existing data maps to auto-created
    admin profile
  - Single-user installs see no changes — profile system is invisible until
    a second profile is created
  - WebSocket count emitters scoped to profile rooms (watchlist/wishlist)
  - Background scanners (watchlist, wishlist, discovery) iterate all profiles
2026-03-04 10:46:08 -08:00
Broque Thomas
84de4ad16b Redesign watchlist modal with enriched artist detail view 2026-03-03 20:26:21 -08:00
Broque Thomas
0f428dc45c Add WebSocket real-time updates with automatic HTTP polling fallback
Migrates 38 HTTP polling loops to WebSocket push events across 6 phases: service status, dashboard stats, enrichment workers, tool progress, sync/discovery progress, and scan status. All original HTTP polling is preserved as automatic fallback — if WebSocket is unavailable or disconnects, the app seamlessly reverts to its previous behavior. Includes 162 tests verifying event delivery, data shape, and HTTP parity. Also fixes a copy-paste bug in Beatport sync error cleanup.
2026-03-03 18:26:29 -08:00
Broque Thomas
7b854baba8 Detect and remove deleted content during incremental database updates
Incremental database updates now detect when artists or albums have been removed from your media server (Plex, Jellyfin, or Navidrome) and automatically clean them up from SoulSync's database. Previously, deleted content would persist as ghost entries until you ran a full refresh. Removal counts are reported in the scan results. Includes safety checks to prevent accidental mass deletion if the server is unreachable or returns incomplete data.
2026-03-03 13:13:38 -08:00
Broque Thomas
65bae58cfe Reverse proxy fix 2026-03-03 12:04:53 -08:00
Broque Thomas
d9aa8303a7 Add SoulSync REST API (v1) with API key authentication
Adds a full public REST API at /api/v1/ with 32 endpoints covering library, search, downloads, wishlist, watchlist, playlists, system status, and settings. Includes API key authentication (Bearer token), per-endpoint rate limiting, and consistent JSON response format. API keys can be generated and managed from the Settings page. No changes to existing functionality — the API delegates to the same backend services the web UI uses.
2026-03-03 09:49:00 -08:00
Broque Thomas
7a4cbc066f Fix infinite monitor loop on post-processing completed tasks 2026-03-02 12:50:20 -08:00
Broque Thomas
c4778bc269 wishlist should always 'force download all' 2026-03-02 12:46:39 -08:00
Broque Thomas
df33adf6a7 include 'add to watchlist' button to each artist in library. 2026-03-02 10:04:10 -08:00
Broque Thomas
1f73ba4f94 view recommended artists. 2026-03-02 09:12:35 -08:00
Broque Thomas
8f2dd66aee add a 'watch all' button on hero slider to quickly add all artists in slider. 2026-03-02 08:40:22 -08:00
Broque Thomas
db2f7e999c Fix ListenBrainz database path not respecting DATABASE_PATH env var 2026-03-02 07:46:42 -08:00
Broque Thomas
c279f2e4fa Design retag layout 2026-03-01 22:52:27 -08:00
Broque Thomas
6df61d6ba1 use watchlist artist name 2026-03-01 19:24:12 -08:00
Broque Thomas
4fba18b25e Feat: Custom accent colors. 2026-03-01 15:29:37 -08:00
Broque Thomas
1aa6cdc9b6 Rebuild the import feature and move it to its own page. 2026-03-01 14:18:55 -08:00
Broque Thomas
e38c0adc57 Fix: Various artist compilations caused failure on acoustID check. 2026-03-01 11:39:04 -08:00
Broque Thomas
dced926a6e fixed issue where cancelled downloads wouldn't send album context to wishlist. 2026-03-01 09:22:50 -08:00
Broque Thomas
afa495756f Version 1.7 update 2026-02-28 21:58:13 -08:00
Broque Thomas
acfc26a4bd Add Tidal as a download source with full pipeline integration
Adds Tidal as a third download source alongside Soulseek and YouTube. Uses the tidalapi library with device-flow authentication to search and download tracks in configurable quality (Low/High/Lossless/HiRes) with automatic fallback. Integrates into the download orchestrator for all modes (Tidal Only, Hybrid with fallback chain), the transfer monitor, post-processing pipeline, and file finder. Frontend includes download settings with quality selector, device auth flow, and dynamic sidebar/dashboard labels that reflect the active download source. No breaking changes for existing users.
2026-02-28 21:47:19 -08:00
Broque Thomas
49c769ff5c Fix Hydrabase endpoint gaps, M3U default, and soul_id mapping
Filled all missing Hydrabase fallthrough gates across 6 endpoints (artist album tracks, iTunes album, discover album, Spotify track, both similar artists), added     track_number/disc_number to Track dataclass, fixed get_album_tracks to send soul_id instead of text query, mapped soul_id field from Hydrabase responses across all search methods, updated 7 frontend call sites to pass album name/artist params, and fixed M3U export defaulting to enabled when users never turned it on.
2026-02-28 18:25:55 -08:00
Broque Thomas
0193f53d28 Improve Spotify artist search for short names using field filter 2026-02-28 11:04:50 -08:00
Broque Thomas
97502ec600 Boost exact artist name matches to top of search results
Fix short artist names (e.g. "ano") getting buried by wildcard matches. Re-ranks Spotify and iTunes results so exact   name matches appear first, without changing what's returned.
2026-02-28 08:41:19 -08:00
Broque Thomas
694f190b6d Update web_server.py 2026-02-28 08:00:11 -08:00
Broque Thomas
1fbb1ec2c5 debug post processing flow 2026-02-27 11:34:27 -08:00
Broque Thomas
698b1fa4f3 clear current downloads 2026-02-27 11:18:13 -08:00
Broque Thomas
2dc72a39b7 Add music library selection for Navidrome
SoulSync was importing from all Navidrome libraries regardless of user access restrictions. Added a "Music Library"   dropdown in Navidrome settings that lets users scope imports to a specific music folder. Uses the Subsonic musicFolderId       parameter on artist, album, and search API calls. Selecting "All Libraries" reverts to the previous behavior.
2026-02-27 09:37:58 -08:00
Broque Thomas
7dfc10eb0b Store sync status in database instead of JSON file
Sync status was being saved to storage/sync_status.json on disk, which fails in Docker with permission denied         errors. Moved to the existing database preference system so it works in all environments.
2026-02-27 08:50:50 -08:00
Broque Thomas
c4bf6ff0f3 jellyfin server connection fix 2026-02-27 08:47:50 -08:00
Broque Thomas
e14a317a56 Add wishlist batch remove API and UI
Introduce batch removal support for wishlist tracks. Adds a new POST endpoint /api/wishlist/remove-batch that validates input, removes multiple tracks via the wishlist service, logs the result and returns a removed count. Updates the frontend (webui/static/script.js) to provide per-track and per-album checkboxes, a Select All button, a batch action bar with selection count and a Remove Selected action (with confirmation), and logic to refresh the view and wishlist count after removal. Styles (webui/static/style.css) are extended to support unified watchlist/wishlist batch bars, checkbox styling, and a Select All button. Preserves existing single-item removal behavior.
2026-02-26 17:19:34 -08:00
Broque Thomas
8c145c29cd Raise min_confidence thresholds to 0.9
Increase the minimum confidence cutoff to 0.9 in multiple discovery/search routines to reduce false positives. Affected functions: _search_spotify_for_tidal_track, _run_youtube_discovery_worker, _run_listenbrainz_discovery_worker, and _run_beatport_discovery_worker. This tightens matching criteria (previously 0.6/0.7/0.75) to favor higher-confidence matches, potentially reducing incorrect matches at the cost of fewer total matches.
2026-02-26 14:17:43 -08:00
Broque Thomas
e3a5608c95 Add manual candidate download API and UI
Add server endpoint to trigger a manual download for a user-selected candidate from the candidates modal (/api/downloads/task/<id>/download-candidate). The endpoint validates input, resets task and batch state (status, error, used_sources, active_count, permanently_failed_tracks), reconstructs Track/TrackResult objects and dispatches a background download attempt via missing_download_executor. Update the frontend candidates modal to show a download button per candidate, wire it to POST the candidate to the new API, and add CSS for table layout and download button styling. Enables restarting failed/not_found tasks by choosing a specific source without blocking the UI.
2026-02-26 13:45:38 -08:00
Broque Thomas
2852e2b39f Add candidates review modal & API
Expose cached search results for failed downloads and add a UI to review them. Implements a new GET /api/downloads/task/<task_id>/candidates endpoint that serializes any cached_candidates and track_info for a task and returns an error message and candidate count. The download worker now collects top raw search results (all_raw_results) and stores them in download_tasks[task_id]['cached_candidates'] when no match is found so users can inspect what Soulseek returned. The batch status payload includes has_candidates to mark "not_found" tasks as reviewable. On the frontend, new script functions (_ensureCandidatesClickListener, showCandidatesModal, _renderCandidatesModal, closeCandidatesModal) fetch and render a modal table of candidates; existing status rendering is updated to attach click handlers and error tooltips. Styles for the modal and a clickable .has-candidates state are added to style.css.
2026-02-26 13:09:51 -08:00
Broque Thomas
b814ae17ce Add quarantine clear API and folder sweeper
Add a POST /api/quarantine/clear endpoint to delete all files and folders inside the ss_quarantine directory (uses docker_resolve_path and reports removed item count). Implement _sweep_empty_download_directories() to walk the downloads folder bottom-up and remove empty directories (preserves root download dir, skips hidden entries, robust against locked/non-empty dirs). Wire the sweeper into existing cleanup flows: clear_finished_downloads(), the periodic _simple_monitor_task(), and the failed-tracks post-cleanup path so leftover empty folders are removed. Also add a Clear Quarantine button in the web UI and a clearQuarantine() client function to call the new API and show feedback.
2026-02-26 11:40:13 -08:00
Broque Thomas
fff8e62070 Set Spotify artist albums limit to 50
Add limit=50 to spotify_client.get_artist_albums so up to 50 albums/singles/compilations are returned per request, reducing the need for extra pagination and ensuring more complete discography results from a single call.
2026-02-26 10:03:02 -08:00
Broque Thomas
3c0d1fc187 Add album artists and watchlist artist fallback
Store album artists in the Spotify track data structure (handles both dict and object album forms) so downstream processing has access to album-level artist metadata. Also update missing-track processing to fall back to source_info['watchlist_artist_name'] when artist_name is absent, ensuring artist context is preserved for legacy/watchlist-sourced entries.
2026-02-26 08:58:44 -08:00
Broque Thomas
3893d75ad0 Raise artist threshold; add title similarity floor
In web_server.py, increase the minimum artist similarity from 0.4 to 0.5 in both cache validation and candidate scoring. Add a title similarity floor (min_title_similarity = 0.5) to _discovery_score_candidates: compute cleaned/core titles, allow a core-title exact match to bypass the floor, otherwise require title similarity >= 0.5. Only candidates that pass both artist and title floors proceed to full weighted scoring. This reduces false positives where a strong artist match could previously mask a poor title match.
2026-02-26 07:54:21 -08:00
Broque Thomas
e77dbf3f1e Refactor matching + use improved discovery scoring
Introduce a generic score_track_match(...) in core/matching_engine.py and make calculate_match_confidence(...) delegate to it. The new scorer is source-agnostic, consolidates artist/title/duration logic (core-title fast path, cleaned similarity, weighted 60/30/10 scoring) and improves artist matching.

In web_server.py add cache-validation (_validate_discovery_cache_artist) and a reusable _discovery_score_candidates(...) helper that calls the new scorer. Propagate per-match confidence through discovery flows (Tidal, YouTube, ListenBrainz, Beatport), increase Spotify/iTunes search limits, add an extended high-limit search strategy, tighten per-source thresholds, and save match confidence to the discovery cache. Overall this centralizes and standardizes matching logic and improves accuracy/validation for cached discovery results.
2026-02-25 22:43:33 -08:00
Broque Thomas
a2cff3f16f Use album artists for wishlist track grouping
Add album.artists to the album data when adding wishlist tracks and change the missing-tracks processing to prefer album-level artists for constructing artist context. The code now uses spotify_data.get('album') or {} and picks artist_ctx from album.artists first, falls back to source_info.artist_name (with safe JSON parsing if source_info is a string), then to track-level artists, and finally to a generic Unknown Artist. This ensures tracks from compilations or albums where the album artist differs from track artists are grouped consistently.
2026-02-25 20:06:19 -08:00
Broque Thomas
a0828c7aad Improve album grouping and normalize suffixes
Expand track title normalization and refine album grouping logic.

- core/acoustid_verification.py: Broadened the parenthetical-suffix regex to strip year-based remasters and additional variants (e.g. "2025 Remaster", "single edit", "album edit") while still removing common extras like (Live), (Deluxe), (Radio Edit), and featuring tags.
- web_server.py: Restrict the smart album grouping to only run for singles/auto-detected albums; explicit album downloads now preserve the original Spotify album name to avoid mangling names (e.g. reworked/remastered vs deluxe). Added explicit logging for both smart grouping and skipped grouping paths. The verification post-processing worker now checks an is_album_download flag in context and skips re-grouping when true, with fallback logging on errors.

These changes prevent unintended renaming of explicit album downloads and improve normalization of common title suffixes.
2026-02-25 15:39:01 -08:00
Broque Thomas
4bff57cb70 Handle edit versions, improve cleanup & thresholds
- matching_engine.py: Add 'single edit' and 'album edit' tokens and clarify radio edit comment so edit/cut variants are recognized as different cuts rather than being silently normalized away.
- database/music_database.py: Fix SQL param ordering by appending server_source to params; add a pre-step to strip "(with ...)" / "[with ...]" only when used inside brackets (so titles like "Stay With Me" are preserved); stop removing edit/version tokens in the generic cleanup and document that radio/single/album edits are treated as distinct by the similarity scorer to avoid incorrect matches.
- web_server.py: Increase DB match confidence threshold from 0.70 to 0.80 and update the runtime check accordingly.

These changes prevent edit/cut variants from being conflated with original recordings, improve title normalization for "with" featuring syntax in brackets, and fix a params ordering bug and a too-low match threshold.
2026-02-25 12:09:53 -08:00
Broque Thomas
35a03d6839 Add global watchlist override and UI
Introduce a global watchlist override feature and UI to control release/content filters across all watchlist artists. Backend: add /api/watchlist/global-config (GET/POST) for reading/updating global settings, validation to require at least one release type when override is enabled, and _apply_watchlist_global_overrides() to apply settings to WatchlistArtist objects. Scanners (manual and automatic) now call _apply_watchlist_global_overrides() and perform additional checks (_should_include_release, _should_include_track) to skip releases/tracks according to config. Frontend: add a Global Watchlist Settings modal, controls (release types, content filters, include-all shortcut), save/validation logic, banners/notices when global override is active, and integration into the watchlist modal and per-artist config. Styles: add supporting CSS for the modal and banners. Small cleanup/whitespace adjustments included.
2026-02-24 22:09:08 -08:00
Broque Thomas
7261b04950 Add hero cycling for similar artists
Add support for cycling hero/featured similar artists by introducing a last_featured timestamp and using it to prefer least-recently-featured artists. Changes include:

- DB migration: add _add_similar_artists_last_featured_column and call it during migrations to add a last_featured TIMESTAMP column (non-fatal on error).
- Query changes: get_top_similar_artists now excludes watchlist artists via a LEFT JOIN and wa.id IS NULL and orders results by last_featured (nulls first), then by last_featured asc, occurrence_count desc, and similarity_rank asc. The query aliasing was also added for clarity.
- New helper: mark_artists_featured updates similar_artists.last_featured = CURRENT_TIMESTAMP for shown artists.
- Web server: increase fetch limit to 50, remove random shuffle and instead take the top 10 (already ordered by cycling logic), and call mark_artists_featured to rotate featured artists.

These changes aim to provide deterministic, least-recently-shown cycling of hero artists while keeping watchlist artists out of recommendations.
2026-02-24 18:54:21 -08:00
Broque Thomas
f90d6567f7 Hydrabase: improve parsing add discography/tracks
Improve Hydrabase response handling and add discography/album track helpers. core/hydrabase_client.py: extract peer counts from stats.connectedPeers, handle new "response" key and stats-only or unexpected response shapes (return empty instead of wrapping), and add search_discography() and get_album_tracks() to map Hydrabase results into Album/Track objects. web_server.py: avoid redundant Hydrabase round-trips by passing precomputed hydrabase_counts into the background comparison worker; prefer Hydrabase for artist discography and album track import when active (with Spotify fallback); and route album-context searches to Hydrabase when configured. These changes reduce duplicate network calls and improve robustness against varied Hydrabase payloads.
2026-02-24 18:42:48 -08:00
Broque Thomas
44297a210b Add thread-safe MusicBrainz release cache
Introduce an in-memory, locked cache (_mb_release_cache and _mb_release_cache_lock) for MusicBrainz release lookups to ensure concurrent post-processing threads use a consistent release MBID and avoid splitting albums in players. Use the track artist (first artist in multi-artist fields) for artist MBID matching. Remove usage of AudioDB's per-track strMusicBrainzAlbumID as a fallback for MUSICBRAINZ_RELEASE_ID and add a comment explaining why (AudioDB links tracks to original albums which can differ per track and split albums). Includes safeguards around match_release calls and stores empty results in the cache to avoid repeated failed lookups.
2026-02-24 16:43:36 -08:00
Broque Thomas
fb7b373d71 Improve edition detection and completion logic
Prefer an album's stored track count when owned_tracks meets or exceeds it (avoids marking a complete standard edition as incomplete when an external source reports a larger deluxe count). Add more robust edition-detection regexes and generic catch-alls (parenthesized/bracketed text containing "edition") in music_database.py and web_server.py, and include silver/gold/platinum edition variants. Also tidy related regex handling and comments to improve matching for varied edition naming (e.g. "MMXI Special Edition").
2026-02-24 12:05:12 -08:00
Broque Thomas
a8e84fa20f Verify bytes before marking downloads complete
Add robust checks and stabilization before treating transfers as finished. The patch verifies bytesTransferred vs expected size in multiple places (monitor, API polling, YouTube handling, and batch status building) to avoid acting on premature "Completed/Succeeded" states. It adds per-poll file-claiming to prevent two contexts from grabbing the same file, introduces file-stability loops and retries when locating/moving files, replaces a blind 1s wait with repeated size checks, and fsyncs destination files after copy. Also introduces an _incomplete_warned flag to reduce log spam and ensures post-processing is triggered reliably once files are truly stable.
2026-02-24 08:52:47 -08:00
Broque Thomas
1283041836 Integrate Hydrabase P2P metadata client
Add Hydrabase support as an optional/dev metadata source and comparison tool.

- Add core/hydrabase_client.py: synchronous Hydrabase WebSocket client that normalizes results to Track/Artist/Album types and exposes raw access.
- Update config/settings.py: add hydrabase settings (url, api_key, auto_connect) and getter.
- Update web_server.py: integrate HydrabaseClient, initialize client alongside the existing HydrabaseWorker, add auto-reconnect using saved config, persist credentials on connect/disconnect, add endpoints for status and stored comparisons, background comparison runner (Hydrabase vs Spotify vs iTunes), and adapt multiple search endpoints to optionally use Hydrabase as the primary metadata source with fallbacks.
- Update web UI (webui/index.html, webui/static/script.js, webui/static/style.css): add network stats and source comparison UI, pre-fill saved credentials, show peer count, load/display comparisons, update disconnect behavior to disable dev mode, and add Hydrabase badge styling.

Behavioral notes: when dev mode + Hydrabase are active, searches can be served from Hydrabase and comparisons to Spotify/iTunes are run in background; when Hydrabase fails the code falls back to Spotify/iTunes. Saved Hydrabase credentials are persisted for auto-reconnect; disconnect disables dev mode and auto_connect.

Files touched: config/settings.py, core/hydrabase_client.py, web_server.py, webui/index.html, webui/static/script.js, webui/static/style.css.
2026-02-24 08:11:48 -08:00
Broque Thomas
1c34967fd3 Raise Spotify API interval; pause enrichments
Increase Spotify client rate limit and reduce API contention during watchlist scans. Changes:

- core/spotify_client.py: Bumped MIN_API_INTERVAL from 0.2s to 0.35s (~171 calls/min) to stay safely under Spotify's ~180/min limit.
- web_server.py: In start_watchlist_scan and automatic scan flow, pause spotify_enrichment_worker and itunes_enrichment_worker before scanning (tracking with _enrichment_was_running/_itunes_enrichment_was_running) and resume them in finally blocks; added console prints for pause/resume. This prevents enrichment workers from contending for API quota during long scans.
- webui/static/script.js: Improved enrichment status tooltip logic to prioritize explicit currentType and then fall back to completion-based inference with explicit branches for artists, albums, and tracks for clearer progress text.

These changes aim to avoid API rate violations and make scan progress display more predictable.
2026-02-23 21:22:27 -08:00
Broque Thomas
317d5c1770 Add Retag tool (DB, backend, frontend)
Introduce a new Retag tool to track and re-tag previously downloaded albums/singles. Changes include:

- Database: add migration hook and create retag_groups and retag_tracks tables, indexes, and many helper methods (add/find/update/delete groups & tracks, stats, trimming).
- Backend (web_server): capture completed album/single downloads into the retag tables, implement retag execution logic (_execute_retag) to fetch album metadata, match tracks, update tags, move files, download cover art, and update DB. Add thread-safe globals, executor, and REST endpoints for stats, listing groups, group tracks, album search, execute, status, and delete.
- Frontend (webui): add Retag Tool card, modals, search UI, JS to list groups/tracks, search albums, start retag operations, poll status, and update UI; include help content. Add CSS for modals and components.

The migration is invoked during DB init to ensure existing installations create the new tables. The tool caps stored groups (default 100) and avoids duplicate track entries.
2026-02-23 20:39:04 -08:00
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
Broque Thomas
28ca3f5ce5 album level search for wishlist albums/eps 2026-01-31 11:21:35 -08:00
Broque Thomas
258fd7a8ae Add album-level Soulseek search and fix unbounded log growth
When downloading an album/EP, perform a single album-level search on Soulseek to find a user with the complete album folder before falling back to per-track  search. This improves album completion rates and ensures consistent quality across tracks.
2026-01-30 12:46:52 -08:00
Broque Thomas
578d29f774 Add Spotify OAuth callback route to Flask app for reverse proxy support
Add a /callback Flask route (port 8008) that handles Spotify OAuth token exchange, mirroring the existing HTTPServer handler on port 8888. This allows users behind a reverse proxy to point their Spotify redirect_uri at the  main app.
2026-01-29 13:37:22 -08:00
Broque Thomas
681d4b3cd2 Fixed an issue where the wishlist wouldn't open if null value is detected 2026-01-27 17:04:31 -08:00
Broque Thomas
8248fab16e Fix iTunes wishlist and remove Single suffix
iTunes tracks now include album field for proper wishlist support.
  Strip " - Single" and " - EP" suffixes from iTunes album names.
2026-01-26 17:00:51 -08:00
Broque Thomas
375dcb8a19 Revert "Fix race condition preventing failed tracks from being added to wishlist"
This reverts commit c0c05c7b89.
2026-01-26 16:51:19 -08:00
Broque Thomas
c0c05c7b89 Fix race condition preventing failed tracks from being added to wishlist
Root cause: When album downloads completed, the frontend immediately closed
the modal and called /api/playlists/cleanup_batch, which deleted the batch
from memory. The wishlist processing thread (submitted to executor) would
then try to access the batch and fail silently because it was already deleted.

This explains why:
- Wishlist auto-processing worked (different timing/3-second delay)
- Manual "Add to Wishlist" button worked (different code path, before downloads)
- Album modal failed tracks didn't get added (race condition)

The fix prevents batch cleanup until wishlist processing completes:

Backend (web_server.py):
1. Mark wishlist_processing_started=True when submitting processing job
2. Mark wishlist_processing_complete=True when processing finishes
3. Block cleanup endpoint if processing in progress (return 202)

Frontend (script.js):
4. Handle 202 response and retry cleanup after 2-second delay

This eliminates the race condition while maintaining async processing and
ensuring all failed/cancelled tracks are properly added to the wishlist.
2026-01-26 16:44:31 -08:00
Broque Thomas
c728309a09 Fix automatic wishlist addition for failed/cancelled tracks in download modal
Issue: Tracks marked as failed or cancelled in the download missing tracks modal
were not being automatically added to the wishlist on completion, despite manual
"Add to Wishlist" button working correctly. Modal completed silently without
mentioning wishlist.

Root cause: Line 549 in web_server.py was calling _on_download_completed() with
wrong parameters - missing batch_id. This caused the completion handler to look
up wrong batch, return early, and never process failed tracks to wishlist.

The bug affected downloads that complete via monitor detection (YouTube, Soulseek)
since the monitor loop calls this function when it detects completed transfers.

Fix: Added missing batch_id parameter to _on_download_completed() call on line 549.

Changed:
  _on_download_completed(task_id, success=True)
To:
  _on_download_completed(batch_id, task_id, success=True)

Tested: Automatic wishlist addition now works correctly for failed/cancelled tracks.
2026-01-26 15:39:07 -08:00
Broque Thomas
087ead1a9a Fix Album Folder Organization for Recent Releases and Wishlist
Frontend: treat discover_album_ playlists as Album Downloads to ensure proper folder structure for Recent Releases.
Backend: inject explicit album context into Wishlist download tasks to force Artist/Album/ grouping instead of flat file handling.
2026-01-26 12:35:26 -08:00
Broque Thomas
6096049c50 Fix album tracks added as singles from Library page with iTunes source
Library page was using album data from discography listing while Artists page used track.album from API. With iTunes, these could have different track counts, causing different album_type classifications.

  - Updated handleAddToWishlist to use track.album data like Artists page does
  - Added album_type copying to owned releases in merge_discography_data
2026-01-25 17:44:17 -08:00
Broque Thomas
d7542b20c9 update version modal and readme 2026-01-25 10:33:32 -08:00
Broque Thomas
905e98016f Fix watchlist artist config and add image at insert when Itunes source. 2026-01-25 09:24:28 -08:00
Broque Thomas
59848acaf3 feat: dynamic source labels and enhanced connection testing
- Display "Apple Music" instead of "Spotify" in UI when iTunes is active source
- Enhanced connection test messages to indicate Spotify config/auth status
- Fixed similar artists requiring Spotify re-scan when Spotify becomes available
- Fixed hero slider buttons failing for iTunes-only artists
- Updated activity feed items to show correct source name dynamically
2026-01-25 00:29:33 -08:00
Broque Thomas
4f1dc2c15f force refetch similar artists when Spotify IDs missing
When Spotify is enabled after populating similar artists with only iTunes IDs, the freshness check now detects missing Spotify IDs and triggers a refetch. This fixes the Discover page not showing data when switching from iTunes-only mode.
2026-01-24 23:46:01 -08:00
Broque Thomas
3cb88669e3 Fix iTunes-only Discover page not loading data
- Add similar artists fetching to web UI scan loop
  - Add database migration for UNIQUE constraint on similar_artists table                                          - Add source-agnostic /api/discover/album endpoint for iTunes support
  - Fix NOT NULL constraint on discovery_recent_albums blocking iTunes albums
  - Add fallback to watchlist artists when no similar artists exist
  - Add /api/discover/refresh and /api/discover/diagnose endpoints
  - Add retry logic with exponential backoff for iTunes API calls
  - Ensure cache_discovery_recent_albums runs even when pool population skips
2026-01-24 21:48:17 -08:00
Broque Thomas
b0e34c6942 Update web_server.py 2026-01-24 14:53:43 -08:00
Broque Thomas
580d28cfb9 Update web_server.py 2026-01-24 14:35:54 -08:00
Broque Thomas
2170ffa99e Fix discovery modal fix button for iTunes source and ListenBrainz playlists
- Fix platform detection to include is_listenbrainz_playlist check when generating fix buttons
  - Update openDiscoveryFixModal to check both listenbrainzPlaylistStates and youtubePlaylistStates
  - Update searchDiscoveryFix to detect discovery_source and use appropriate search API
  - Add /api/itunes/search_tracks endpoint for manual track search when using iTunes source
  - Update selectDiscoveryFixTrack state lookup to include ListenBrainz

  Fix button now works correctly for all platforms (YouTube, ListenBrainz, Tidal, Beatport) with both Spotify
  and iTunes discovery sources.
2026-01-24 14:25:27 -08:00
Broque Thomas
84e6b01cc6 Add iTunes fallback to Tidal and Beatport discovery workers
- Update _search_spotify_for_tidal_track to accept use_spotify and itunes_client parameters for dual-source
  support
  - Update _run_tidal_discovery_worker to check is_spotify_authenticated() and fall back to iTunes when Spotify
  unavailable
  - Update _run_beatport_discovery_worker with same iTunes fallback pattern
  - Add discovery_source field to state and results for frontend awareness
  - Activity messages now indicate which provider (SPOTIFY/ITUNES) completed discovery

  All four discovery modals (YouTube, ListenBrainz, Tidal, Beatport) now support the dual-source architecture:
  Spotify preferred, iTunes fallback.
2026-01-24 14:17:09 -08:00
Broque Thomas
4319d440ee Add iTunes as resilient primary source for discovery features
Implement dual-source architecture where iTunes serves as always-available
  primary source and Spotify as preferred source when authenticated.

  - Make watchlist scans provider-aware (manual and auto paths)
  - Update discovery pool population to process both sources
  - Update recent albums caching for both sources
  - Create source-specific curated playlists (Fresh Tape, Archives)
  - Add on-the-fly iTunes ID resolution for similar artists
  - Add iTunes ID check to similar artists freshness validation
  - Fix sqlite3.Row compatibility in personalized playlists
  - Fix iTunes ISO 8601 date format parsing
  - Update API endpoints to serve source-appropriate data

  This ensures the app remains fully functional if Spotify becomes
  unavailable (rate limits, auth issues, bans) by seamlessly falling
  back to iTunes data that has been building in parallel.
2026-01-24 09:47:41 -08:00
Broque Thomas
1d14a8b987 Discover page itunes integration. Spotify and Itunes will have their own pool 2026-01-23 23:05:58 -08:00
Broque Thomas
1560726bbc rebuild discovery pool flow to allow multiprocessing of itunes and spotfiy. each getting their own pool. 2026-01-23 14:25:26 -08:00
Broque Thomas
fecd371b5e Add lazy loading for artist images across UI
Implements lazy loading of artist images in search results, artist pages, and similar artist bubbles to improve performance and user experience. Updates the iTunes client to prefer explicit album versions and deduplicate albums accordingly. Adds a new API endpoint to fetch artist images, and updates frontend logic to asynchronously fetch and display images where missing.
2026-01-23 07:38:03 -08:00
Broque Thomas
f12478ee70 Add iTunes fallback and improve artist/album handling
Adds iTunes fallback to SpotifyClient for search and metadata when Spotify is not authenticated. Updates album type logic to distinguish EPs, singles, and albums more accurately. Refactors watchlist database methods to support both Spotify and iTunes artist IDs. Improves deduplication and normalization of album names from iTunes. Updates web server and frontend to use new album type logic and support both ID types. Adds artist bubble snapshot example data.
2026-01-22 19:12:14 -08:00
Broque Thomas
f126cf7118 Add cross-provider support for watchlist artists
Introduces iTunes artist ID support to WatchlistArtist and database schema, enabling proactive backfilling of missing provider IDs (Spotify/iTunes) for watchlist artists. Updates WatchlistScanner to use MetadataService for provider-agnostic scanning and ID matching, and modifies web_server to support scans with either provider. Includes new database migration and update methods for iTunes and Spotify artist IDs.
2026-01-22 17:04:23 -08:00
Rafael Richard
3e733ab459 gracefully try to load configs from file into DB if the DB is fresh 2026-01-18 12:04:39 -05:00
Broque Thomas
f4bdb76c72 Reduce stuck flag timeout to 15 minutes
Changed stuck flag detection for wishlist and watchlist auto-processing from 2 hours to 15 minutes for faster recovery. Updated log messages and related logic to reflect the new timeout and display stuck duration in minutes. Also updated wishlist stats to use the improved stuck detection function.
2026-01-13 12:15:34 -08:00
Broque Thomas
fb56fbca5f Enhance wishlist auto-processing detection and UI feedback
Added backend support for reporting current auto-processing state and cycle in the wishlist stats API. Updated frontend to detect and handle auto-processing start, close modals, and notify users, as well as improved countdown timer updates and conflict handling when starting manual processing.
2026-01-13 10:00:17 -08:00
Broque Thomas
2ede3ddee7 Improve wishlist and watchlist auto-scheduling robustness
Refactored wishlist and watchlist auto-processing scheduling to use atomic timer updates and retry logic, preventing stuck timers and improving reliability. Removed excessive debug logging and improved error handling throughout related functions. Added checks to prevent concurrent wishlist processing and ensured timer state is managed consistently.
2026-01-13 09:42:07 -08:00
Broque Thomas
af5cc33c34 Deduplicate wishlist tracks during sanitization and filtering
Added logic to remove duplicate tracks based on their Spotify track ID during both the sanitization and category filtering steps in get_wishlist_tracks(). This ensures the frontend receives a deduplicated list and improves data consistency. A warning is printed if duplicates are found and removed during sanitization.
2026-01-11 17:50:10 -08:00
Broque Thomas
030757fe12 Improve wishlist deduplication and timer reset logic
Enhanced wishlist and watchlist processing to deduplicate tracks during both sanitization and filtering, preventing duplicate downloads and processing. Added explicit resets of next run timer variables to ensure accurate scheduling after each cycle. Updated countdown timer display in the UI to show '0s' when timer reaches zero instead of hiding it.
2026-01-11 15:46:47 -08:00
Broque Thomas
a8766828d9 Add content type filters for watchlist artists
Introduces new filters for live versions, remixes, acoustic versions, and compilation albums to the watchlist artist configuration. Updates the database schema, backend API, and web UI to support these options, allowing users to customize which content types are included for each artist in their watchlist.
2026-01-11 01:33:39 -08:00
Broque Thomas
d9a8162fb9 Fix context key consistency and add metadata write locks
Standardizes the use of extract_filename() for context keys to ensure consistent lookup and storage across download and post-processing flows. Increases fuzzy match thresholds in file search to reduce false positives. Adds per-file threading locks to prevent concurrent metadata writes, improving thread safety during metadata enhancement.
2026-01-09 23:14:15 -08:00
Broque Thomas
05aa731381 Sanitize context values in path template substitution
Introduced _sanitize_context_values to clean all string values in the context dictionary before applying path templates. This prevents unsafe characters (like '/') in metadata (e.g., artist names) from creating unintended path components during template substitution.
2026-01-09 08:20:11 -08:00
Broque Thomas
d4f1b3ed9a Add fallback for legacy config loading
Adds a check for the presence of 'load_config' in config_manager. If not found, uses a fallback method to support older settings.py files, improving compatibility with legacy Docker volumes.
2026-01-07 08:47:30 -08:00
Broque Thomas
c7f2f95450 Update to SoulSync v1.3 with YouTube engine and fixes
Bump version to 1.3 and update the version info modal to highlight the new YouTube download engine, Docker and system reliability fixes, and general improvements. Update the web UI to display v1.3 and apply minor formatting and accessibility improvements throughout index.html.
2026-01-06 22:53:21 -08:00
Broque Thomas
9aa2ac463a Add Docker path resolution for downloads
Updated _find_downloaded_file to resolve download paths for Docker environments, ensuring compatibility with host-mounted directories. Also updated README to clarify YouTube integration and sources.
2026-01-06 21:13:16 -08:00
Broque Thomas
ec1d193093 Improve Tidal auth instructions for remote and Docker users
Enhanced the Tidal authentication page to provide clearer, step-by-step instructions for users accessing the app remotely or via Docker. The new flow helps users update the callback URL as needed, improving the authentication experience in non-localhost environments.
2026-01-06 20:03:23 -08:00
Broque Thomas
c251edc709 Improve stuck flag recovery and album classification
Adds critical fixes to reschedule timers after stuck flag recovery for wishlist and watchlist processes, preventing deadlocks and ensuring continuity. Refines album classification by prioritizing Spotify's album_type over track count heuristics, and ensures album_type is included in API responses. Updates frontend logic to pass and use fresh album/artist context for discover_album modals, improving metadata accuracy and display.
2026-01-06 16:04:05 -08:00
Broque Thomas
74e5c3d4d7 Revert "Refactor wishlist auto-processing to use heartbeat thread"
This reverts commit 238abb2aea.
2026-01-06 15:20:40 -08:00
Broque Thomas
7d22671628 Revert "Improve album classification and modal data handling"
This reverts commit b32871cf28.
2026-01-06 15:20:30 -08:00
Broque Thomas
6b7877edab Revert "Refactor watchlist auto-scanning to use heartbeat thread"
This reverts commit 5f03641b7d.
2026-01-06 15:19:52 -08:00
Broque Thomas
5f03641b7d Refactor watchlist auto-scanning to use heartbeat thread
Replaces the timer-based automatic watchlist scanning with a background heartbeat thread for improved reliability and robust retry logic. The new system handles scheduling, conflict detection, and error recovery, while UI countdown updates are decoupled from actual scheduling.
2026-01-06 14:45:05 -08:00
Broque Thomas
b32871cf28 Improve album classification and modal data handling
Refines album/single classification in get_wishlist_stats by considering explicit 'album' type for short multi-track releases. Also updates openDownloadModalForRecentAlbum to include total_tracks and album_type in album data for more accurate processing.
2026-01-06 14:27:14 -08:00
Broque Thomas
238abb2aea Refactor wishlist auto-processing to use heartbeat thread
Replaces the previous daisy-chained timer logic for automatic wishlist processing with a robust background heartbeat thread. This ensures reliable scheduling, simplifies stopping/starting, and improves error handling. UI countdown updates remain compatible with existing call sites.
2026-01-06 12:03:38 -08:00
Broque Thomas
97161856e7 Update web_server.py 2026-01-06 11:02:38 -08:00
Broque Thomas
31944b7b2d Update web_server.py 2026-01-06 10:33:55 -08:00
Broque Thomas
9d275488e2 Add config reload support and improve config loading
Introduces a reload_config method to SpotifyClient and refactors ConfigManager to support reloading configuration from a file. Updates web_server.py to use the new config loading mechanism, ensuring configuration is loaded into the existing singleton instance and SpotifyClient is properly re-initialized after settings changes.
2026-01-06 09:29:11 -08:00
Broque Thomas
42a4ccfc24 Update data directory mapping and config path handling
Changed Docker volume mapping from './database' to './data' in README-Docker.md for SQLite files. Updated web_server.py to support config path override via SOULSYNC_CONFIG_PATH environment variable, improving Docker compatibility.
2026-01-06 07:17:35 -08:00
Broque Thomas
e5457c0214 Improve title normalization in discography merge
Updated the normalize_title function to remove unicode accents using NFD normalization and filtering non-spacing marks. This enhances title comparison by handling accented characters more robustly.
2026-01-06 07:10:04 -08:00
Broque Thomas
ddd2ebdb13 Improve YouTube download handling and shutdown safety
Adjusts matching weights for YouTube sources to rely more on title and duration, adds a shutdown callback to the YouTube client to prevent new downloads during shutdown, and enhances post-processing to reliably resolve actual YouTube file paths. Improves error handling for file removal, ensures no new batch downloads start during shutdown, and refines download monitoring to trigger post-processing on completed YouTube downloads. Also increases YouTube download retries and improves logging for debugging.
2026-01-05 18:08:43 -08:00
Broque Thomas
af2265a68a Improve YouTube download handling and add Spotify search API
Enhanced download status and post-processing logic to properly handle YouTube downloads alongside Soulseek transfers. Improved file organization for simple downloads, moving singles without album info directly to the Transfer root. Added a new generic Spotify search API endpoint. Updated frontend logic to correctly display YouTube download titles and results, and improved filename parsing for YouTube tracks.
2026-01-04 20:16:08 -08:00
Broque Thomas
61e822bf6f Add robust YouTube streaming support and improve format checks
Enhances streaming logic to better support YouTube as a download source, including improved filename handling, fuzzy file matching, and search query generation. Updates format checks in the frontend to skip them for YouTube (always MP3). Refactors backend to use a unified download status API for both Soulseek and YouTube, and improves service test messaging based on the active download mode.
2026-01-04 12:30:35 -08:00
Broque Thomas
88f08c2d0e Update web_server.py 2026-01-04 00:53:02 -08:00
Broque Thomas
c94b1d2f5b Add hybrid Soulseek/YouTube download orchestration - TESTING
Introduces a DownloadOrchestrator class to route downloads between Soulseek and YouTube based on user-configurable modes (Soulseek only, YouTube only, Hybrid with fallback). Updates web server and UI to support new download source settings, including hybrid mode options and YouTube confidence threshold. Refactors YouTube client for thread-safe download management and bot detection bypass. Ensures quality filtering is skipped for YouTube results and improves file matching and post-processing logic for YouTube downloads.
2026-01-03 22:20:36 -08:00
Broque Thomas
a8935ae8f5 Add $year variable support to file path templates
Introduces the $year variable for album, single, and playlist path templates, allowing users to include the release year in file organization. Updates the backend to extract and provide the year, adjusts the web UI to document the new variable, and updates validation logic to recognize $year as valid in templates.
2026-01-02 08:24:43 -08:00
Broque Thomas
238788ca78 Add error handling to wishlist rescheduling
Wrapped calls to schedule_next_wishlist_processing in try/except blocks to prevent timer thread termination if scheduling fails. This improves robustness and ensures continuity of automatic wishlist processing cycles even in the event of scheduling errors.
2026-01-01 09:24:09 -08:00
Broque Thomas
b426d9d1e4 Enhance search UI with dynamic glow and premium design
Increases Spotify search result limits for artists and albums. Adds dynamic glow effects to artist, album, and track cards in the UI using image-based colors. Significantly upgrades the CSS for enhanced dropdowns, cards, and lists with a premium, vibrant, and responsive design.
2025-12-30 22:29:50 -08:00
Broque Thomas
2d61b0d076 Update to v1.2 with enhanced search and matching
Bumps SoulSync version to 1.2, introducing a unified multi-source search system, a major overhaul of the matching engine for improved accuracy, and significant wishlist and quality-of-life improvements. Updates version info and UI elements to reflect the new release and its features.
2025-12-30 19:47:54 -08:00
Broque Thomas
d64bcaf7ab Update web_server.py 2025-12-30 13:21:29 -08:00
Broque Thomas
a07019851e Update web_server.py 2025-12-30 10:39:22 -08:00
Broque Thomas
c6d3475185 Update web_server.py 2025-12-30 10:34:20 -08:00
Broque Thomas
420044387e Add streaming for enhanced search tracks
Introduces a backend API endpoint and frontend logic to allow users to stream individual tracks directly from enhanced search results. Updates the UI to include a play button for each track, adjusts the search mode toggle to default to enhanced search, and refines related styles for improved user experience.
2025-12-29 18:13:50 -08:00
Broque Thomas
d20609c33b Add search bubble snapshot and download tracking system
Implements a persistent search bubble system for tracking album and track downloads in enhanced search. Adds backend API endpoints for saving and hydrating search bubble snapshots, frontend state management for search download bubbles, UI for displaying and managing active/completed downloads, and associated styles for search bubble cards. This enables users to resume and manage search downloads across page refreshes.
2025-12-29 14:33:59 -08:00
Broque Thomas
a431ec0bd6 Improve enhanced search UI and increase track results
Increased the number of tracks returned by enhanced search from 10 to 20. Updated CSS for enhanced search sections and artist cards to improve visual appearance, spacing, and responsiveness, including larger artist images, new backgrounds, and better layout for various screen sizes.
2025-12-29 12:37:05 -08:00
Broque Thomas
a167a00a0a Add enhanced search with categorized dropdown UI
Implements an enhanced search endpoint in the backend that unifies Spotify and local database results, returning categorized artists, albums, and tracks. Updates the frontend with a new dropdown overlay for live search, debounced input, categorized result rendering, and direct integration with the main results area for album/track selection. Adds new CSS for the dropdown and result cards, and updates the Track dataclass to include image URLs for richer UI display.
2025-12-29 09:56:32 -08:00
Broque Thomas
14a5944ae1 Add enhanced search mode UI and retry logic for downloads
Introduces a search mode toggle in the downloads UI, allowing users to switch between basic and enhanced search modes. Adds new HTML structure, JavaScript logic, and CSS styles for the enhanced search interface (currently placeholder functionality). In the backend, implements a retry system for file discovery after download completion to handle race conditions, with cleanup of stale retry attempts to prevent memory leaks.
2025-12-28 22:24:23 -08:00
Broque Thomas
31903990fa ensure listenbrainz is saved to db 2025-12-18 09:19:39 -08:00
Broque Thomas
cbe44faa1b Update web_server.py 2025-12-16 11:11:20 -08:00
Broque Thomas
b50f6c8d9f Add limit parameter to wishlist tracks API
Introduces an optional 'limit' query parameter to the /api/wishlist/tracks endpoint for improved performance when fetching tracks. Updates the frontend to use this parameter, limiting requests to 50 tracks per category when fetching album and single covers.
2025-12-13 08:32:42 -08:00
Broque Thomas
4587022877 Remove source prefixes from playlist names
Eliminates hardcoded source prefixes (e.g., [Tidal], [YouTube], [Beatport], [ListenBrainz]) from playlist names in both backend and frontend code. Source detection is now handled via virtualPlaylistId prefixes, improving consistency and simplifying playlist name handling throughout the application.
2025-12-12 12:36:45 -08:00
Broque Thomas
8f30f0ab91 Enhance matched downloads with full Spotify metadata
Adds robust track-to-track matching for album and single downloads, enabling enhanced metadata enrichment using Spotify data. Updates both backend and frontend to support matching Soulseek tracks to Spotify tracks, sending full Spotify track objects for improved organization and post-processing. Simplifies context handling for simple downloads and removes legacy flags, ensuring more accurate and consistent metadata for matched downloads.
2025-12-12 07:13:53 -08:00
Broque Thomas
20d9ac6ec4 move search page downloads to transfer folder 2025-12-11 17:35:20 -08:00
Broque Thomas
c6c0a30ced Improve Spotify auth instructions for remote access
Adds detection for remote access during Spotify authentication and provides step-by-step instructions for users connecting remotely, including guidance on modifying the callback URL. Enhances user experience with styled HTML and a copy-to-clipboard button for the host IP.
2025-12-11 14:21:56 -08:00