diff --git a/allstarr/Services/Common/EndpointBenchmarkService.cs b/allstarr/Services/Common/EndpointBenchmarkService.cs
index 688e3d9..0e22afd 100644
--- a/allstarr/Services/Common/EndpointBenchmarkService.cs
+++ b/allstarr/Services/Common/EndpointBenchmarkService.cs
@@ -20,6 +20,9 @@ public class EndpointBenchmarkService
///
/// Benchmarks a list of endpoints by making test requests.
/// Returns endpoints sorted by average response time (fastest first).
+ ///
+ /// IMPORTANT: The testFunc should implement its own timeout to prevent slow endpoints
+ /// from blocking startup. Recommended: 5-10 second timeout per ping.
///
public async Task> BenchmarkEndpointsAsync(
List endpoints,
diff --git a/allstarr/Services/Spotify/SpotifyApiClient.cs b/allstarr/Services/Spotify/SpotifyApiClient.cs
index 8bb5cf2..65286ca 100644
--- a/allstarr/Services/Spotify/SpotifyApiClient.cs
+++ b/allstarr/Services/Spotify/SpotifyApiClient.cs
@@ -349,6 +349,17 @@ public class SpotifyApiClient : IDisposable
var response = await _webApiClient.SendAsync(request, cancellationToken);
+ // Handle 429 rate limiting with exponential backoff
+ if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
+ {
+ var retryAfter = response.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(5);
+ _logger.LogWarning("Spotify rate limit hit (429) when fetching playlist {PlaylistId}. Waiting {Seconds}s before retry...", playlistId, retryAfter.TotalSeconds);
+ await Task.Delay(retryAfter, cancellationToken);
+
+ // Retry the request
+ response = await _webApiClient.SendAsync(request, cancellationToken);
+ }
+
if (!response.IsSuccessStatusCode)
{
_logger.LogError("Failed to fetch playlist via GraphQL: {StatusCode}", response.StatusCode);
@@ -801,7 +812,19 @@ public class SpotifyApiClient : IDisposable
};
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
- var response = await _httpClient.SendAsync(request, cancellationToken);
+ var response = await _webApiClient.SendAsync(request, cancellationToken);
+
+ // Handle 429 rate limiting with exponential backoff
+ if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
+ {
+ var retryAfter = response.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(5);
+ _logger.LogWarning("Spotify rate limit hit (429) when fetching library playlists. Waiting {Seconds}s before retry...", retryAfter.TotalSeconds);
+ await Task.Delay(retryAfter, cancellationToken);
+
+ // Retry the request
+ response = await _httpClient.SendAsync(request, cancellationToken);
+ }
+
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("GraphQL user playlists request failed: {StatusCode}", response.StatusCode);
@@ -851,11 +874,11 @@ public class SpotifyApiClient : IDisposable
if (itemCount < limit) break;
offset += limit;
- // GraphQL is less rate-limited, but still add a small delay
- if (_settings.RateLimitDelayMs > 0)
- {
- await Task.Delay(_settings.RateLimitDelayMs, cancellationToken);
- }
+ // Add delay between pages to avoid rate limiting
+ // Library fetching can be aggressive, so use a longer delay
+ var delayMs = Math.Max(_settings.RateLimitDelayMs, 500); // Minimum 500ms between pages
+ _logger.LogDebug("Waiting {DelayMs}ms before fetching next page of library playlists...", delayMs);
+ await Task.Delay(delayMs, cancellationToken);
}
_logger.LogInformation("Found {Count} playlists matching '{SearchName}' via GraphQL", playlists.Count, searchName);
diff --git a/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs b/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs
index e1bbbfc..e23ce76 100644
--- a/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs
+++ b/allstarr/Services/SquidWTF/SquidWTFStartupValidator.cs
@@ -73,7 +73,11 @@ public class SquidWTFStartupValidator : BaseStartupValidator
{
try
{
- var response = await _httpClient.GetAsync(endpoint, ct);
+ // 5 second timeout per ping - mark slow endpoints as failed
+ using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
+ timeoutCts.CancelAfter(TimeSpan.FromSeconds(5));
+
+ var response = await _httpClient.GetAsync(endpoint, timeoutCts.Token);
return response.IsSuccessStatusCode;
}
catch
diff --git a/docker-compose.yml b/docker-compose.yml
index 81bbbc5..f4ed98c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,8 +17,11 @@ services:
networks:
- allstarr-network
+ # Spotify Lyrics API sidecar service
+ # Note: This image only supports AMD64. On ARM64 systems, Docker will use emulation.
spotify-lyrics:
image: akashrchandran/spotify-lyrics-api:latest
+ platform: linux/amd64
container_name: allstarr-spotify-lyrics
restart: unless-stopped
ports: