mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
stupid timezones
This commit is contained in:
@@ -14,19 +14,22 @@ public class SpotifyImportSettings
|
||||
|
||||
/// <summary>
|
||||
/// Hour when Spotify Import plugin runs (24-hour format, 0-23)
|
||||
/// Example: 16 for 4:00 PM
|
||||
/// NOTE: This setting is now optional and only used for the sync window check.
|
||||
/// The fetcher will search backwards from current time for the last 48 hours,
|
||||
/// so timezone confusion is avoided.
|
||||
/// </summary>
|
||||
public int SyncStartHour { get; set; } = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Minute when Spotify Import plugin runs (0-59)
|
||||
/// Example: 15 for 4:15 PM
|
||||
/// NOTE: This setting is now optional and only used for the sync window check.
|
||||
/// </summary>
|
||||
public int SyncStartMinute { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// How many hours to search for missing tracks files after sync start time
|
||||
/// Example: 2 means search from 4:00 PM to 6:00 PM
|
||||
/// This prevents the fetcher from running too frequently.
|
||||
/// Set to 0 to disable the sync window check and always search on startup.
|
||||
/// </summary>
|
||||
public int SyncWindowHours { get; set; } = 2;
|
||||
|
||||
|
||||
@@ -72,18 +72,27 @@ public class SpotifyMissingTracksFetcher : BackgroundService
|
||||
}
|
||||
_logger.LogInformation("========================================");
|
||||
|
||||
// Always run once on startup to ensure we have missing tracks
|
||||
// Check if we should run on startup
|
||||
if (!_hasRunOnce)
|
||||
{
|
||||
_logger.LogInformation("Running initial fetch on startup");
|
||||
try
|
||||
var shouldRun = await ShouldRunOnStartupAsync();
|
||||
if (shouldRun)
|
||||
{
|
||||
await FetchMissingTracksAsync(stoppingToken, bypassSyncWindowCheck: true);
|
||||
_hasRunOnce = true;
|
||||
_logger.LogInformation("Running initial fetch on startup");
|
||||
try
|
||||
{
|
||||
await FetchMissingTracksAsync(stoppingToken, bypassSyncWindowCheck: true);
|
||||
_hasRunOnce = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error during startup fetch");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
_logger.LogError(ex, "Error during startup fetch");
|
||||
_logger.LogInformation("Skipping startup fetch - existing cache is still current");
|
||||
_hasRunOnce = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,78 +130,69 @@ public class SpotifyMissingTracksFetcher : BackgroundService
|
||||
private async Task<bool> ShouldRunOnStartupAsync()
|
||||
{
|
||||
_logger.LogInformation("=== STARTUP CACHE CHECK ===");
|
||||
_logger.LogInformation("Cache directory: {Dir}", CacheDirectory);
|
||||
_logger.LogInformation("Checking {Count} playlists", _playlistIdToName.Count);
|
||||
|
||||
// List all files in cache directory for debugging
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(CacheDirectory))
|
||||
{
|
||||
var files = Directory.GetFiles(CacheDirectory, "*.json");
|
||||
_logger.LogInformation("Found {Count} JSON files in cache directory:", files.Length);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
var age = DateTime.UtcNow - fileInfo.LastWriteTimeUtc;
|
||||
_logger.LogInformation(" - {Name} (age: {Age:F1}h, size: {Size} bytes)",
|
||||
Path.GetFileName(file), age.TotalHours, fileInfo.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Cache directory does not exist: {Dir}", CacheDirectory);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error listing cache directory");
|
||||
}
|
||||
var settings = _spotifySettings.Value;
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Check file cache first, then Redis
|
||||
foreach (var playlistName in _playlistIdToName.Values)
|
||||
// Calculate when today's sync window ends
|
||||
var todaySync = now.Date
|
||||
.AddHours(settings.SyncStartHour)
|
||||
.AddMinutes(settings.SyncStartMinute);
|
||||
var todaySyncEnd = todaySync.AddHours(settings.SyncWindowHours);
|
||||
|
||||
// If we haven't reached today's sync window end yet, check if we have yesterday's file
|
||||
if (now < todaySyncEnd)
|
||||
{
|
||||
var filePath = GetCacheFilePath(playlistName);
|
||||
_logger.LogInformation("Checking playlist: {Playlist}", playlistName);
|
||||
_logger.LogInformation(" Expected file path: {Path}", filePath);
|
||||
_logger.LogInformation("Today's sync window hasn't ended yet (ends at {End})", todaySyncEnd);
|
||||
_logger.LogInformation("Checking if we have a recent cache file...");
|
||||
|
||||
if (File.Exists(filePath))
|
||||
// Check if we have any cache (file or Redis) for all playlists
|
||||
var allPlaylistsHaveCache = true;
|
||||
|
||||
foreach (var playlistName in _playlistIdToName.Values)
|
||||
{
|
||||
var fileAge = DateTime.UtcNow - File.GetLastWriteTimeUtc(filePath);
|
||||
_logger.LogInformation(" File exists! Age: {Age:F1}h", fileAge.TotalHours);
|
||||
_logger.LogInformation(" ✓ Found file cache (age: {Age:F1}h, no expiration)", fileAge.TotalHours);
|
||||
var filePath = GetCacheFilePath(playlistName);
|
||||
var cacheKey = $"spotify:missing:{playlistName}";
|
||||
|
||||
// Load from file into Redis if not already there
|
||||
var key = $"spotify:missing:{playlistName}";
|
||||
if (!await _cache.ExistsAsync(key))
|
||||
// Check file cache
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
_logger.LogInformation(" Loading into Redis...");
|
||||
await LoadFromFileCache(playlistName);
|
||||
var fileAge = DateTime.UtcNow - File.GetLastWriteTimeUtc(filePath);
|
||||
_logger.LogInformation(" {Playlist}: Found file cache (age: {Age:F1}h)", playlistName, fileAge.TotalHours);
|
||||
|
||||
// Load into Redis if not already there
|
||||
if (!await _cache.ExistsAsync(cacheKey))
|
||||
{
|
||||
await LoadFromFileCache(playlistName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
// Check Redis cache
|
||||
if (await _cache.ExistsAsync(cacheKey))
|
||||
{
|
||||
_logger.LogInformation(" Already in Redis");
|
||||
_logger.LogInformation(" {Playlist}: Found in Redis cache", playlistName);
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(" File does not exist at expected path");
|
||||
|
||||
// No cache found for this playlist
|
||||
_logger.LogInformation(" {Playlist}: No cache found", playlistName);
|
||||
allPlaylistsHaveCache = false;
|
||||
}
|
||||
|
||||
var cacheKey = $"spotify:missing:{playlistName}";
|
||||
if (await _cache.ExistsAsync(cacheKey))
|
||||
if (allPlaylistsHaveCache)
|
||||
{
|
||||
_logger.LogInformation(" ✓ Found in Redis cache");
|
||||
_logger.LogInformation("=== ALL PLAYLISTS HAVE CACHE - SKIPPING STARTUP FETCH ===");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(" Not in Redis cache");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Today's sync window has passed (ended at {End})", todaySyncEnd);
|
||||
_logger.LogInformation("Will search for new files");
|
||||
}
|
||||
|
||||
_logger.LogInformation("=== NO RECENT CACHE FOUND - WILL FETCH ===");
|
||||
_logger.LogInformation("=== WILL FETCH ON STARTUP ===");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -321,70 +321,74 @@ public class SpotifyMissingTracksFetcher : BackgroundService
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
|
||||
// Start from the configured sync time (most likely time)
|
||||
// Search forward first (newest files), then backwards to handle timezone differences
|
||||
// We want the file with the furthest forward timestamp (most recent)
|
||||
var now = DateTime.UtcNow;
|
||||
var todaySync = now.Date
|
||||
.AddHours(settings.SyncStartHour)
|
||||
.AddMinutes(settings.SyncStartMinute);
|
||||
|
||||
// If we haven't reached today's sync time yet, start from yesterday's sync time
|
||||
var syncTime = now >= todaySync ? todaySync : todaySync.AddDays(-1);
|
||||
|
||||
_logger.LogInformation(" Searching +12h forward, -24h backward from {SyncTime}", syncTime);
|
||||
_logger.LogInformation(" Searching +24h forward, then -48h backward from {Now}", now);
|
||||
|
||||
var found = false;
|
||||
DateTime? foundFileTime = null;
|
||||
|
||||
// Search forward 12 hours from sync time
|
||||
_logger.LogInformation(" Phase 1: Searching forward 12 hours from sync time...");
|
||||
for (var minutesAhead = 0; minutesAhead <= 720; minutesAhead++) // 720 minutes = 12 hours
|
||||
// First search forward 24 hours (most likely to find newest files with timezone ahead)
|
||||
_logger.LogInformation(" Phase 1: Searching forward 24 hours...");
|
||||
for (var minutesAhead = 1; minutesAhead <= 1440; minutesAhead++)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested) break;
|
||||
|
||||
var time = syncTime.AddMinutes(minutesAhead);
|
||||
var time = now.AddMinutes(minutesAhead);
|
||||
var result = await TryFetchMissingTracksFile(playlistName, time, jellyfinUrl, apiKey, httpClient, cancellationToken, existingFileTime);
|
||||
if (result.found)
|
||||
{
|
||||
found = true;
|
||||
foundFileTime = result.fileTime;
|
||||
break;
|
||||
if (foundFileTime.HasValue)
|
||||
{
|
||||
_logger.LogInformation(" ✓ Found file from {Time} (+{Offset:F1}h ahead)",
|
||||
foundFileTime.Value, (foundFileTime.Value - now).TotalHours);
|
||||
}
|
||||
break; // Found newest file, stop searching
|
||||
}
|
||||
|
||||
// Small delay every 60 requests
|
||||
if (minutesAhead > 0 && minutesAhead % 60 == 0)
|
||||
// Small delay every 60 requests to avoid rate limiting
|
||||
if (minutesAhead % 60 == 0)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Then search backwards 24 hours from sync time to catch yesterday's file
|
||||
// If not found forward, search backwards 48 hours
|
||||
if (!found)
|
||||
{
|
||||
_logger.LogInformation(" Phase 2: Searching backward 24 hours from sync time...");
|
||||
for (var minutesBehind = 1; minutesBehind <= 1440; minutesBehind++) // 1440 minutes = 24 hours
|
||||
_logger.LogInformation(" Phase 2: Searching backward 48 hours...");
|
||||
for (var minutesBehind = 0; minutesBehind <= 2880; minutesBehind++)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested) break;
|
||||
|
||||
var time = syncTime.AddMinutes(-minutesBehind);
|
||||
var time = now.AddMinutes(-minutesBehind);
|
||||
var result = await TryFetchMissingTracksFile(playlistName, time, jellyfinUrl, apiKey, httpClient, cancellationToken, existingFileTime);
|
||||
if (result.found)
|
||||
{
|
||||
found = true;
|
||||
foundFileTime = result.fileTime;
|
||||
if (foundFileTime.HasValue)
|
||||
{
|
||||
_logger.LogInformation(" ✓ Found file from {Time} (-{Offset:F1}h ago)",
|
||||
foundFileTime.Value, (now - foundFileTime.Value).TotalHours);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Small delay every 60 requests
|
||||
if (minutesBehind % 60 == 0)
|
||||
// Small delay every 60 requests to avoid rate limiting
|
||||
if (minutesBehind > 0 && minutesBehind % 60 == 0)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
_logger.LogWarning(" ✗ Could not find new missing tracks file (searched +12h/-24h window)");
|
||||
_logger.LogWarning(" ✗ Could not find new missing tracks file (searched +24h forward, -48h backward)");
|
||||
|
||||
// Keep the existing cache - don't let it expire
|
||||
if (existingTracks != null && existingTracks.Count > 0)
|
||||
|
||||
Reference in New Issue
Block a user