Commit Graph

422 Commits

Author SHA1 Message Date
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
f77281fd3d Fix GetJellyfinTrack: add UserId and verify Audio type for URL-based mapping 2026-02-04 19:04:12 -05:00
791a8b3fdb Fix Jellyfin search: add UserId and verify Audio type 2026-02-04 19:03:21 -05:00
7311bbc04a Add debug logging to GetPlaylists cache reading 2026-02-04 18:59:00 -05:00
696a2d56f2 Fix manual mappings: preserve on rematch + fix local/external count detection 2026-02-04 18:53:09 -05:00
5680b9c7c9 Fix GetPlaylists to use pre-built cache with manual mappings for accurate counts 2026-02-04 18:49:12 -05:00
1d31784ff8 Fix manual mapping: add immediate playlist rebuild and manual mapping priority in cache builder 2026-02-04 18:38:25 -05:00
10e58eced9 fix: add authentication to playlist cache pre-building
- PreBuildPlaylistItemsCacheAsync was failing with HTTP 401
- Background services don't have client headers for authentication
- Now manually creates X-Emby-Authorization header with API key
- Fixes 'Failed to fetch Jellyfin playlist items: HTTP 401' warning
- Playlist items cache now builds successfully after track matching

All 225 tests pass.
2026-02-04 18:23:11 -05:00
0937fcf163 fix: accurate playlist counting and three-color progress bars
- Fix playlist counting logic to use fuzzy matching (same as track view)
- Count local tracks by matching Jellyfin tracks to Spotify tracks
- Count external matched tracks from cache
- Count missing tracks (not found locally or externally)
- Progress bars now show three colors:
  * Green: Local tracks in Jellyfin
  * Orange: External matched tracks (SquidWTF/Deezer/Qobuz)
  * Grey: Missing tracks (not found anywhere)
- Add 'Missing' badge to tracks that couldn't be found
- Missing tracks can still be manually mapped
- Fixes incorrect counts like '28 matched • 1 missing' showing 29 external tracks

All 225 tests pass.
2026-02-04 17:49:10 -05:00
506f39d606 feat: instant UI update after manual track mapping
- Backend now returns mapped track details after saving
- Frontend updates track in-place without requiring page refresh
- Track status changes from External to Local immediately
- Map button is removed after successful mapping
- Playlist counts refresh in background
- Improved UX: no more 'refresh the playlist' message

All 225 tests pass.
2026-02-04 17:44:57 -05:00
7bb7c6a40e fix: manual mapping UI and [S] tag consistency
- Fix manual mapping track selection visual feedback (use accent color + background)
- Clear all playlist caches after manual mapping (matched, ordered, items)
- Strip [S] suffix from titles/artists/albums when searching for lyrics
- Add [S] suffix to artist and album names when song has [S] for consistency
- Ensures external tracks are clearly marked across all metadata fields

All 225 tests pass.
2026-02-04 17:31:56 -05:00
3403f7a8c9 fix: remove orphaned code causing JavaScript syntax error
Removed duplicate/orphaned lines after searchProvider() function that were
causing 'expected expression, got }' syntax error in admin UI.
2026-02-04 17:06:24 -05:00
3e5c57766b feat: pre-build playlist cache and make matching interval configurable
- Pre-build playlist items cache during track matching for instant serving
- Add PreBuildPlaylistItemsCacheAsync() to SpotifyTrackMatchingService
- Combines local Jellyfin tracks + external matched tracks in correct Spotify order
- Saves to both Redis and file cache for persistence across restarts
- Change matching interval from hardcoded 30 minutes to configurable (default: 24 hours)
- Add SPOTIFY_IMPORT_MATCHING_INTERVAL_HOURS environment variable
- Set to 0 to only run once on startup (manual trigger still works)
- Add endpoint usage files to .gitignore
- Update documentation in README and .env.example

Rationale: Spotify playlists like Discover Weekly update once per week,
so running every 24 hours is more than sufficient. Pre-building the cache
eliminates slow 'on the fly' playlist building.

All 225 tests pass.
2026-02-04 17:03:50 -05:00
24c6219189 Fix external track counting by checking matched tracks cache
- External tracks are injected on-the-fly, not stored in Jellyfin DB
- Check spotify:matched:ordered cache to get accurate external count
- Calculate external tracks as: total matched - local tracks
- This will properly show the two-color progress bar (green local + orange external)
- All 225 tests passing
2026-02-04 16:54:56 -05:00
ea21d5aa77 Add clickable search links and enhanced debug logging
- Made search query clickable - opens provider-specific search
- Added searchProvider() function with URLs for Deezer, Qobuz, SquidWTF
- Enhanced console logging to debug progress bar data
- Logs raw playlist data and calculated percentages
- Search link opens in new tab
2026-02-04 16:51:46 -05:00
ee84770397 Improve progress bar visibility and add debug logging
- Increased progress bar height from 8px to 12px for better visibility
- Changed colors to more vibrant shades (green #10b981, orange #f59e0b)
- Added console debug logging for playlist stats
- Shows local (green) and external (orange) track percentages side-by-side
2026-02-04 16:50:20 -05:00
7ccb660299 Add startup cache warming service
- Proactively loads all file caches into Redis on container startup
- Warms genre cache (30-day expiration)
- Warms playlist items cache (24-hour expiration)
- Logs warming progress and duration
- Ensures fast access immediately after restart
- Cleans up expired genre cache files automatically
- All 225 tests passing
2026-02-04 16:46:27 -05:00
0793c4614b Add file-based caching for MusicBrainz genres
- Dual-layer caching: Redis (fast) + file system (persistent)
- File cache survives container restarts
- 30-day cache expiration for both layers
- Negative result caching to avoid repeated failed lookups
- Safe file names using base64 encoding
- Automatic cache restoration to Redis on startup
- Cache directory: /app/cache/genres
2026-02-04 16:44:35 -05:00
bf02dc5a57 Add MusicBrainz genre enrichment and improve track counting
- Fixed external track detection (check for provider prefix in ID)
- Added genre support to MusicBrainz service (inc=genres+tags)
- Created GenreEnrichmentService for async genre lookup with caching
- Show provider name and search query for external tracks in admin UI
- Display search query that will be used for external track streaming
- Aggregate playlist genres from track genres
- All 225 tests passing
2026-02-04 16:43:17 -05:00
7938871556 Release 1.0.0 - Production ready
- Fixed AdminController export/import .env endpoints (moved from ConfigUpdateRequest class)
- Added ArtistId and AlbumId to integration test fixtures
- All 225 tests passing
- Version set to 1.0.0 (semantic versioning)
- MusicBrainz service ready for future ISRC-based matching (1.1.0)
- Import/export handles full .env configuration with timestamped backups
2026-02-04 16:33:58 -05:00
39f6893741 Add MusicBrainz API integration for metadata enrichment
- Added MusicBrainzSettings model with username/password authentication
- Created MusicBrainzService with ISRC lookup and recording search
- Implements proper rate limiting (1 req/sec) per MusicBrainz rules
- Added meaningful User-Agent header as required
- Registered service in Program.cs with configuration
- Added MusicBrainz section to appsettings.json
- Credentials stored in .env (MUSICBRAINZ_USERNAME/PASSWORD)

Next: Add to admin UI and implement import/export for .env
2026-02-04 16:23:16 -05:00
cd4fd702fc Match Jellyfin response structure exactly based on real API responses
Verified against real Jellyfin responses for tracks, albums, artists, and playlists:
- Reordered fields to match Jellyfin's exact field order
- Added missing fields: PremiereDate, HasLyrics, Container, ETag, etc.
- Fixed MediaType to 'Unknown' for albums/artists (not null)
- Fixed UserData.Key format to match Jellyfin patterns
- Added ParentLogoItemId, ParentBackdropItemId for proper hierarchy
- Fixed Genres/GenreItems to always be arrays (never null)
- Added complete MediaStream structure with all Jellyfin fields
- Playlists now have MediaType='Audio' to match real playlists
- All responses now perfectly mimic real Jellyfin structure
2026-02-04 16:17:45 -05:00
038c3a9614 Fix playlist count caching and make external tracks perfectly mimic Jellyfin responses
- Fixed UpdateSpotifyPlaylistCounts to properly handle file cache without skipping items
- Added Genres and GenreItems fields to all tracks (empty array if no genre)
- Added complete MediaStreams with audio codec info for external tracks
- Added missing MediaSource fields: IgnoreDts, IgnoreIndex, GenPtsInput, HasSegments
- Ensured Artists array never contains null values
- All external tracks now have proper genre arrays to match Jellyfin structure
2026-02-04 16:12:41 -05:00
6e966f9e0d Fix nullability warnings in SpotifyTrackMatchingService 2026-02-04 16:10:16 -05:00
b778b3d31e Fix MediaSources null array fields and add logging for artist albums
- Added MediaStreams, MediaAttachments, Formats as empty arrays instead of null
- Added RunTimeTicks field to MediaSources
- Added detailed logging to GetExternalChildItems to debug artist album issues
- This should fix 'type Null is not a subtype of type List<dynamic>' error
2026-02-04 16:04:04 -05:00
526a079368 Fix compilation errors and nullability warnings
- Fixed LrclibService.GetLyricsAsync call to use empty string and 0 for duration
- Fixed nullability warnings in SpotifyTrackMatchingService by explicitly casting to nullable tuple
2026-02-04 15:40:52 -05:00
7a7b884af2 Update playlist progress bar to show stacked blue/yellow segments
- Blue segment shows local tracks percentage
- Yellow segment shows external matched tracks percentage
- Bar fills to 100% when all tracks are matched
- Added tooltips showing track counts on hover
2026-02-04 15:37:07 -05:00
6ab5e44112 Fix apostrophe normalization syntax error - use Unicode escape sequences 2026-02-04 15:33:59 -05:00
7c92515723 Fix null boolean error and playlist count showing 0 after restart
- Added all required boolean fields to MediaSources (IsRemote, IsInfiniteStream, RequiresOpening, etc)
- UpdateSpotifyPlaylistCounts now loads from file cache if Redis is empty
- This fixes 'type Null is not a subtype of type bool' error in Finamp
- Playlist counts now show correctly even after container restart
2026-02-04 15:32:18 -05:00
8091d30602 Add parallel provider racing for searches and lyrics pre-fetching
- Created ParallelMetadataService to race all providers and return fastest result
- Search now uses parallel service when available for lower latency
- Pre-fetch LRCLib lyrics for top 3 search results in background
- FuzzyMatcher already handles apostrophe normalization (applied everywhere)
2026-02-04 15:29:56 -05:00
e7ff330625 Add logging for server-to-client WebSocket messages to debug remote control
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-02-04 11:30:12 -05:00
aadda9b873 Fix apostrophe matching, add URL input for track mapping, improve search caching
Some checks failed
CI / build-and-test (push) Has been cancelled
- Enhanced FuzzyMatcher to normalize apostrophes (', ', ', etc) for better matching
- Added Redis-only caching for search results (15 min TTL)
- Added pattern-based cache deletion for search and image keys
- Added URL input field in Map to Local modal to paste Jellyfin track URLs
- Added /api/admin/jellyfin/track/{id} endpoint to fetch track details by ID
- Fixed duplicate cache key declaration in GetSpotifyPlaylistTracksOrderedAsync
- Updated cache clearing to include new spotify:playlist:items:* keys
2026-02-04 01:44:56 -05:00