From 1966357e29f8f7102b92a86faab7dee41b0c9953 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:35:01 -0600 Subject: [PATCH] Resolve all remaining FIXMEs with credit detection --- .../Entrypoint.cs | 2 +- .../QueueManager.cs | 52 ++++++++++++++++- .../ScheduledTasks/DetectCreditsTask.cs | 14 ++--- .../ScheduledTasks/DetectIntroductionsTask.cs | 57 +++---------------- 4 files changed, 64 insertions(+), 61 deletions(-) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs index a7a176a..a2c1877 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs @@ -60,7 +60,7 @@ public class Entrypoint : IServerEntryPoint // Enqueue all episodes at startup to ensure any FFmpeg errors appear as early as possible _logger.LogInformation("Running startup enqueue"); var queueManager = new QueueManager(_loggerFactory.CreateLogger(), _libraryManager); - queueManager.EnqueueAllEpisodes(); + queueManager.GetMediaItems(); } catch (Exception ex) { diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs index dfc5639..bfe2105 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs @@ -3,6 +3,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; @@ -37,10 +38,10 @@ public class QueueManager } /// - /// Iterates through all libraries on the server and queues all episodes for analysis. + /// Gets all media items on the server. /// /// Queued media items. - public ReadOnlyDictionary> EnqueueAllEpisodes() + public ReadOnlyDictionary> GetMediaItems() { // Assert that ffmpeg with chromaprint is installed if (!FFmpegWrapper.CheckFFmpegVersion()) @@ -220,4 +221,51 @@ public class QueueManager Plugin.Instance!.TotalQueued++; } + + /// + /// Verify that a collection of queued media items still exist in Jellyfin and in storage. + /// This is done to ensure that we don't analyze items that were deleted between the call to GetMediaItems() and popping them from the queue. + /// + /// Queued media items. + /// Analysis mode. + /// Media items that have been verified to exist in Jellyfin and in storage. + public (ReadOnlyCollection VerifiedItems, bool AnyUnanalyzed) + VerifyQueue(ReadOnlyCollection candidates, AnalysisMode mode) + { + var unanalyzed = false; + var verified = new List(); + + var timestamps = mode == AnalysisMode.Introduction ? + Plugin.Instance!.Intros : + Plugin.Instance!.Credits; + + foreach (var candidate in candidates) + { + try + { + var path = Plugin.Instance!.GetItemPath(candidate.EpisodeId); + + if (File.Exists(path)) + { + verified.Add(candidate); + } + + if (!timestamps.ContainsKey(candidate.EpisodeId)) + { + unanalyzed = true; + } + } + catch (Exception ex) + { + _logger.LogDebug( + "Skipping {Mode} analysis of {Name} ({Id}): {Exception}", + mode, + candidate.Name, + candidate.EpisodeId, + ex); + } + } + + return (verified.AsReadOnly(), unanalyzed); + } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs index 74ef0c0..fa05f34 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs @@ -9,10 +9,6 @@ using Microsoft.Extensions.Logging; namespace ConfusedPolarBear.Plugin.IntroSkipper; -#if !DEBUG -#error Fix all FIXMEs introduced during initial credit implementation before release -#endif - /// /// Analyze all television episodes for credits. /// @@ -84,7 +80,7 @@ public class DetectCreditsTask : IScheduledTask _loggerFactory.CreateLogger(), _libraryManager); - var queue = queueManager.EnqueueAllEpisodes(); + var queue = queueManager.GetMediaItems(); if (queue.Count == 0) { @@ -101,9 +97,11 @@ public class DetectCreditsTask : IScheduledTask // Analyze all episodes in the queue using the degrees of parallelism the user specified. Parallel.ForEach(queue, options, (season) => { - // TODO: FIXME: use VerifyEpisodes - var episodes = season.Value.AsReadOnly(); - if (episodes.Count == 0) + var (episodes, unanalyzed) = queueManager.VerifyQueue( + season.Value.AsReadOnly(), + AnalysisMode.Credits); + + if (episodes.Count == 0 || unanalyzed) { return; } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs index 83df267..23890a0 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntroductionsTask.cs @@ -83,7 +83,7 @@ public class DetectIntroductionsTask : IScheduledTask _loggerFactory.CreateLogger(), _libraryManager); - var queue = queueManager.EnqueueAllEpisodes(); + var queue = queueManager.GetMediaItems(); if (queue.Count == 0) { @@ -100,13 +100,15 @@ public class DetectIntroductionsTask : IScheduledTask MaxDegreeOfParallelism = Plugin.Instance!.Configuration.MaxParallelism }; - // TODO: if the queue is modified while the task is running, the task will fail. - // clone the queue before running the task to prevent this. - // Analyze all episodes in the queue using the degrees of parallelism the user specified. Parallel.ForEach(queue, options, (season) => { - var (episodes, unanalyzed) = VerifyEpisodes(season.Value.AsReadOnly()); + // Since the first run of the task can run for multiple hours, ensure that none + // of the current media items were deleted from Jellyfin since the task was started. + var (episodes, unanalyzed) = queueManager.VerifyQueue( + season.Value.AsReadOnly(), + AnalysisMode.Introduction); + if (episodes.Count == 0) { return; @@ -178,51 +180,6 @@ public class DetectIntroductionsTask : IScheduledTask return Task.CompletedTask; } - /// - /// Verify that all episodes in a season exist in Jellyfin and as a file in storage. - /// TODO: FIXME: move to queue manager. - /// - /// QueuedEpisodes. - /// Verified QueuedEpisodes and a flag indicating if any episode in this season has not been analyzed yet. - private ( - ReadOnlyCollection VerifiedEpisodes, - bool AnyUnanalyzed) - VerifyEpisodes(ReadOnlyCollection candidates) - { - var unanalyzed = false; - var verified = new List(); - - foreach (var candidate in candidates) - { - try - { - // Verify that the episode exists in Jellyfin and in storage - var path = Plugin.Instance!.GetItemPath(candidate.EpisodeId); - - if (File.Exists(path)) - { - verified.Add(candidate); - } - - // Flag this season for analysis if the current episode hasn't been analyzed yet - if (!Plugin.Instance.Intros.ContainsKey(candidate.EpisodeId)) - { - unanalyzed = true; - } - } - catch (Exception ex) - { - _logger.LogDebug( - "Skipping analysis of {Name} ({Id}): {Exception}", - candidate.Name, - candidate.EpisodeId, - ex); - } - } - - return (verified.AsReadOnly(), unanalyzed); - } - /// /// Fingerprints all episodes in the provided season and stores the timestamps of all introductions. ///