diff --git a/allstarr/Services/Spotify/SpotifyMissingTracksFetcher.cs b/allstarr/Services/Spotify/SpotifyMissingTracksFetcher.cs index b0e097c..41fff44 100644 --- a/allstarr/Services/Spotify/SpotifyMissingTracksFetcher.cs +++ b/allstarr/Services/Spotify/SpotifyMissingTracksFetcher.cs @@ -80,10 +80,11 @@ public class SpotifyMissingTracksFetcher : BackgroundService var syncEndTime = syncTime.AddHours(settings.SyncWindowHours); _logger.LogInformation("Search Schedule:"); - _logger.LogInformation(" Plugin sync time: {Time:HH:mm} (configured)", syncTime); - _logger.LogInformation(" Search window: {Start:HH:mm} - {End:HH:mm} ({Hours}h window)", + _logger.LogInformation(" Plugin sync time: {Time:HH:mm} UTC (configured)", syncTime); + _logger.LogInformation(" Search window: {Start:HH:mm} - {End:HH:mm} UTC ({Hours}h window)", syncTime, syncEndTime, settings.SyncWindowHours); - _logger.LogInformation(" Will search for missing tracks files every 5 minutes during window"); + _logger.LogInformation(" Will search for new files once per day after sync window ends"); + _logger.LogInformation(" Background check interval: 5 minutes"); // Fetch playlist names from Jellyfin await LoadPlaylistNamesAsync(); @@ -114,7 +115,7 @@ public class SpotifyMissingTracksFetcher : BackgroundService } else { - _logger.LogInformation("Skipping startup fetch - existing cache is still current"); + _logger.LogInformation("Skipping startup fetch - already have current files"); _hasRunOnce = true; } } @@ -123,7 +124,13 @@ public class SpotifyMissingTracksFetcher : BackgroundService { try { - await FetchMissingTracksAsync(stoppingToken); + // Only fetch if we're past today's sync window AND we haven't fetched today yet + var shouldFetch = await ShouldFetchNowAsync(); + if (shouldFetch) + { + await FetchMissingTracksAsync(stoppingToken); + _hasRunOnce = true; + } } catch (Exception ex) { @@ -134,6 +141,47 @@ public class SpotifyMissingTracksFetcher : BackgroundService } } + private async Task ShouldFetchNowAsync() + { + var settings = _spotifySettings.Value; + var now = DateTime.UtcNow; + + // Calculate today's sync window + var todaySync = now.Date + .AddHours(settings.SyncStartHour) + .AddMinutes(settings.SyncStartMinute); + var todaySyncEnd = todaySync.AddHours(settings.SyncWindowHours); + + // Only fetch if we're past today's sync window + if (now < todaySyncEnd) + { + return false; + } + + // Check if we already have today's files + foreach (var playlistName in _playlistIdToName.Values) + { + var filePath = GetCacheFilePath(playlistName); + + if (File.Exists(filePath)) + { + var fileTime = File.GetLastWriteTimeUtc(filePath); + + // If file is from today's sync or later, we already have it + if (fileTime >= todaySync) + { + continue; + } + } + + // Missing today's file for this playlist + return true; + } + + // All playlists have today's files + return false; + } + private async Task LoadPlaylistNamesAsync() { _playlistIdToName.Clear(); @@ -157,19 +205,22 @@ public class SpotifyMissingTracksFetcher : BackgroundService var settings = _spotifySettings.Value; var now = DateTime.UtcNow; - // Calculate when today's sync window ends + // Calculate today's sync window 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 + _logger.LogInformation("Today's sync window: {Start:yyyy-MM-dd HH:mm} - {End:yyyy-MM-dd HH:mm} UTC", + todaySync, todaySyncEnd); + _logger.LogInformation("Current time: {Now:yyyy-MM-dd HH:mm} UTC", now); + + // If we're still before today's sync window end, we should have yesterday's or today's file + // Don't search again until after today's sync window ends if (now < todaySyncEnd) { - _logger.LogInformation("Today's sync window hasn't ended yet (ends at {End})", todaySyncEnd); - _logger.LogInformation("Checking if we have a recent cache file..."); + _logger.LogInformation("We're before today's sync window end - checking if we have recent cache..."); - // Check if we have any cache (file or Redis) for all playlists var allPlaylistsHaveCache = true; foreach (var playlistName in _playlistIdToName.Values) @@ -206,13 +257,65 @@ public class SpotifyMissingTracksFetcher : BackgroundService if (allPlaylistsHaveCache) { _logger.LogInformation("=== ALL PLAYLISTS HAVE CACHE - SKIPPING STARTUP FETCH ==="); + _logger.LogInformation("Will search again after {Time:yyyy-MM-dd HH:mm} UTC", todaySyncEnd); return false; } } - else + + // If we're after today's sync window end, check if we already have today's file + if (now >= todaySyncEnd) { - _logger.LogInformation("Today's sync window has passed (ended at {End})", todaySyncEnd); - _logger.LogInformation("Will search for new files"); + _logger.LogInformation("We're after today's sync window end - checking if we already fetched today's files..."); + + var allPlaylistsHaveTodaysFile = true; + + foreach (var playlistName in _playlistIdToName.Values) + { + var filePath = GetCacheFilePath(playlistName); + var cacheKey = $"spotify:missing:{playlistName}"; + + // Check if file exists and was created today (after sync start) + if (File.Exists(filePath)) + { + var fileTime = File.GetLastWriteTimeUtc(filePath); + + // File should be from today's sync window or later + if (fileTime >= todaySync) + { + var fileAge = DateTime.UtcNow - fileTime; + _logger.LogInformation(" {Playlist}: Have today's file (created {Time:yyyy-MM-dd HH:mm}, age: {Age:F1}h)", + playlistName, fileTime, fileAge.TotalHours); + + // Load into Redis if not already there + if (!await _cache.ExistsAsync(cacheKey)) + { + await LoadFromFileCache(playlistName); + } + continue; + } + else + { + _logger.LogInformation(" {Playlist}: File is old (from {Time:yyyy-MM-dd HH:mm}, before today's sync)", + playlistName, fileTime); + } + } + else + { + _logger.LogInformation(" {Playlist}: No file found", playlistName); + } + + allPlaylistsHaveTodaysFile = false; + } + + if (allPlaylistsHaveTodaysFile) + { + _logger.LogInformation("=== ALL PLAYLISTS HAVE TODAY'S FILES - SKIPPING STARTUP FETCH ==="); + + // Calculate when to search next (tomorrow after sync window) + var tomorrowSyncEnd = todaySyncEnd.AddDays(1); + _logger.LogInformation("Will search again after {Time:yyyy-MM-dd HH:mm} UTC", tomorrowSyncEnd); + return false; + } } _logger.LogInformation("=== WILL FETCH ON STARTUP ===");