Release v1.0.0 - Production Ready

Major Features:
- Spotify playlist injection with missing tracks search
- Transparent proxy authentication system
- WebSocket session management for external tracks
- Manual track mapping and favorites system
- Lyrics support (Spotify + LRCLib) with prefetching
- Admin dashboard with analytics and configuration
- Performance optimizations with health checks and endpoint racing
- Comprehensive caching and memory management

Performance Improvements:
- Quick health checks (3s timeout) before trying endpoints
- Health check results cached for 30 seconds
- 5 minute timeout for large artist responses
- Background Odesli conversion after streaming starts
- Parallel lyrics prefetching
- Endpoint benchmarking and racing
- 16 SquidWTF endpoints with load balancing

Reliability:
- Automatic endpoint fallback and failover
- Token expiration handling
- Concurrent request optimization
- Memory leak fixes
- Proper session cleanup

User Experience:
- Web UI for configuration and playlist management
- Real-time progress tracking
- API analytics dashboard
- Manual track mapping interface
- Playlist statistics and health monitoring
This commit is contained in:
2026-02-08 00:43:47 -05:00
parent fa9739bfaa
commit 2b09484c0b
58 changed files with 19997 additions and 1016 deletions

View File

@@ -24,7 +24,6 @@ namespace allstarr.Services.Deezer;
public class DeezerDownloadService : BaseDownloadService
{
private readonly HttpClient _httpClient;
private readonly SemaphoreSlim _requestLock = new(1, 1);
private readonly string? _arl;
private readonly string? _arlFallback;
@@ -33,9 +32,6 @@ public class DeezerDownloadService : BaseDownloadService
private string? _apiToken;
private string? _licenseToken;
private DateTime _lastRequestTime = DateTime.MinValue;
private readonly int _minRequestIntervalMs = 200;
private const string DeezerApiBase = "https://api.deezer.com";
// Deezer's standard Blowfish CBC encryption key for track decryption
@@ -111,7 +107,10 @@ public class DeezerDownloadService : BaseDownloadService
// Build organized folder structure: Artist/Album/Track using AlbumArtist (fallback to Artist for singles)
var artistForPath = song.AlbumArtist ?? song.Artist;
var basePath = SubsonicSettings.StorageMode == StorageMode.Cache ? CachePath : DownloadPath;
// Cache mode uses cache/Music folder (cleaned up after 24h), Permanent mode uses downloads folder
var basePath = SubsonicSettings.StorageMode == StorageMode.Cache
? Path.Combine("cache", "Music")
: "downloads";
var outputPath = PathHelper.BuildTrackPath(basePath, artistForPath, song.Album, song.Title, song.Track, extension);
// Create directories if they don't exist
@@ -494,27 +493,6 @@ public class DeezerDownloadService : BaseDownloadService
await RetryWithBackoffAsync<bool>(action, maxRetries, initialDelayMs);
}
private async Task<T> QueueRequestAsync<T>(Func<Task<T>> action)
{
await _requestLock.WaitAsync();
try
{
var now = DateTime.UtcNow;
var timeSinceLastRequest = (now - _lastRequestTime).TotalMilliseconds;
if (timeSinceLastRequest < _minRequestIntervalMs)
{
await Task.Delay((int)(_minRequestIntervalMs - timeSinceLastRequest));
}
_lastRequestTime = DateTime.UtcNow;
return await action();
}
finally
{
_requestLock.Release();
}
}
#endregion