Commit graph

1003 commits

Author SHA1 Message Date
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
268735eeff Update README.md 2026-02-01 12:26:52 -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
da44278f12 add fourth search query for enhanced matching with both a cleaned and artist removed search. 2026-01-31 20:53:56 -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
abc510b9b9 Fix album detection for split albums and false positive matching
Merge split album entries (e.g. Navidrome assigning different IDs to the same album) by grouping on title+year instead of album ID, so track counts are correctly combined across all entries. Also add a minimum title similarity floor (0.6) to prevent a perfect artist match from carrying unrelated albums over the confidence threshold.
2026-01-29 15:12:13 -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
622e95e4df fix tidal auth issue on restart 2026-01-29 11:33:29 -08:00
Broque Thomas
56222b799a Fix ListenBrainz playlists not showing when tables don't exist yet
ListenBrainzManager opens its own raw sqlite3 connection, bypassing MusicDatabase initialization. If it runs   before MusicDatabase creates the tables, all queries fail with "no such table: listenbrainz_playlists". Added _ensure_tables() to ListenBrainzManager init that runs CREATE TABLE IF NOT EXISTS for both ListenBrainz tables — a  no-op when they already exist, but creates them if MusicDatabase hasn't run yet.
2026-01-29 10:16:19 -08:00
Broque Thomas
a74596cdd6 Fix Quality Scanner for Navidrome & expand ListenBrainz playlist limit
- Expose suffix, bitRate, and path fields on NavidromeTrack from the Subsonic API response
  - Add fallback in insert_or_update_media_track() to populate file_path and bitrate for Navidrome tracks, fixing the Quality Scanner
   returning 0 results
  - Increase ListenBrainz playlist cache limit from 4 to 25 per type
  - Add sub-tab grouping in the Recommendations tab (Weekly Jams, Weekly Exploration, Top Discoveries, etc.)
2026-01-29 09:24:55 -08:00
Broque Thomas
6750c20dc4 display all listenbrainz playlsts in unique categories. 2026-01-29 08:55:19 -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
634e5b6b9c organization 2026-01-27 12:23:19 -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
ceafcdc49f Revert "Fix iTunes wishlist and remove ' - Single' suffix"
This reverts commit d9685364c1.
2026-01-26 16:51:12 -08:00
Broque Thomas
fcbead51d8 Merge branch 'main' of https://github.com/Nezreka/SoulSync 2026-01-26 16:45:18 -08:00
Broque Thomas
d9685364c1 Fix iTunes wishlist and remove ' - Single' suffix
Two fixes for iTunes integration:

1. iTunes failed tracks now properly added to wishlist
   - Root cause: iTunes tracks had no 'album' field (unlike Spotify)
   - Fix: Added album information to each track in get_album_tracks()
   - Tracks now include: album id, name, images, release_date

2. Remove ' - Single' suffix from iTunes album names
   - Root cause: iTunes API includes ' - Single' in collectionName
   - Fix: Added _clean_album_name() helper method
   - Strips ' - Single' and ' - EP' suffixes from all album names
   - Applied to all 6 locations where collectionName is used

Both Spotify and iTunes sources now work identically for wishlist
auto-processing when tracks fail or are cancelled.
2026-01-26 16:44:42 -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
6bf43f33c2 Fix iTunes wishlist and remove ' - Single' suffix
Two fixes for iTunes integration:

1. iTunes failed tracks now properly added to wishlist
   - Root cause: iTunes tracks had no 'album' field (unlike Spotify)
   - Fix: Added album information to each track in get_album_tracks()
   - Tracks now include: album id, name, images, release_date

2. Remove ' - Single' suffix from iTunes album names
   - Root cause: iTunes API includes ' - Single' in collectionName
   - Fix: Added _clean_album_name() helper method
   - Strips ' - Single' and ' - EP' suffixes from all album names
   - Applied to all 6 locations where collectionName is used

Both Spotify and iTunes sources now work identically for wishlist
auto-processing when tracks fail or are cancelled.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 16:39:00 -08:00
Broque Thomas
c05486b49c 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.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-26 16:19:51 -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
9af0be1300 fixed issue where legacy cold was called. 2026-01-26 13:37:42 -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
66e906fece fix: enable album folder organization for Discover page downloads
Updated startMissingTracksProcess in script.js to recognize discover_album_ IDs as album downloads. This ensures that albums downloaded from the Discover page (e.g., Recent Releases) are correctly organized into "Artist/Album" folders instead of being treated as flat playlists.
2026-01-26 11:42:45 -08:00
Broque Thomas
db2400b42a fix navidrome sync issue where duplicate playlists are created 2026-01-26 10:25:55 -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
b9f2344f0f lookup artist name on track/album pull to ensure clean data.
Great for multi artist scenarios
2026-01-25 15:08:08 -08:00
Broque Thomas
48b914a4be Update spotify_client.py 2026-01-25 13:51:29 -08:00
Broque Thomas
61618c2fc7
Merge pull request #126 from Nezreka/itunes
Apple Music added as fallback metadata source if Spotify is not available. Spotify is always preferred if it is available with its richer data responses. Will easily swap from Spotify to Apple Music on the fly if you are rate limited by Spotify or worse, temp banned. 

Apple Music and Spotify will now each have their own discovery pool for the discover page. Both will always be updated on every watchlist scan so long as Spotify is authorized, otherwise only Apple Music data is pulled.

**Known issues:**

Any artist image pulled while Apple Music is the primary source will only pull album art for that artist since they do not provide artist images :(  This can, very rarely, lead to cases where the album image that is pulled could have another another artist displayed if it's some collab single, EP or something. Seen it happen once with an indie artist so it's possible.

Looking for a great api / website to parse easily specifically for artist names and a huge db to pull from.
2026-01-25 11:07:50 -08:00
Broque Thomas
f4365fa836 Fix artist image not appearing for artist bubble, artist bubble modal or when an artist is selected in search. 2026-01-25 10:51:02 -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
47c45ddea7 feat: dynamic music source labels in discovery modals
- Added global currentMusicSourceName variable to track active source
- Updated discovery modal to show "Apple Music" when iTunes is active
- Replaced hardcoded "Spotify" in modal titles, headers, and descriptions
- Discovery modals now automatically reflect the correct music source (Spotify/Apple Music)
2026-01-25 00:35:16 -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
4cbb3c952b Fix 'view discog' button on discover hero slider when itunes is primary source. 2026-01-24 23:58:08 -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