Commit Graph

553 Commits

Author SHA1 Message Date
7abc26c069 Fix album detail endpoint to use correct parameter
- Change album endpoint from ?id= to ?f= to match API spec
- Album search parsing is correct (data.albums.items)
- Album detail parsing is correct (data with items array)
2026-02-06 01:45:10 -05:00
a2e9021100 Fix artist detail parsing to handle missing artist property
- Add TryGetProperty check for artist field in albums response
- Log response keys when artist data not found for debugging
- Improves error handling when API returns albums without artist field
2026-02-06 01:39:43 -05:00
5f22fb0a3b Integrate Spotify lyrics sidecar service
- Add spotify-lyrics-api sidecar container to docker-compose
- Replace direct Spotify API lyrics code with sidecar API calls
- Update SpotifyLyricsService to use sidecar exclusively
- Add LyricsApiUrl setting to SpotifyApiSettings
- Update prefetch to try Spotify lyrics first, then LRCLib
- Remove unused direct API authentication and parsing code
2026-02-06 01:24:49 -05:00
a3d1d81810 Add Spotify lyrics sidecar service and integrate with prefetch
- Add spotify-lyrics-api container to docker-compose
- Update SpotifyLyricsService to use sidecar API
- Prefetch now tries Spotify lyrics first (using track ID), then LRCLib
- Add SPOTIFY_LYRICS_API_URL setting
- Sidecar handles sp_dc cookie authentication automatically
2026-02-06 01:21:30 -05:00
2dd7020a61 Add logging to Spotify lyrics search for better debugging
Show when Spotify search is skipped, fails, or finds no matches
2026-02-06 00:59:23 -05:00
e36e685bee Fix lyrics cache key mismatch between prefetch and lookup
Use same artist format (comma-separated) in prefetch as LrclibService
to ensure cached lyrics are found during playback
2026-02-06 00:58:13 -05:00
7ff6dbbe7a Prioritize local Jellyfin lyrics over LRCLib in prefetch
Some checks failed
CI / build-and-test (push) Has been cancelled
- Check for embedded lyrics in local Jellyfin tracks before fetching from LRCLib
- Remove previously cached LRCLib lyrics when local lyrics are found
- Prevents unnecessary API calls and respects user's embedded lyrics
- Tracks with local lyrics are counted as 'cached' in prefetch stats
2026-02-05 15:09:59 -05:00
e0dbd1d4fd feat: Add lyrics ID mapping system, fix playlist display, enhance track view
- Add complete lyrics ID mapping system with Redis cache, file persistence, and cache warming
- Manual lyrics mappings checked FIRST before automatic search in LrclibService
- Add lyrics status badge to track view (blue badge shows when lyrics are cached)
- Enhance search links to show 'Search: Track Title - Artist Name'
- Fix Active Playlists tab to read from .env file directly (shows all 18 playlists now)
- Add Map Lyrics ID button to every track with modal for entering lrclib.net IDs
- Add POST /api/admin/lyrics/map and GET /api/admin/lyrics/mappings endpoints
- Lyrics mappings stored in /app/cache/lyrics_mappings.json with no expiration
- Cache warming loads lyrics mappings on startup
- All mappings follow same pattern as track mappings (Redis + file + warming)
2026-02-05 14:58:57 -05:00
328a6a0eea Add lyrics completion bar per playlist showing percentage of tracks with cached lyrics
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-02-05 12:44:11 -05:00
9abb53de1a Fix search to use SquidWTF HiFi API with round-robin base URLs, capitalize provider names in UI, and widen tracks modal to 90% 2026-02-05 12:35:33 -05:00
349fb740a2 Fix scrobbling: track playing item in session and send proper PlaybackStopped data on cleanup 2026-02-05 11:56:26 -05:00
b604d61039 Adjust modal size to 75% width and 65% height, call PlaybackStopped when cleaning up sessions 2026-02-05 11:53:35 -05:00
3b8d83b43e Add lyrics prefetch endpoint and UI button: prefetch lyrics for individual playlists with progress feedback 2026-02-05 11:45:36 -05:00
8555b67a38 Fix external track streaming: normalize provider names to lowercase (squidwtf, deezer, qobuz) 2026-02-05 11:40:45 -05:00
629e95ac30 Improve logging: clarify search vs manual mappings, show manual mapping counts in final log 2026-02-05 11:38:26 -05:00
2153a24c86 Make modal wider (800px) and taller (90vh) to fit buttons side by side 2026-02-05 11:35:09 -05:00
1ddb3954f3 Add missing using statement for IMusicMetadataService 2026-02-05 11:27:22 -05:00
3319c9b21b Fix external mapping: add Map to External button for external tracks, fetch metadata from provider, set searchQuery for missing tracks 2026-02-05 11:23:01 -05:00
8966fb1fa2 Add lyrics prefetching for injected playlists with file cache
New LyricsPrefetchService automatically fetches lyrics for all tracks in
Spotify injected playlists. Lyrics are cached in Redis and persisted to
disk for fast loading on startup.

Features:
- Prefetches lyrics for all playlist tracks on startup (after 3min delay)
- Daily refresh to catch new tracks
- File cache at /app/cache/lyrics for persistence
- Cache warming on startup loads lyrics from disk into Redis
- Rate limited to 2 requests/second to be respectful to LRCLIB API
- Logs fetched/cached/missing counts per playlist

Benefits:
- Instant lyrics availability for playlist tracks
- Survives container restarts
- Reduces API calls during playback
- Better user experience with pre-loaded lyrics
2026-02-05 11:15:42 -05:00
3b24ef3e78 Fix: Fetch full metadata for manual external mappings
Manual external mappings now fetch complete track metadata from the
provider (SquidWTF) instead of using minimal Spotify metadata. This
ensures proper IDs, artist IDs, album IDs, cover art, and all metadata
needed for playback.

Also fixed admin UI to properly detect manual external mappings so
tracks show as 'External' instead of 'Missing'.

Changes:
- Fetch full Song metadata using GetSongAsync when manual mapping exists
- Fallback to minimal metadata if fetch fails
- Admin controller now checks isManualMapping flag to set correct status
- Tracks with manual external mappings now show proper provider badge
2026-02-05 11:13:26 -05:00
dbeb060d52 Fix: Manual external mappings now work correctly for playback
Two critical fixes:
1. External songs from manual mappings now get proper IDs (ext-{provider}-song-{id})
   - Previously had no ID, causing 'dummy' errors in Jellyfin
   - Now follows same format as auto-matched external tracks

2. Admin UI now correctly shows manual external mappings as available
   - Previously showed as 'Missing' even after mapping
   - Now properly detects manual external mappings and shows provider badge

This fixes the 400 Bad Request errors when trying to play manually mapped tracks.
2026-02-05 11:12:15 -05:00
2155a287a5 Add manual mapping indicators and search button for missing tracks
- Manual mappings now show a blue 'Manual' badge next to the track status
- Added search button (🔍) for missing tracks to help find them
- Backend now returns isManualMapping, manualMappingType, and manualMappingId
- Frontend displays manual mapping indicators for both local and external tracks
- Missing tracks now show a search link to help locate them on SquidWTF
2026-02-05 10:20:31 -05:00
cb57b406c1 Fix: Manual external mappings now properly included in playlist cache
The bug was in PreBuildPlaylistItemsCacheAsync - when a manual external
mapping was found, it was added to matchedTracks but the code used
'continue' to skip to the next track WITHOUT adding it to finalItems.

This meant external manual mappings were never included in the playlist
cache that gets served to clients.

The fix converts the external song to Jellyfin item format and adds it
to finalItems before continuing, ensuring manual external mappings are
properly included in the pre-built playlist cache.
2026-02-05 10:07:57 -05:00
e91833ebbb Fix variable name conflict and change cache logs to DEBUG level
- Fixed CS0136 error: renamed 'doc' to 'extDoc' in AdminController to avoid variable name conflict
- Changed all Redis cache logs (HIT/MISS/SET) to DEBUG level instead of suppressing
- This allows cache logs to be visible in docker logs but not as noisy at INFO level
2026-02-05 09:59:28 -05:00
2e1577eb5a Fix external mapping deserialization and suppress cache MISS logs
- Fixed RuntimeBinderException when processing external mappings by replacing dynamic with JsonDocument parsing
- Suppressed cache MISS logs for manual/external mappings (they're expected to be missing most of the time)
- Only log manual/external mapping HITs at INFO level, other cache operations at DEBUG level
- Applied fix to SpotifyTrackMatchingService (2 locations) and AdminController (2 locations)
2026-02-05 09:57:07 -05:00
7cb722c396 Fix HasValue method to handle JsonElement properly
- Changed parameter type from dynamic? to object? to avoid runtime binding issues
- Added check for JsonValueKind.Undefined in addition to Null
- Fixes crash when checking external mappings that return JsonElement
- Applied fix to both AdminController and SpotifyTrackMatchingService
2026-02-05 09:40:39 -05:00
9dcaddb2db Make manual mappings permanent and persist to file
- Manual mappings now have NO expiration (permanent in Redis)
- Save manual mappings to /app/cache/mappings/*.json files
- Load manual mappings on startup via CacheWarmingService
- Manual mappings are first-order and survive restarts/cache clears
- User decisions are now truly permanent
2026-02-05 09:33:37 -05:00
5766cf9f62 Delete file caches when manual mappings are created
- When mapping a track to local or external, delete both Redis and file caches
- This forces the matched tracks cache to rebuild with the new mapping
- Ensures manual mappings are immediately reflected in playlists
2026-02-05 09:31:07 -05:00
a12d5ea3c9 Fix excessive track matching and reduce HTTP logging noise
- Added 5-minute cooldown between matching runs to prevent spam
- Improved cache checking to skip unnecessary matching
- Persist matched tracks cache to file for faster restarts
- Cache warming service now loads matched tracks on startup
- Suppress verbose HTTP client logs (LogicalHandler/ClientHandler)
- Only run matching when cache is missing or manual mappings added
2026-02-05 09:30:00 -05:00
25bbf45cbb Fix memory leak in ActiveDownloads dictionary
- Changed ActiveDownloads from Dictionary to ConcurrentDictionary for thread safety
- Added automatic cleanup of completed downloads after 5 minutes
- Added automatic cleanup of failed downloads after 2 minutes
- This fixes the 929MB -> 10MB memory issue where downloads were never removed from tracking
2026-02-05 09:19:32 -05:00
3fd13b855d Fix RuntimeBinderException, add session cleanup, memory stats endpoint, and fix all warnings
- Fixed RuntimeBinderException when comparing JsonElement with null
- Added HasValue() helper method for safe dynamic type checking
- Implemented intelligent session cleanup:
  * 50 seconds after playback stops (allows song changes)
  * 3 minutes of total inactivity (catches crashed clients)
- Added memory stats endpoint: GET /api/admin/memory-stats
- Added sessions monitoring endpoint: GET /api/admin/sessions
- Added GetSessionsInfo() to JellyfinSessionManager for debugging
- Fixed all nullable reference warnings
- Reduced warnings from 10 to 0
2026-02-05 09:17:40 -05:00
d9c0b8bb54 Add separate 'Map to External' button for missing tracks
Some checks failed
CI / build-and-test (push) Has been cancelled
- Missing tracks now show both 'Map to Local' and 'Map to External' buttons
- External tracks continue to show only 'Map to Local' button
- Added openExternalMap() function that opens modal in external mapping mode
- Added event listeners for .map-external-btn buttons
- External mapping button styled with warning color to distinguish from local mapping
- Users can now easily choose between mapping to Jellyfin tracks or external provider IDs
2026-02-05 00:26:02 -05:00
400ea31477 Fix missing track labeling and add external manual mapping support
- Fixed syntax errors in AdminController.cs (missing braces, duplicate code)
- Implemented proper track status logic to distinguish between:
  * Local tracks: isLocal=true, externalProvider=null
  * External matched tracks: isLocal=false, externalProvider='SquidWTF'
  * Missing tracks: isLocal=null, externalProvider=null
- Added external manual mapping support for SquidWTF/Deezer/Qobuz IDs
- Updated frontend UI with dual mapping modes (Jellyfin vs External)
- Extended ManualMappingRequest class with ExternalProvider + ExternalId fields
- Updated SpotifyTrackMatchingService to handle external manual mappings
- Fixed variable name conflicts and dynamic argument casting issues
- All tests passing (225/225)

Resolves issue where missing tracks incorrectly showed provider name instead of 'Missing' status.
2026-02-05 00:15:23 -05:00
b1cab0ddfc Fix missing track labeling and add external manual mapping support
FIXES:
- Fixed track display logic to properly distinguish between external matched and missing tracks
- Missing tracks now show 'Missing' instead of incorrectly showing provider name
- Added support for manual external provider mappings (e.g., SquidWTF IDs)

CHANGES:
- Extended ManualMappingRequest to support ExternalProvider + ExternalId
- Updated SaveManualMapping to handle both Jellyfin and external mappings
- Updated SpotifyTrackMatchingService to check for external manual mappings
- Updated AdminController track details to use proper missing/matched logic

NOTE: Build currently has syntax errors that need to be fixed, but core logic is implemented.
2026-02-04 23:56:21 -05:00
7cba915c5e Fix authentication issues in SpotifyTrackMatchingService
- Fixed SpotifyTrackMatchingService to use GetJsonAsyncInternal for authenticated requests
- This resolves 401 Unauthorized errors when fetching existing playlist tracks
- Should prevent unnecessary rematching when cache is warm
- Fixes 'No Items found in Jellyfin playlist response' warnings

The service was using GetJsonAsync with null headers, causing 401 errors.
Now uses server API key authentication for internal operations.
2026-02-04 23:44:45 -05:00
dfd7d678e7 Add internal API method and fix playlist count authentication
- Added GetJsonAsyncInternal method to JellyfinProxyService for server-side requests
- Uses server API key instead of client tokens for internal operations
- Updated UpdateSpotifyPlaylistCounts to use internal method with proper authentication
- This should resolve 401 Unauthorized errors when updating playlist counts

Now Spotify playlists should show correct track counts without authentication issues.
2026-02-04 23:42:16 -05:00
4071f6d650 Fix authentication issue in UpdateSpotifyPlaylistCounts
- Added UserId parameter to playlist items request to avoid 401 Unauthorized
- Fixed JsonElement casting issue that was causing InvalidCastException
- This should resolve both the authentication error and the track count update

Now Spotify playlists should show correct track counts without authentication errors.
2026-02-04 23:40:13 -05:00
d045b33afd Fix Spotify playlist track counts to include external tracks
- Changed totalAvailableCount calculation to include both local and external matched tracks
- Updated logging to show breakdown of local vs external tracks
- This fixes Discover Weekly and other external-only playlists showing 0 tracks in clients

Now playlists with all external tracks will show correct track counts in Feishin and other clients.
2026-02-04 23:35:10 -05:00
4f74b34b9a Fix Spotify playlist track counts in client responses
- Fixed UpdateSpotifyPlaylistCounts to use GetPlaylistByJellyfinId instead of GetPlaylistById
- Fixed GetSpotifyPlaylistTracksOrderedAsync to use correct playlist config lookup
- Added diagnostic logging for playlist config lookups
- Removed test-websocket.html file

This fixes the issue where Spotify playlists showed 0 tracks in playlist lists
but worked correctly when accessed directly.
2026-02-04 23:31:30 -05:00
b7417614b3 Remove memory optimization markdown file 2026-02-04 23:18:38 -05:00
72b1584d51 Fix admin dashboard to show total playable tracks (local + external matched) 2026-02-04 23:16:56 -05:00
4b289e4ddd Move admin endpoints to internal port 5275 for security 2026-02-04 22:55:21 -05:00
07844cc9c5 Add GC hints to prevent memory leaks from large byte arrays 2026-02-04 22:50:35 -05:00
1601b96800 Add memory monitoring endpoint 2026-02-04 22:45:11 -05:00
7db66067f4 Complete mark-for-deletion system and memory optimization 2026-02-04 22:41:08 -05:00
f44d8652b4 Improve favorite/unfavorite logic - copy from cache, avoid re-downloads 2026-02-04 22:34:11 -05:00
8fad6d8c4e Fix manual mapping detection in Active Playlists tab
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-02-04 19:35:34 -05:00
d11b656b23 Add loading state to save mapping button and timeout handling 2026-02-04 19:24:02 -05:00
cf1428d678 Fix manual mapping race condition and add log gitignore 2026-02-04 19:17:48 -05:00
030937b196 Add error handling and better logging for playlist cache deserialization 2026-02-04 19:10:04 -05:00