mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix download racing, cache cleanup, and WebUI storage mode display
- Replace endpoint racing with round-robin fallback for downloads to reduce CPU usage and prevent cancellation errors - Fix cache cleanup to use LastWriteTimeUtc instead of unreliable LastAccessTimeUtc - Add storage mode, cache duration, and download mode to admin config endpoint - Show actual download path based on storage mode (cache/Music vs downloads)
This commit is contained in:
@@ -28,6 +28,7 @@ public class AdminController : ControllerBase
|
|||||||
private readonly SpotifyApiSettings _spotifyApiSettings;
|
private readonly SpotifyApiSettings _spotifyApiSettings;
|
||||||
private readonly SpotifyImportSettings _spotifyImportSettings;
|
private readonly SpotifyImportSettings _spotifyImportSettings;
|
||||||
private readonly JellyfinSettings _jellyfinSettings;
|
private readonly JellyfinSettings _jellyfinSettings;
|
||||||
|
private readonly SubsonicSettings _subsonicSettings;
|
||||||
private readonly DeezerSettings _deezerSettings;
|
private readonly DeezerSettings _deezerSettings;
|
||||||
private readonly QobuzSettings _qobuzSettings;
|
private readonly QobuzSettings _qobuzSettings;
|
||||||
private readonly SquidWTFSettings _squidWtfSettings;
|
private readonly SquidWTFSettings _squidWtfSettings;
|
||||||
@@ -52,6 +53,7 @@ public class AdminController : ControllerBase
|
|||||||
IOptions<SpotifyApiSettings> spotifyApiSettings,
|
IOptions<SpotifyApiSettings> spotifyApiSettings,
|
||||||
IOptions<SpotifyImportSettings> spotifyImportSettings,
|
IOptions<SpotifyImportSettings> spotifyImportSettings,
|
||||||
IOptions<JellyfinSettings> jellyfinSettings,
|
IOptions<JellyfinSettings> jellyfinSettings,
|
||||||
|
IOptions<SubsonicSettings> subsonicSettings,
|
||||||
IOptions<DeezerSettings> deezerSettings,
|
IOptions<DeezerSettings> deezerSettings,
|
||||||
IOptions<QobuzSettings> qobuzSettings,
|
IOptions<QobuzSettings> qobuzSettings,
|
||||||
IOptions<SquidWTFSettings> squidWtfSettings,
|
IOptions<SquidWTFSettings> squidWtfSettings,
|
||||||
@@ -69,6 +71,7 @@ public class AdminController : ControllerBase
|
|||||||
_spotifyApiSettings = spotifyApiSettings.Value;
|
_spotifyApiSettings = spotifyApiSettings.Value;
|
||||||
_spotifyImportSettings = spotifyImportSettings.Value;
|
_spotifyImportSettings = spotifyImportSettings.Value;
|
||||||
_jellyfinSettings = jellyfinSettings.Value;
|
_jellyfinSettings = jellyfinSettings.Value;
|
||||||
|
_subsonicSettings = subsonicSettings.Value;
|
||||||
_deezerSettings = deezerSettings.Value;
|
_deezerSettings = deezerSettings.Value;
|
||||||
_qobuzSettings = qobuzSettings.Value;
|
_qobuzSettings = qobuzSettings.Value;
|
||||||
_squidWtfSettings = squidWtfSettings.Value;
|
_squidWtfSettings = squidWtfSettings.Value;
|
||||||
@@ -1408,8 +1411,13 @@ public class AdminController : ControllerBase
|
|||||||
},
|
},
|
||||||
library = new
|
library = new
|
||||||
{
|
{
|
||||||
downloadPath = _configuration["Library:DownloadPath"] ?? "./downloads",
|
downloadPath = _subsonicSettings.StorageMode == StorageMode.Cache
|
||||||
keptPath = _configuration["Library:KeptPath"] ?? "/app/kept"
|
? Path.Combine("cache", "Music")
|
||||||
|
: (_configuration["Library:DownloadPath"] ?? "./downloads"),
|
||||||
|
keptPath = _configuration["Library:KeptPath"] ?? "/app/kept",
|
||||||
|
storageMode = _subsonicSettings.StorageMode.ToString(),
|
||||||
|
cacheDurationHours = _subsonicSettings.CacheDurationHours,
|
||||||
|
downloadMode = _subsonicSettings.DownloadMode.ToString()
|
||||||
},
|
},
|
||||||
deezer = new
|
deezer = new
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -94,16 +94,16 @@ public class CacheCleanupService : BackgroundService
|
|||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(filePath);
|
var fileInfo = new FileInfo(filePath);
|
||||||
|
|
||||||
// Use last access time to determine if file should be deleted
|
// Use last write time (when file was created/downloaded) to determine if file should be deleted
|
||||||
// This gets updated when a cached file is streamed
|
// LastAccessTime is unreliable on many filesystems (noatime mount option)
|
||||||
if (fileInfo.LastAccessTimeUtc < cutoffTime)
|
if (fileInfo.LastWriteTimeUtc < cutoffTime)
|
||||||
{
|
{
|
||||||
var size = fileInfo.Length;
|
var size = fileInfo.Length;
|
||||||
File.Delete(filePath);
|
File.Delete(filePath);
|
||||||
deletedCount++;
|
deletedCount++;
|
||||||
totalSize += size;
|
totalSize += size;
|
||||||
_logger.LogDebug("Deleted cached file: {Path} (last accessed: {LastAccess})",
|
_logger.LogDebug("Deleted cached file: {Path} (age: {Age:F1} hours)",
|
||||||
filePath, fileInfo.LastAccessTimeUtc);
|
filePath, (DateTime.UtcNow - fileInfo.LastWriteTimeUtc).TotalHours);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -135,10 +135,10 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
// Resolve unique path if file already exists
|
// Resolve unique path if file already exists
|
||||||
outputPath = PathHelper.ResolveUniquePath(outputPath);
|
outputPath = PathHelper.ResolveUniquePath(outputPath);
|
||||||
|
|
||||||
// Race all endpoints to download from the fastest one
|
// Use round-robin with fallback for downloads to reduce CPU usage
|
||||||
Logger.LogInformation("🏁 Racing {Count} endpoints for fastest download", _fallbackHelper.EndpointCount);
|
Logger.LogDebug("Using round-robin endpoint selection for download");
|
||||||
|
|
||||||
var response = await _fallbackHelper.RaceAllEndpointsAsync(async (baseUrl, ct) =>
|
var response = await _fallbackHelper.TryWithFallbackAsync(async (baseUrl) =>
|
||||||
{
|
{
|
||||||
// Map quality settings to Tidal's quality levels per hifi-api spec
|
// Map quality settings to Tidal's quality levels per hifi-api spec
|
||||||
var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch
|
var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch
|
||||||
@@ -154,10 +154,10 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
var url = $"{baseUrl}/track/?id={trackId}&quality={quality}";
|
var url = $"{baseUrl}/track/?id={trackId}&quality={quality}";
|
||||||
|
|
||||||
// Get download info from this endpoint
|
// Get download info from this endpoint
|
||||||
var infoResponse = await _httpClient.GetAsync(url, ct);
|
var infoResponse = await _httpClient.GetAsync(url, cancellationToken);
|
||||||
infoResponse.EnsureSuccessStatusCode();
|
infoResponse.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var json = await infoResponse.Content.ReadAsStringAsync(ct);
|
var json = await infoResponse.Content.ReadAsStringAsync(cancellationToken);
|
||||||
var doc = JsonDocument.Parse(json);
|
var doc = JsonDocument.Parse(json);
|
||||||
|
|
||||||
if (!doc.RootElement.TryGetProperty("data", out var data))
|
if (!doc.RootElement.TryGetProperty("data", out var data))
|
||||||
@@ -185,8 +185,8 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
request.Headers.Add("User-Agent", "Mozilla/5.0");
|
request.Headers.Add("User-Agent", "Mozilla/5.0");
|
||||||
request.Headers.Add("Accept", "*/*");
|
request.Headers.Add("Accept", "*/*");
|
||||||
|
|
||||||
return await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
|
return await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
}, cancellationToken);
|
});
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
@@ -228,8 +228,8 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
{
|
{
|
||||||
return await QueueRequestAsync(async () =>
|
return await QueueRequestAsync(async () =>
|
||||||
{
|
{
|
||||||
// Race all endpoints for fastest download info retrieval
|
// Use round-robin with fallback instead of racing to reduce CPU usage
|
||||||
return await _fallbackHelper.RaceAllEndpointsAsync(async (baseUrl, ct) =>
|
return await _fallbackHelper.TryWithFallbackAsync(async (baseUrl) =>
|
||||||
{
|
{
|
||||||
// Map quality settings to Tidal's quality levels per hifi-api spec
|
// Map quality settings to Tidal's quality levels per hifi-api spec
|
||||||
var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch
|
var quality = _squidwtfSettings.Quality?.ToUpperInvariant() switch
|
||||||
@@ -246,10 +246,10 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
|
|
||||||
Logger.LogDebug("Fetching track download info from: {Url}", url);
|
Logger.LogDebug("Fetching track download info from: {Url}", url);
|
||||||
|
|
||||||
var response = await _httpClient.GetAsync(url, ct);
|
var response = await _httpClient.GetAsync(url, cancellationToken);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
var json = await response.Content.ReadAsStringAsync(ct);
|
var json = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||||
var doc = JsonDocument.Parse(json);
|
var doc = JsonDocument.Parse(json);
|
||||||
|
|
||||||
if (!doc.RootElement.TryGetProperty("data", out var data))
|
if (!doc.RootElement.TryGetProperty("data", out var data))
|
||||||
@@ -282,8 +282,7 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
? audioQualityEl.GetString()
|
? audioQualityEl.GetString()
|
||||||
: "LOSSLESS";
|
: "LOSSLESS";
|
||||||
|
|
||||||
Logger.LogDebug("Decoded manifest - URL: {Url}, MIME: {MimeType}, Quality: {Quality}",
|
Logger.LogInformation("Track download URL obtained from hifi-api: {Url}", downloadUrl);
|
||||||
downloadUrl, mimeType, audioQuality);
|
|
||||||
|
|
||||||
return new DownloadResult
|
return new DownloadResult
|
||||||
{
|
{
|
||||||
@@ -291,7 +290,7 @@ public class SquidWTFDownloadService : BaseDownloadService
|
|||||||
MimeType = mimeType ?? "audio/flac",
|
MimeType = mimeType ?? "audio/flac",
|
||||||
AudioQuality = audioQuality ?? "LOSSLESS"
|
AudioQuality = audioQuality ?? "LOSSLESS"
|
||||||
};
|
};
|
||||||
}, cancellationToken);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user