using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; namespace NFOGuard.Emby.Plugin { public class NFOGuardScheduledTask : IScheduledTask { private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly FreeTierManager _freeTierManager; public string Name => "DateSync Task"; public string Key => "NFOGuardSync"; public string Description => "Synchronizes PremiereDate to DateCreated for all TV episodes and movies"; public string Category => "Library"; public NFOGuardScheduledTask(ILibraryManager libraryManager, ILogManager logManager) { _libraryManager = libraryManager; _logger = logManager.GetLogger("NFOGuard"); _freeTierManager = new FreeTierManager(_logger); } public async Task Execute(CancellationToken cancellationToken, IProgress progress) { if (!Plugin.Instance.Configuration.EnableScheduledTask) { _logger.Info("NFOGuard :: Scheduled task is disabled in configuration"); return; } _logger.Info("NFOGuard :: Starting scheduled task execution"); try { // Get all TV episodes and movies var episodes = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { "Episode" }, Recursive = true }).OfType().ToList(); var movies = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { "Movie" }, Recursive = true }).OfType().ToList(); var totalItems = episodes.Count + movies.Count; _logger.Info($"NFOGuard :: Found {episodes.Count} episodes and {movies.Count} movies to process ({totalItems} total)"); // Calculate items that need processing var itemsNeedingSync = episodes.Where(e => e.PremiereDate.HasValue && e.DateCreated != e.PremiereDate.Value).Count() + movies.Where(m => m.PremiereDate.HasValue && m.DateCreated != m.PremiereDate.Value).Count(); _logger.Info($"NFOGuard :: {itemsNeedingSync} items need date synchronization"); // Check free tier limits if (!_freeTierManager.CanProcessItems(itemsNeedingSync, out string reason)) { _logger.Info($"NFOGuard :: {reason}"); // In free tier, process only what we can var pluginConfig = Plugin.Instance.Configuration; if (pluginConfig.MonthlyProcessingLimit > 0 && !pluginConfig.LicenseValid) { var allowedCount = Math.Max(0, pluginConfig.MonthlyProcessingLimit - pluginConfig.ProcessedThisMonth); _logger.Info($"NFOGuard :: Free tier: Processing {allowedCount} items only"); if (allowedCount == 0) { _logger.Info("NFOGuard :: Free tier limit reached. No items will be processed this month."); return; } } } var processedCount = 0; var updatedCount = 0; var config = Plugin.Instance.Configuration; var remainingQuota = config.MonthlyProcessingLimit > 0 && !config.LicenseValid ? Math.Max(0, config.MonthlyProcessingLimit - config.ProcessedThisMonth) : int.MaxValue; // Process episodes for (int i = 0; i < episodes.Count && (config.LicenseValid || remainingQuota > 0); i++) { cancellationToken.ThrowIfCancellationRequested(); var episode = episodes[i]; try { // Always log detailed info to diagnose the issue if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Episode: {episode.Name} :: " + $"PremiereDate: {episode.PremiereDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? "NULL"}, " + $"DateCreated: {episode.DateCreated.ToString("yyyy-MM-dd HH:mm:ss")}"); if (episode.PremiereDate.HasValue && episode.DateCreated != episode.PremiereDate.Value) { _logger.Info($"NFOGuard :: SYNCING episode: {episode.Name} :: " + $"PremiereDate: {episode.PremiereDate.Value}, DateCreated: {episode.DateCreated}"); episode.DateCreated = episode.PremiereDate.Value; episode.UpdateToRepository(ItemUpdateType.MetadataEdit); updatedCount++; // Track usage and decrement quota _freeTierManager.RecordProcessedItems(1); if (!config.LicenseValid && config.MonthlyProcessingLimit > 0) { remainingQuota--; } } else if (!episode.PremiereDate.HasValue) { if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Episode {episode.Name} has no PremiereDate - skipping"); } else { if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Episode {episode.Name} dates already match - no sync needed"); } processedCount++; var progressPercentage = (double)processedCount / totalItems * 100; progress?.Report(progressPercentage); } catch (Exception ex) { _logger.ErrorException($"NFOGuard :: Error processing episode {episode.Name}", ex); } } // Process movies for (int i = 0; i < movies.Count && (config.LicenseValid || remainingQuota > 0); i++) { cancellationToken.ThrowIfCancellationRequested(); var movie = movies[i]; try { // Always log detailed info to diagnose the issue if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Movie: {movie.Name} :: " + $"PremiereDate: {movie.PremiereDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? "NULL"}, " + $"DateCreated: {movie.DateCreated.ToString("yyyy-MM-dd HH:mm:ss")}"); if (movie.PremiereDate.HasValue && movie.DateCreated != movie.PremiereDate.Value) { _logger.Info($"NFOGuard :: SYNCING movie: {movie.Name} :: " + $"PremiereDate: {movie.PremiereDate.Value}, DateCreated: {movie.DateCreated}"); movie.DateCreated = movie.PremiereDate.Value; movie.UpdateToRepository(ItemUpdateType.MetadataEdit); updatedCount++; // Track usage and decrement quota _freeTierManager.RecordProcessedItems(1); if (!config.LicenseValid && config.MonthlyProcessingLimit > 0) { remainingQuota--; } } else if (!movie.PremiereDate.HasValue) { if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Movie {movie.Name} has no PremiereDate - skipping"); } else { if (Plugin.Instance.Configuration.LogVerbose) _logger.Info($"NFOGuard :: Movie {movie.Name} dates already match - no sync needed"); } processedCount++; var progressPercentage = (double)processedCount / totalItems * 100; progress?.Report(progressPercentage); } catch (Exception ex) { _logger.ErrorException($"NFOGuard :: Error processing movie {movie.Name}", ex); } } _logger.Info($"NFOGuard :: Scheduled task completed. Processed: {processedCount} items ({episodes.Count} episodes, {movies.Count} movies), Updated: {updatedCount}"); } catch (Exception ex) { _logger.ErrorException("NFOGuard :: Scheduled task failed", ex); throw; } } public IEnumerable GetDefaultTriggers() { // Default to run weekly at 3 AM on Sunday return new[] { new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerWeekly, DayOfWeek = DayOfWeek.Sunday, TimeOfDayTicks = TimeSpan.FromHours(3).Ticks } }; } } }