Commit Graph

590 Commits

Author SHA1 Message Date
d961eee2be chore: bump version to 1.3.0 2026-02-11 00:00:42 -05:00
3f2bf579e3 Merge branch 'beta' into dev 2026-02-10 23:52:31 -05:00
f74728fc73 fix: use MBID lookup for MusicBrainz genre enrichment
Search API doesn't return genres even with inc=genres parameter.
Now doing search to get MBID, then lookup by MBID to get genres.
v1.2.8
2026-02-10 23:52:14 -05:00
a8a1e61852 fix: use MBID lookup for MusicBrainz genre enrichment
Search API doesn't return genres even with inc=genres parameter.
Now doing search to get MBID, then lookup by MBID to get genres.
2026-02-10 23:51:50 -05:00
38ce124e98 Merge branch 'beta' into dev 2026-02-10 23:03:26 -05:00
87467be61b feat: add LyricsPlus API with modular orchestrator architecture
Add multi-source lyrics support with clean, modular architecture for easier debugging and maintenance.

New Features:
- LyricsPlusService: Multi-source lyrics API (Apple Music, Spotify, Musixmatch)
- LyricsOrchestrator: Priority-based coordinator for all lyrics sources
- Modular service architecture with independent error handling
- Word-level and line-level timing support with LRC conversion

Architecture:
- Priority chain: Spotify → LyricsPlus → LRCLib
- Each service logs independently (→ Trying, ✓ Found,  Not found)
- Fallback continues even if one service fails
- Easy to add new sources or modify priority

Benefits:
- Easier debugging with clear service-level logs
- Better maintainability with separated concerns
- More reliable with graceful fallback handling
- Extensible for future lyrics sources
v1.2.7
2026-02-10 23:02:17 -05:00
7ae5b75d68 feat: add LyricsPlus API integration with modular orchestrator
- Add LyricsPlusService for multi-source lyrics (Apple, Spotify, Musixmatch)
- Create LyricsOrchestrator for modular, priority-based lyrics fetching
- Refactor lyrics logic into independent service modules
- Priority: Spotify → LyricsPlus → LRCLib
- Each service logs independently for easier debugging
- Supports word-level and line-level timing conversion to LRC
2026-02-10 23:01:47 -05:00
206cb0b84f Merge branch 'beta' into dev
Some checks failed
CI / build-and-test (push) Has been cancelled
2026-02-10 13:36:29 -05:00
713ecd4ec8 v1.2.6: fix search result ordering to prioritize local tracks
Some checks failed
Docker Build & Push / build-and-test (push) Has been cancelled
Docker Build & Push / docker (push) Has been cancelled
v1.2.6
2026-02-10 13:36:06 -05:00
962004b174 fix: sort search results by individual match scores instead of source averages
Local tracks now properly appear first when they match better, even if there are many external results. Each track is scored individually with +10 boost for local tracks, then all results are sorted by score.
2026-02-10 13:29:52 -05:00
f017c5db72 Merge branch 'beta' into dev 2026-02-10 12:57:13 -05:00
0ff1e3a428 v1.2.5: fix genre enrichment blocking cover art loading v1.2.5 2026-02-10 12:56:43 -05:00
e9f527d7dc fix: make genre enrichment non-blocking in DeezerMetadataService 2026-02-10 12:56:13 -05:00
f55a8bfe43 Merge branch 'beta' into dev 2026-02-10 12:51:21 -05:00
cef18b9482 v1.2.5: prioritize local tracks and optimize genre enrichment
Local tracks now appear first in search results with +10 score boost. Genre enrichment is non-blocking for faster cover art and playback.
2026-02-10 12:50:52 -05:00
b4fbf7431d fix: prioritize local tracks in search and make genre enrichment non-blocking
- Local tracks get +10 score boost in search results (exact matches now appear first)
- Genre enrichment is now fire-and-forget (doesn't block cover art or playback)
- External playlist tracks are enriched with MusicBrainz genres during pre-building
- Genres are cached for 30 days to minimize API calls
2026-02-10 12:50:24 -05:00
ae418bef94 Merge branch 'beta' into dev 2026-02-10 12:15:04 -05:00
1bfe30b216 v1.2.4: stop racing SquidWTF endpoints for better throughput
Use round-robin instead of racing to enable parallel processing of 12 tracks simultaneously (one per endpoint) instead of racing all endpoints for each track.
v1.2.4
2026-02-10 12:14:38 -05:00
c492f085b1 perf: stop racing SquidWTF endpoints, use round-robin for parallel processing
Changed from racing all 12 endpoints per track to round-robin distribution. This allows processing 12 tracks in parallel (one per endpoint) instead of wasting requests racing for each track. Also fixed Jellyfin playlist field requests to include all metadata fields.
2026-02-10 12:14:09 -05:00
1d88902e8f Merge branch 'beta' into dev 2026-02-10 11:56:37 -05:00
c9c82a650d v1.2.3: fix Spotify playlist metadata fields
Complete Jellyfin item structure for external tracks with all requested fields including PlaylistItemId, DateCreated, ParentId, Tags, People, and SortName.
v1.2.3
2026-02-10 11:56:12 -05:00
6a39184b01 fix: add missing Jellyfin fields to external tracks
Added PlaylistItemId, DateCreated, ParentId, Tags, People, and SortName fields to match real Jellyfin item structure for Spotify playlist tracks.
2026-02-10 11:55:07 -05:00
b69625dc2e refactor: added a buncha tests 2026-02-10 11:23:58 -05:00
d0a7dbcc96 v1.2.2: fix metadata loss in Spotify playlists
Spotify playlist tracks were missing genres, composers, and other metadata because the proxy only requested MediaSources field instead of passing through all client-requested fields.
v1.2.2
2026-02-10 11:01:38 -05:00
c40adc4465 v1.2.2: Fix critical metadata loss in Spotify playlist proxy
CRITICAL FIX: Spotify playlist tracks were missing Genres, People, ProviderIds,
ParentId, Tags, DateCreated, and SortName fields because the proxy was only
requesting Fields=MediaSources from Jellyfin instead of passing through all
requested fields from the client.

This caused Jellyfin clients to display incomplete metadata (empty genre columns,
missing composer info, etc.) when viewing Spotify-synced playlists through allstarr.

Changes:
- Modified GetSpotifyPlaylistTracksOrderedAsync to pass through all Fields
  parameters from the original client request instead of hardcoding MediaSources
- Now preserves all metadata fields that Jellyfin provides for local tracks
- Fixes genre display, composer info, and other metadata in playlist views

Impact: All Spotify playlist tracks now show complete metadata matching direct
Jellyfin behavior.
2026-02-10 10:59:57 -05:00
9c9a827a91 v1.2.1: MusicBrainz genre enrichment + cleanup
## Features
- Implement automatic MusicBrainz genre enrichment for all external sources
  - Deezer: Enriches when genre missing
  - Qobuz: Enriches when genre missing
  - SquidWTF/Tidal: Always enriches (Tidal doesn't provide genres)
- Use ISRC codes for exact matching, fallback to title/artist search
- Cache results in Redis (30 days) + file cache for performance
- Respect MusicBrainz rate limits (1 req/sec)

## Cleanup
- Remove unused Spotify API ClientId and ClientSecret settings
- Simplify Spotify API configuration

## Fixes
- Make GenreEnrichmentService optional to fix test failures
- All 225 tests passing

This ensures all external tracks have genre metadata for better
organization and filtering in music clients.
2026-02-10 10:29:49 -05:00
ea4a533c24 fix: make GenreEnrichmentService optional in metadata services
- Add null checks before calling genre enrichment
- Make constructor parameter optional with default null
- Fixes test failures where GenreEnrichmentService couldn't be mocked
- All 225 tests now passing
2026-02-10 10:29:24 -05:00
96889738df v1.2.1: MusicBrainz genre enrichment + cleanup
## Features
- Implement automatic MusicBrainz genre enrichment for all external sources
  - Deezer: Enriches when genre missing
  - Qobuz: Enriches when genre missing
  - SquidWTF/Tidal: Always enriches (Tidal doesn't provide genres)
- Use ISRC codes for exact matching, fallback to title/artist search
- Cache results in Redis (30 days) + file cache for performance
- Respect MusicBrainz rate limits (1 req/sec)

## Cleanup
- Remove unused Spotify API ClientId and ClientSecret settings
- Simplify Spotify API configuration

This ensures all external tracks have genre metadata for better
organization and filtering in music clients.
v1.2.1
2026-02-10 10:25:41 -05:00
02640b8a3a feat: implement MusicBrainz genre enrichment for all external sources
- Add automatic genre enrichment for Deezer, Qobuz, and SquidWTF tracks
- Use ISRC codes for exact matching, fallback to title/artist search
- Cache results in Redis (30 days) and file cache for performance
- Remove unused Spotify API ClientId and ClientSecret settings
- Respect MusicBrainz rate limits (1 req/sec)

This ensures all external tracks have genre metadata, even when the
source provider doesn't include it (especially SquidWTF/Tidal).
2026-02-10 10:25:17 -05:00
6ea03b8005 fix: add proper local Jellyfin mapping modal for Map to Local button
Some checks failed
CI / build-and-test (push) Has been cancelled
Map to Local now opens a Jellyfin search modal instead of the external provider modal.
2026-02-09 18:29:15 -05:00
1369d09cbd fix: invalidate playlist cache when schedule is updated
Playlist summary cache now refreshes immediately when sync schedules are changed.
2026-02-09 18:24:59 -05:00
f3c791496e v1.2.0: Spotify playlist improvements and admin UI fixes
Some checks failed
Docker Build & Push / build-and-test (push) Has been cancelled
Docker Build & Push / docker (push) Has been cancelled
Enhanced Spotify playlist integration with GraphQL API, fixed track counts and folder filtering, improved session IP tracking with X-Forwarded-For support, and added per-playlist cron scheduling.
2026-02-09 18:17:15 -05:00
838151741f fix: show correct track counts and IPs in admin UI
Fixed Spotify playlists showing 0 tracks, filtered out playlist folders, and corrected session IPs to use X-Forwarded-For header.
2026-02-09 18:16:38 -05:00
88bf083386 Fix GraphQL query for fetching user playlists - use libraryV3
- Changed from fetchLibraryPlaylists to libraryV3 operation (correct Spotify GraphQL endpoint)
- Use GET request with query params instead of POST (matches Jellyfin plugin implementation)
- Updated response parsing to match libraryV3 structure (me.libraryV3.items[].item.data)
- Fixed owner field to use 'username' instead of 'name'
- This should resolve the BadRequest (400) errors when fetching user playlists
2026-02-09 16:36:10 -05:00
670544a9d6 Fix AdminController Spotify 429 rate limiting in Link Playlists tab
- Replace direct REST API calls with SpotifyApiClient GraphQL method
- Add GetUserPlaylistsAsync() method to fetch all user playlists via GraphQL
- GraphQL endpoint is much less rate-limited than REST API /me/playlists
- Enhanced playlist data with track count, owner, and image URL from GraphQL
- Simplified AdminController code by delegating to SpotifyApiClient
2026-02-09 16:32:18 -05:00
0dca6b792d Fix Spotify 429 rate limiting and startup performance issues
- Fix: Use correct HttpClient (_webApiClient) for GraphQL library playlists endpoint
  - Was using _httpClient which pointed to wrong base URL causing 429 errors
- Add: Retry logic with Retry-After header support for 429 responses
- Add: Minimum 500ms delay between library playlist pages to prevent rate limiting
- Add: 5-second timeout per endpoint benchmark ping to prevent slow endpoints from blocking startup
- Add: Documentation for timeout requirements in EndpointBenchmarkService
- Fix: ARM64 compatibility for spotify-lyrics service via platform emulation in docker-compose
2026-02-09 16:09:38 -05:00
f135db3f60 fix: use GraphQL for user playlists to avoid 429 rate limits
Some checks failed
CI / build-and-test (push) Has been cancelled
- Switched from REST API /me/playlists to GraphQL fetchLibraryPlaylists
- GraphQL endpoint is less aggressively rate-limited by Spotify
- Fixes 429 errors when using 'Select from My Playlists' dropdown
- Background services already use GraphQL and work fine
2026-02-09 15:10:14 -05:00
2b76fa9e6f Enhance README with features and images
Updated README.md to enhance feature descriptions and add images.
2026-02-09 14:54:43 -05:00
6949f8aed4 feat: implement per-playlist cron scheduling with persistent cache
- Added Cronos package for cron expression parsing
- Each playlist now has independent cron schedule (default: 0 8 * * 1)
- Cache persists until next cron run, not just cache duration
- Prevents excess Spotify API calls - only refreshes on cron trigger
- Manual refresh still allowed with 5-minute cooldown
- Added 429 rate limit handling for user playlist fetching
- Added crontab.guru link to UI for easy schedule building
- Both SpotifyPlaylistFetcher and SpotifyTrackMatchingService use cron
- Automatic matching only runs when cron schedule triggers
2026-02-09 14:23:23 -05:00
a37f7e0b1d feat: add sync schedule editing and improve Spotify rate limit handling
Renamed Active Playlists to Injected Playlists. Added sync schedule column with inline edit button. Added endpoint to update playlist sync schedules. Improved error handling for Spotify rate limits with user-friendly messages.
2026-02-09 13:22:02 -05:00
2b4cd35cf7 feat: add per-playlist cron sync schedules
Each playlist now has its own cron schedule for syncing with Spotify. Default is 0 8 * * 1 (Monday 8 AM). Removed global MatchingIntervalHours in favor of per-playlist scheduling.
2026-02-09 13:15:04 -05:00
faa07c2791 fix: resolve build errors in forwarded headers and config endpoints
Fixed duplicate builder variable, deprecated KnownNetworks property, and removed non-existent SpotifyImportSettings properties from config endpoint.
2026-02-09 13:12:21 -05:00
bdd753fd02 feat: add admin UI improvements and forwarded headers support
Enhanced admin configuration UI with missing fields, required indicators, and sp_dc warning. Added Spotify playlist selector for linking with auto-filtering of already-linked playlists. Configured forwarded headers to pass real client IPs from nginx to Jellyfin. Improved track view modal error handling.
2026-02-09 12:49:50 -05:00
0a07804833 feat: standardize download path configuration and auto-migrate .env
- Change DOWNLOAD_PATH to Library__DownloadPath (ASP.NET Core standard)
- Add EnvMigrationService to automatically update old .env files on startup
- Update .env.example with new variable name
- Ensures cache cleanup and downloads use consistent path configuration
- No manual intervention needed - migration happens automatically
2026-02-09 12:29:57 -05:00
6c14fc299c fix: prevent duplicate downloads and lock release bug
- Track lock state to prevent double-release in finally block
- Fixes exception when download is already in progress
- Prevents duplicate file downloads with (1), (2), (3) suffixes
- Ensures proper lock management during concurrent download requests
2026-02-09 12:26:08 -05:00
b03a4b85c9 fix: cache cleanup service using wrong path
- Update CacheCleanupService to use downloads/cache instead of /tmp/allstarr-cache
- Matches the actual path used by download services
- Fixes cache files not being cleaned up after expiration
2026-02-09 12:24:48 -05:00
565cb46b72 feat: add proper multi-artist support with ArtistIds list
- Add ArtistIds list to Song model to store IDs for all artists
- Update SquidWTF ParseTidalTrack and ParseTidalTrackFull to populate ArtistIds from artists array
- Update Deezer ParseDeezerTrackFull to populate ArtistIds from contributors
- Update JellyfinResponseBuilder to use real ArtistIds instead of fake IDs
- Fixes UnprocessableEntity errors when clicking on secondary artists
- Enables proper navigation to all artist pages in Jellyfin clients
2026-02-09 12:22:40 -05:00
f68706f300 Release v1.1.1 - Download Structure Fix
Some checks failed
Docker Build & Push / build-and-test (push) Has been cancelled
Docker Build & Push / docker (push) Has been cancelled
Fixed cache and permanent files to use unified downloads/ structure instead of separate paths.
2026-02-08 01:51:18 -05:00
6357b524da remove monochrome-api.samidy.com endpoint
Some checks failed
CI / build-and-test (push) Has been cancelled
Free tier account can't stream or download, only useful for metadata fallback.
2026-02-08 01:49:12 -05:00
aa9f0d0345 fix: use unified download structure for cache and permanent files
- Cache mode now uses downloads/cache/ instead of cache/Music/
- Permanent mode now uses downloads/permanent/ instead of downloads/
- Kept files already use downloads/kept/
- All download paths now unified under downloads/ base directory
2026-02-08 01:38:14 -05:00