- Extract SubsonicRequestParser for HTTP parameter extraction
- Extract SubsonicResponseBuilder for XML/JSON response formatting
- Extract SubsonicModelMapper for search result parsing and merging
- Extract SubsonicProxyService for upstream Subsonic server communication
- Add comprehensive test coverage (45 tests) for all new services
- Reduce SubsonicController from 1174 to 666 lines (-43%)
All tests passing. Build succeeds with 0 errors.
- Register DeezerDownloadService and DeezerMetadataService as Singleton
to properly share state across requests (rate limiting, download tracking)
- Fix race condition in LocalLibraryService.LoadMappingsAsync with
double-check locking pattern
- Dispose HttpRequestMessage objects to prevent memory leaks (4 occurrences)
- Handle fire-and-forget TriggerLibraryScanAsync with proper error logging
- Replace Console.WriteLine with ILogger in SubsonicController
- Fix while loop in DownloadSongAsync to refresh activeDownload state
- Use modern C# range operator syntax for Substring calls
- Clean up appsettings.json template (remove private IP, clear ARL token)
- Add documentation comment for Blowfish decryption key
- Add downloads directory to gitignore
- getArtist now merges local Navidrome albums with Deezer albums
- getAlbum returns complete song metadata for Deezer albums
- Added missing fields (parent, isDir, suffix, contentType, type, isVideo, duration, genre) for Aonsoku compatibility
ASP.NET Core serializes anonymous objects with camelCase, producing
'subsonicResponse' instead of 'subsonic-response'. Fixed by using
Dictionary with explicit key names for all JSON responses.
- Remove custom /ping endpoint that returned non-standard JSON format
- Fix wildcard endpoint to return Subsonic-compatible error responses
- All requests now properly relay to/from the real Subsonic server
- Parse Subsonic search3 response (JSON and XML formats)
- Merge local results with external Deezer results
- Local results appear first, then external results
- Add isExternal flag to distinguish sources