diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs index 5ae2aa9..159e950 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs @@ -34,4 +34,9 @@ public enum EdlAction /// Show a skip button. /// Intro, + + /// + /// Show a skip button. + /// + Credit, } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs index 0620c78..3f5ff66 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs @@ -63,14 +63,12 @@ public static class EdlManager { var id = episode.EpisodeId; - if (!Plugin.Instance!.Intros.TryGetValue(id, out var intro)) + bool hasIntro = Plugin.Instance!.Intros.TryGetValue(id, out var intro) && intro.Valid; + bool hasCredit = Plugin.Instance!.Credits.TryGetValue(id, out var credit) && credit.Valid; + + if (!hasIntro && !hasCredit) { - _logger?.LogDebug("Episode {Id} did not have an introduction, skipping", id); - continue; - } - else if (!intro.Valid) - { - _logger?.LogDebug("Episode {Id} did not have a valid introduction, skipping", id); + _logger?.LogDebug("Episode {Id} has neither a valid intro nor credit, skipping", id); continue; } @@ -84,7 +82,31 @@ public static class EdlManager continue; } - File.WriteAllText(edlPath, intro.ToEdl(action)); + var edlContent = string.Empty; + + if (hasIntro) + { + edlContent += intro?.ToEdl(action); + } + + if (hasCredit) + { + if (edlContent.Length > 0) + { + edlContent += Environment.NewLine; + } + + if (action == EdlAction.Intro) + { + edlContent += credit?.ToEdl(EdlAction.Credit); + } + else + { + edlContent += credit?.ToEdl(action); + } + } + + File.WriteAllText(edlPath, edlContent); } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs index 6b5b501..311e69c 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Entrypoint.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -259,45 +261,33 @@ public class Entrypoint : IServerEntryPoint var progress = new Progress(); var cancellationToken = _cancellationTokenSource.Token; + var modes = new List(); + var tasklogger = _loggerFactory.CreateLogger("DefaultLogger"); + if (Plugin.Instance!.Configuration.AutoDetectIntros && Plugin.Instance!.Configuration.AutoDetectCredits) { - // This is where we can optimize a single scan - var baseIntroAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Introduction, - _loggerFactory.CreateLogger(), - _loggerFactory, - _libraryManager); - - baseIntroAnalyzer.AnalyzeItems(progress, cancellationToken); - - var baseCreditAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Credits, - _loggerFactory.CreateLogger(), - _loggerFactory, - _libraryManager); - - baseCreditAnalyzer.AnalyzeItems(progress, cancellationToken); + modes.Add(AnalysisMode.Introduction); + modes.Add(AnalysisMode.Credits); + tasklogger = _loggerFactory.CreateLogger(); } else if (Plugin.Instance!.Configuration.AutoDetectIntros) { - var baseIntroAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Introduction, - _loggerFactory.CreateLogger(), - _loggerFactory, - _libraryManager); - - baseIntroAnalyzer.AnalyzeItems(progress, cancellationToken); + modes.Add(AnalysisMode.Introduction); + tasklogger = _loggerFactory.CreateLogger(); } else if (Plugin.Instance!.Configuration.AutoDetectCredits) { - var baseCreditAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Credits, - _loggerFactory.CreateLogger(), + modes.Add(AnalysisMode.Credits); + tasklogger = _loggerFactory.CreateLogger(); + } + + var baseCreditAnalyzer = new BaseItemAnalyzerTask( + modes.AsReadOnly(), + tasklogger, _loggerFactory, _libraryManager); - baseCreditAnalyzer.AnalyzeItems(progress, cancellationToken); - } + baseCreditAnalyzer.AnalyzeItems(progress, cancellationToken); } Plugin.Instance!.Configuration.PathRestrictions.Clear(); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs index 564d631..4c123df 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs @@ -231,17 +231,16 @@ public class QueueManager /// 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. + /// 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) + public (ReadOnlyCollection VerifiedItems, ReadOnlyCollection RequiredModes) + VerifyQueue(ReadOnlyCollection candidates, ReadOnlyCollection modes) { - var unanalyzed = false; var verified = new List(); + var reqModes = new List(); - var timestamps = mode == AnalysisMode.Introduction ? - Plugin.Instance!.Intros : - Plugin.Instance!.Credits; + var requiresIntroAnalysis = modes.Contains(AnalysisMode.Introduction); + var requiresCreditsAnalysis = modes.Contains(AnalysisMode.Credits); foreach (var candidate in candidates) { @@ -252,24 +251,31 @@ public class QueueManager if (File.Exists(path)) { verified.Add(candidate); - } - if (!timestamps.ContainsKey(candidate.EpisodeId)) - { - unanalyzed = true; + if (requiresIntroAnalysis && !Plugin.Instance!.Intros.ContainsKey(candidate.EpisodeId)) + { + reqModes.Add(AnalysisMode.Introduction); + requiresIntroAnalysis = false; // No need to check again + } + + if (requiresCreditsAnalysis && !Plugin.Instance!.Credits.ContainsKey(candidate.EpisodeId)) + { + reqModes.Add(AnalysisMode.Credits); + requiresCreditsAnalysis = false; // No need to check again + } } } catch (Exception ex) { _logger.LogDebug( "Skipping {Mode} analysis of {Name} ({Id}): {Exception}", - mode, + modes, candidate.Name, candidate.EpisodeId, ex); } } - return (verified.AsReadOnly(), unanalyzed); + return (verified.AsReadOnly(), reqModes.AsReadOnly()); } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs index 3eabd6d..9161383 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs @@ -1,6 +1,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; @@ -12,7 +13,7 @@ using Microsoft.Extensions.Logging; /// public class BaseItemAnalyzerTask { - private readonly AnalysisMode _analysisMode; + private readonly ReadOnlyCollection _analysisModes; private readonly ILogger _logger; @@ -23,22 +24,22 @@ public class BaseItemAnalyzerTask /// /// Initializes a new instance of the class. /// - /// Analysis mode. + /// Analysis mode. /// Task logger. /// Logger factory. /// Library manager. public BaseItemAnalyzerTask( - AnalysisMode mode, + ReadOnlyCollection modes, ILogger logger, ILoggerFactory loggerFactory, ILibraryManager libraryManager) { - _analysisMode = mode; + _analysisModes = modes; _logger = logger; _loggerFactory = loggerFactory; _libraryManager = libraryManager; - if (mode == AnalysisMode.Introduction) + if (Plugin.Instance!.Configuration.EdlAction != EdlAction.None) { EdlManager.Initialize(_logger); } @@ -79,7 +80,7 @@ public class BaseItemAnalyzerTask "No episodes to analyze. If you are limiting the list of libraries to analyze, check that all library names have been spelled correctly."); } - if (this._analysisMode == AnalysisMode.Introduction) + if (Plugin.Instance!.Configuration.EdlAction != EdlAction.None) { EdlManager.LogConfiguration(); } @@ -96,9 +97,9 @@ public class BaseItemAnalyzerTask // 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( + var (episodes, requiredModes) = queueManager.VerifyQueue( season.Value.AsReadOnly(), - this._analysisMode); + _analysisModes); if (episodes.Count == 0) { @@ -107,7 +108,7 @@ public class BaseItemAnalyzerTask var first = episodes[0]; - if (!unanalyzed) + if (requiredModes.Count == 0) { _logger.LogDebug( "All episodes in {Name} season {Season} have already been analyzed", @@ -124,10 +125,13 @@ public class BaseItemAnalyzerTask return; } - var analyzed = AnalyzeItems(episodes, cancellationToken); - Interlocked.Add(ref totalProcessed, analyzed); + foreach (AnalysisMode mode in requiredModes) + { + var analyzed = AnalyzeItems(episodes, mode, cancellationToken); + Interlocked.Add(ref totalProcessed, analyzed); - writeEdl = analyzed > 0 || Plugin.Instance!.Configuration.RegenerateEdlFiles; + writeEdl = analyzed > 0 || Plugin.Instance!.Configuration.RegenerateEdlFiles; + } } catch (FingerprintException ex) { @@ -138,34 +142,15 @@ public class BaseItemAnalyzerTask ex); } - if ( - writeEdl && - Plugin.Instance!.Configuration.EdlAction != EdlAction.None && - _analysisMode == AnalysisMode.Introduction) + if (writeEdl && Plugin.Instance!.Configuration.EdlAction != EdlAction.None) { EdlManager.UpdateEDLFiles(episodes); } - if (_logger is ILogger) - { - if (_analysisMode == AnalysisMode.Introduction) - { - progress.Report(((totalProcessed * 100) / totalQueued) / 2); - } - else - { - progress.Report((((totalProcessed * 100) / totalQueued) / 2) + 50); - } - } - else - { - progress.Report((totalProcessed * 100) / totalQueued); - } + progress.Report((totalProcessed * 100) / totalQueued); }); - if ( - _analysisMode == AnalysisMode.Introduction && - Plugin.Instance!.Configuration.RegenerateEdlFiles) + if (Plugin.Instance!.Configuration.RegenerateEdlFiles) { _logger.LogInformation("Turning EDL file regeneration flag off"); Plugin.Instance!.Configuration.RegenerateEdlFiles = false; @@ -177,10 +162,12 @@ public class BaseItemAnalyzerTask /// Analyze a group of media items for skippable segments. /// /// Media items to analyze. + /// Analysis mode. /// Cancellation token. /// Number of items that were successfully analyzed. private int AnalyzeItems( ReadOnlyCollection items, + AnalysisMode mode, CancellationToken cancellationToken) { var totalItems = items.Count; @@ -206,7 +193,7 @@ public class BaseItemAnalyzerTask analyzers.Add(new ChromaprintAnalyzer(_loggerFactory.CreateLogger())); } - if (this._analysisMode == AnalysisMode.Credits) + if (mode == AnalysisMode.Credits) { analyzers.Add(new BlackFrameAnalyzer(_loggerFactory.CreateLogger())); } @@ -215,7 +202,7 @@ public class BaseItemAnalyzerTask // analyzed items from the queue. foreach (var analyzer in analyzers) { - items = analyzer.AnalyzeMediaFiles(items, this._analysisMode, cancellationToken); + items = analyzer.AnalyzeMediaFiles(items, mode, cancellationToken); } return totalItems; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs index c2d71c7..b7b17e4 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectCreditsTask.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -87,9 +88,10 @@ public class DetectCreditsTask : IScheduledTask _logger.LogInformation("Scheduled Task is starting"); Plugin.Instance!.Configuration.PathRestrictions.Clear(); + var modes = new List { AnalysisMode.Credits }; var baseCreditAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Credits, + modes.AsReadOnly(), _loggerFactory.CreateLogger(), _loggerFactory, _libraryManager); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosCreditsTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosCreditsTask.cs index 925d5c0..aea91ab 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosCreditsTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosCreditsTask.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -86,23 +87,16 @@ public class DetectIntrosCreditsTask : IScheduledTask _logger.LogInformation("Scheduled Task is starting"); Plugin.Instance!.Configuration.PathRestrictions.Clear(); + var modes = new List { AnalysisMode.Introduction, AnalysisMode.Credits }; var baseIntroAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Introduction, + modes.AsReadOnly(), _loggerFactory.CreateLogger(), _loggerFactory, _libraryManager); baseIntroAnalyzer.AnalyzeItems(progress, cancellationToken); - var baseCreditAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Credits, - _loggerFactory.CreateLogger(), - _loggerFactory, - _libraryManager); - - baseCreditAnalyzer.AnalyzeItems(progress, cancellationToken); - ScheduledTaskSemaphore.Release(); return Task.CompletedTask; } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosTask.cs index d6afc35..1ba8900 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/DetectIntrosTask.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; @@ -86,9 +87,10 @@ public class DetectIntrosTask : IScheduledTask _logger.LogInformation("Scheduled Task is starting"); Plugin.Instance!.Configuration.PathRestrictions.Clear(); + var modes = new List { AnalysisMode.Introduction }; var baseIntroAnalyzer = new BaseItemAnalyzerTask( - AnalysisMode.Introduction, + modes.AsReadOnly(), _loggerFactory.CreateLogger(), _loggerFactory, _libraryManager);