From 9dbe6847905ab9dee7648ce3e1e687854d644c01 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Wed, 15 Jun 2022 01:00:03 -0500 Subject: [PATCH 1/5] Add initial support for writing EDL files --- CHANGELOG.md | 2 +- .../TestEdl.cs | 44 ++++++++++++++ .../Configuration/PluginConfiguration.cs | 9 +++ .../Configuration/configPage.html | 41 +++++++++++++ .../Data/EdlAction.cs | 37 ++++++++++++ .../Data/Intro.cs | 18 ++++++ .../EdlManager.cs | 59 +++++++++++++++++++ .../Plugin.cs | 17 +++++- .../ScheduledTasks/FingerprinterTask.cs | 10 ++++ 9 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs create mode 100644 ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e4f5d..8fffef0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v0.1.6.0 +## v0.1.6.0 (unreleased) * Write EDL files with intro timestamps * Restore per season status updates diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs new file mode 100644 index 0000000..fd71c42 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs @@ -0,0 +1,44 @@ +using System; +using Xunit; + +namespace ConfusedPolarBear.Plugin.IntroSkipper.Tests; + +public class TestEdl +{ + // Test data is from https://kodi.wiki/view/Edit_decision_list#MPlayer_EDL + [Theory] + [InlineData(5.3, 7.1, EdlAction.Cut, "5.3 7.1 0")] + [InlineData(15, 16.7, EdlAction.Mute, "15 16.7 1")] + [InlineData(420, 822, EdlAction.CommercialBreak, "420 822 3")] + [InlineData(1, 255.3, EdlAction.SceneMarker, "1 255.3 2")] + [InlineData(1.123456789, 5.654647987, EdlAction.CommercialBreak, "1.12 5.65 3")] + public void TestEdlSerialization(double start, double end, EdlAction action, string expected) + { + var intro = MakeIntro(start, end); + var actual = intro.ToEdl(action); + + Assert.Equal(expected, actual); + } + + [Fact] + public void TestEdlInvalidSerialization() + { + Assert.Throws(() => { + var intro = MakeIntro(0, 5); + intro.ToEdl(EdlAction.None); + }); + } + + [Theory] + [InlineData("Death Note - S01E12 - Love.mkv", "Death Note - S01E12 - Love.edl")] + [InlineData("/full/path/to/file.rm", "/full/path/to/file.edl")] + public void TestEdlPath(string mediaPath, string edlPath) + { + Assert.Equal(edlPath, EdlManager.GetEdlPath(mediaPath)); + } + + private Intro MakeIntro(double start, double end) + { + return new Intro(Guid.Empty, new TimeRange(start, end)); + } +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs index 5a31357..5242088 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs @@ -14,6 +14,8 @@ public class PluginConfiguration : BasePluginConfiguration { } + // ===== Analysis settings ===== + /// /// Gets or sets a value indicating whether the episode's fingerprint should be cached to the filesystem. /// @@ -24,6 +26,13 @@ public class PluginConfiguration : BasePluginConfiguration /// public int MaxParallelism { get; set; } = 2; + /// + /// Gets or sets a value indicating the action to write to created EDL files. + /// + public EdlAction EdlAction { get; set; } = EdlAction.None; + + // ===== Playback settings ===== + /// /// Gets or sets a value indicating whether introductions should be automatically skipped. /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index 54d8930..d681ff6 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -37,6 +37,45 @@ Maximum degree of parallelism to use when analyzing episodes. + +
+ + + +
+ If set to a value other than None, specifies which action to write to + MPlayer compatible EDL files + alongside your episode files.
+ + If this value is changed from None, the plugin will overwrite any EDL files + associated with your episode files. + +
+
@@ -438,6 +477,7 @@ ApiClient.getPluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b").then(function (config) { document.querySelector('#AutoSkip').checked = config.AutoSkip; document.querySelector('#MaxParallelism').value = config.MaxParallelism; + document.querySelector('#EdlAction').value = config.EdlAction; document.querySelector('#CacheFingerprints').checked = config.CacheFingerprints; document.querySelector('#ShowPromptAdjustment').value = config.ShowPromptAdjustment; @@ -453,6 +493,7 @@ ApiClient.getPluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b").then(function (config) { config.AutoSkip = document.querySelector('#AutoSkip').checked; config.MaxParallelism = document.querySelector('#MaxParallelism').value; + config.EdlAction = document.querySelector('#EdlAction').value; config.CacheFingerprints = document.querySelector('#CacheFingerprints').checked; config.ShowPromptAdjustment = document.querySelector("#ShowPromptAdjustment").value; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs new file mode 100644 index 0000000..5ae2aa9 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/EdlAction.cs @@ -0,0 +1,37 @@ +namespace ConfusedPolarBear.Plugin.IntroSkipper; + +/// +/// Taken from https://kodi.wiki/view/Edit_decision_list#MPlayer_EDL. +/// +public enum EdlAction +{ + /// + /// Do not create EDL files. + /// + None = -1, + + /// + /// Completely remove the intro from playback as if it was never in the original video. + /// + Cut, + + /// + /// Mute audio, continue playback. + /// + Mute, + + /// + /// Inserts a new scene marker. + /// + SceneMarker, + + /// + /// Automatically skip the intro once during playback. + /// + CommercialBreak, + + /// + /// Show a skip button. + /// + Intro, +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs index f5adad9..3b174ee 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs @@ -68,4 +68,22 @@ public class Intro /// Gets or sets the recommended time to hide the skip intro prompt. /// public double HideSkipPromptAt { get; set; } + + /// + /// Convert this Intro object to a Kodi compatible EDL entry. + /// + /// User specified configuration EDL action. + /// String. + public string ToEdl(EdlAction action) + { + if (action == EdlAction.None) + { + throw new ArgumentException("Cannot serialize an EdlAction of None"); + } + + var start = Math.Round(IntroStart, 2); + var end = Math.Round(IntroEnd, 2); + + return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action); + } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs new file mode 100644 index 0000000..6340e0d --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs @@ -0,0 +1,59 @@ +namespace ConfusedPolarBear.Plugin.IntroSkipper; + +using System.Collections.ObjectModel; +using System.IO; +using Microsoft.Extensions.Logging; + +/// +/// Update EDL files associated with a list of episodes. +/// +public static class EdlManager +{ + private static ILogger? _logger; + + /// + /// Initialize EDLManager with a logger. + /// + /// ILogger. + public static void Initialize(ILogger logger) + { + _logger = logger; + } + + /// + /// If the EDL action is set to a value other than None, update EDL files for the provided episodes. + /// + /// Episodes to update EDL files for. + public static void UpdateEDLFiles(ReadOnlyCollection episodes) + { + var action = Plugin.Instance!.Configuration.EdlAction; + if (action == EdlAction.None) + { + _logger?.LogDebug("EDL action is set to none, not updating EDL files"); + return; + } + + _logger?.LogDebug("Updating EDL files with action {Action}", action); + + foreach (var episode in episodes) + { + var id = episode.EpisodeId; + var intro = Plugin.Instance!.Intros[id]; + var edlPath = GetEdlPath(Plugin.Instance!.GetItemPath(id)); + + _logger?.LogTrace("Episode {Id} has EDL path {Path}", id, edlPath); + + File.WriteAllText(edlPath, intro.ToEdl(action)); + } + } + + /// + /// Given the path to an episode, return the path to the associated EDL file. + /// + /// Full path to episode. + /// Full path to EDL file. + public static string GetEdlPath(string mediaPath) + { + return Path.ChangeExtension(mediaPath, "edl"); + } +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs index d69d790..5e35194 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs @@ -6,6 +6,7 @@ using ConfusedPolarBear.Plugin.IntroSkipper.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Serialization; @@ -17,6 +18,7 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper; public class Plugin : BasePlugin, IHasWebPages { private IXmlSerializer _xmlSerializer; + private ILibraryManager _libraryManager; private string _introPath; /// @@ -25,13 +27,16 @@ public class Plugin : BasePlugin, IHasWebPages /// Instance of the interface. /// Instance of the interface. /// Server configuration manager. + /// Library manager. public Plugin( IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, - IServerConfigurationManager serverConfiguration) + IServerConfigurationManager serverConfiguration, + ILibraryManager libraryManager) : base(applicationPaths, xmlSerializer) { _xmlSerializer = xmlSerializer; + _libraryManager = libraryManager; // Create the base & cache directories (if needed). FingerprintCachePath = Path.Join(applicationPaths.PluginConfigurationsPath, "intros", "cache"); @@ -129,6 +134,16 @@ public class Plugin : BasePlugin, IHasWebPages } } + /// + /// Gets the full path for an item. + /// + /// Item id. + /// Full path to item. + internal string GetItemPath(Guid id) + { + return _libraryManager.GetItemById(id).Path; + } + /// public IEnumerable GetPages() { diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs index 23e7f7b..24ee83a 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs @@ -88,6 +88,8 @@ public class FingerprinterTask : IScheduledTask _queueLogger = loggerFactory.CreateLogger(); _fingerprintCache = new Dictionary>(); + + EdlManager.Initialize(_logger); } /// @@ -146,6 +148,7 @@ public class FingerprinterTask : IScheduledTask Parallel.ForEach(queue, options, (season) => { var first = season.Value[0]; + var writeEdl = false; try { @@ -153,6 +156,7 @@ public class FingerprinterTask : IScheduledTask // (instead of just using the number of episodes in the current season). var analyzed = AnalyzeSeason(season, cancellationToken); Interlocked.Add(ref totalProcessed, analyzed); + writeEdl = analyzed > 0; } catch (FingerprintException ex) { @@ -180,6 +184,12 @@ public class FingerprinterTask : IScheduledTask } } + if (writeEdl && Plugin.Instance!.Configuration.EdlAction != EdlAction.None) + { + EdlManager.UpdateEDLFiles(season.Value.AsReadOnly()); + } + + totalProcessed += season.Value.Count; progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued); }); From b83fcdd3a1d5830a8f10d166fff51fec7274a363 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Mon, 20 Jun 2022 01:39:56 -0500 Subject: [PATCH 2/5] Make overwriting existing EDL files optional --- .../Configuration/PluginConfiguration.cs | 5 ++++ .../Configuration/configPage.html | 29 ++++++++++++++----- .../EdlManager.cs | 7 +++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs index 5242088..c712b3b 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs @@ -31,6 +31,11 @@ public class PluginConfiguration : BasePluginConfiguration /// public EdlAction EdlAction { get; set; } = EdlAction.None; + /// + /// Gets or sets a value indicating whether or not to overwrite existing EDL files. + /// + public bool OverwriteExistingEdlFiles { get; set; } = false; + // ===== Playback settings ===== /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index d681ff6..540657a 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -70,10 +70,20 @@ If set to a value other than None, specifies which action to write to MPlayer compatible EDL files alongside your episode files.
- - If this value is changed from None, the plugin will overwrite any EDL files - associated with your episode files. - + + If this value is changed after EDL files are generated, you must either manually delete + the EDL files or check the "Overwrite existing EDL files" checkbox below. + + + +
+ + +
+ If checked, the plugin will overwrite any EDL files associated with your episode files.
@@ -477,7 +487,9 @@ ApiClient.getPluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b").then(function (config) { document.querySelector('#AutoSkip').checked = config.AutoSkip; document.querySelector('#MaxParallelism').value = config.MaxParallelism; + document.querySelector('#EdlAction').value = config.EdlAction; + document.querySelector('#OverwriteEdl').checked = config.OverwriteExistingEdlFiles; document.querySelector('#CacheFingerprints').checked = config.CacheFingerprints; document.querySelector('#ShowPromptAdjustment').value = config.ShowPromptAdjustment; @@ -493,15 +505,18 @@ ApiClient.getPluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b").then(function (config) { config.AutoSkip = document.querySelector('#AutoSkip').checked; config.MaxParallelism = document.querySelector('#MaxParallelism').value; + config.EdlAction = document.querySelector('#EdlAction').value; + config.OverwriteExistingEdlFiles = document.querySelector('#OverwriteEdl').checked; config.CacheFingerprints = document.querySelector('#CacheFingerprints').checked; config.ShowPromptAdjustment = document.querySelector("#ShowPromptAdjustment").value; config.HidePromptAdjustment = document.querySelector("#HidePromptAdjustment").value; - ApiClient.updatePluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b", config).then(function (result) { - Dashboard.processPluginConfigurationUpdateResult(result); - }); + ApiClient.updatePluginConfiguration("c83d86bb-a1e0-4c35-a113-e2101cf4ee6b", config) + .then(function (result) { + Dashboard.processPluginConfigurationUpdateResult(result); + }); }); e.preventDefault(); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs index 6340e0d..38e2d16 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs @@ -26,6 +26,7 @@ public static class EdlManager /// Episodes to update EDL files for. public static void UpdateEDLFiles(ReadOnlyCollection episodes) { + var overwrite = Plugin.Instance!.Configuration.OverwriteExistingEdlFiles; var action = Plugin.Instance!.Configuration.EdlAction; if (action == EdlAction.None) { @@ -43,6 +44,12 @@ public static class EdlManager _logger?.LogTrace("Episode {Id} has EDL path {Path}", id, edlPath); + if (!overwrite && File.Exists(edlPath)) + { + _logger?.LogTrace("Refusing to overwrite existing EDL file {Path}", edlPath); + continue; + } + File.WriteAllText(edlPath, intro.ToEdl(action)); } } From 7591025297bf59f3cf78dcc36671c764dfe7b15b Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Fri, 24 Jun 2022 00:02:08 -0500 Subject: [PATCH 3/5] Add EDL regeneration flag --- .../Configuration/PluginConfiguration.cs | 6 ++-- .../Configuration/configPage.html | 13 ++++--- .../EdlManager.cs | 35 +++++++++++++++++-- .../ScheduledTasks/FingerprinterTask.cs | 14 +++++++- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs index c712b3b..d62acef 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs @@ -32,9 +32,11 @@ public class PluginConfiguration : BasePluginConfiguration public EdlAction EdlAction { get; set; } = EdlAction.None; /// - /// Gets or sets a value indicating whether or not to overwrite existing EDL files. + /// Gets or sets a value indicating whether to regenerate all EDL files during the next scan. + /// By default, EDL files are only written for a season if the season had at least one newly analyzed episode. + /// If this is set, all EDL files will be regenerated and overwrite any existing EDL file. /// - public bool OverwriteExistingEdlFiles { get; set; } = false; + public bool RegenerateEdlFiles { get; set; } = false; // ===== Playback settings ===== diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index 540657a..c42b0b6 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -71,19 +71,18 @@ MPlayer compatible EDL files alongside your episode files.
- If this value is changed after EDL files are generated, you must either manually delete - the EDL files or check the "Overwrite existing EDL files" checkbox below. + If this value is changed after EDL files are generated, you must check the "Regenerate EDL files" checkbox below.
- If checked, the plugin will overwrite any EDL files associated with your episode files. + If checked, the plugin will overwrite all EDL files associated with your episodes with the currently discovered introduction timestamps and EDL action.
@@ -489,7 +488,7 @@ document.querySelector('#MaxParallelism').value = config.MaxParallelism; document.querySelector('#EdlAction').value = config.EdlAction; - document.querySelector('#OverwriteEdl').checked = config.OverwriteExistingEdlFiles; + document.querySelector('#RegenerateEdl').checked = config.RegenerateEdlFiles; document.querySelector('#CacheFingerprints').checked = config.CacheFingerprints; document.querySelector('#ShowPromptAdjustment').value = config.ShowPromptAdjustment; @@ -507,7 +506,7 @@ config.MaxParallelism = document.querySelector('#MaxParallelism').value; config.EdlAction = document.querySelector('#EdlAction').value; - config.OverwriteExistingEdlFiles = document.querySelector('#OverwriteEdl').checked; + config.RegenerateEdlFiles = document.querySelector('#RegenerateEdl').checked; config.CacheFingerprints = document.querySelector('#CacheFingerprints').checked; config.ShowPromptAdjustment = document.querySelector("#ShowPromptAdjustment").value; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs index 38e2d16..ac2386f 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs @@ -1,5 +1,6 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper; +using System; using System.Collections.ObjectModel; using System.IO; using Microsoft.Extensions.Logging; @@ -20,13 +21,35 @@ public static class EdlManager _logger = logger; } + /// + /// Logs the configuration that will be used during EDL file creation. + /// + public static void LogConfiguration() + { + if (_logger is null) + { + throw new InvalidOperationException("Logger must not be null"); + } + + var config = Plugin.Instance!.Configuration; + + if (config.EdlAction == EdlAction.None) + { + _logger.LogDebug("EDL action: None - taking no further action"); + return; + } + + _logger.LogDebug("EDL action: {Action}", config.EdlAction); + _logger.LogDebug("Regenerate EDL files: {Regenerate}", config.RegenerateEdlFiles); + } + /// /// If the EDL action is set to a value other than None, update EDL files for the provided episodes. /// /// Episodes to update EDL files for. public static void UpdateEDLFiles(ReadOnlyCollection episodes) { - var overwrite = Plugin.Instance!.Configuration.OverwriteExistingEdlFiles; + var regenerate = Plugin.Instance!.Configuration.RegenerateEdlFiles; var action = Plugin.Instance!.Configuration.EdlAction; if (action == EdlAction.None) { @@ -39,12 +62,18 @@ public static class EdlManager foreach (var episode in episodes) { var id = episode.EpisodeId; - var intro = Plugin.Instance!.Intros[id]; + + if (!Plugin.Instance!.Intros.TryGetValue(id, out var intro)) + { + _logger?.LogDebug("Episode {Id} did not have an introduction, skipping", id); + continue; + } + var edlPath = GetEdlPath(Plugin.Instance!.GetItemPath(id)); _logger?.LogTrace("Episode {Id} has EDL path {Path}", id, edlPath); - if (!overwrite && File.Exists(edlPath)) + if (!regenerate && File.Exists(edlPath)) { _logger?.LogTrace("Refusing to overwrite existing EDL file {Path}", edlPath); continue; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs index 24ee83a..7067e88 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/FingerprinterTask.cs @@ -137,6 +137,9 @@ public class FingerprinterTask : IScheduledTask "No episodes to analyze: either no show libraries are defined or ffmpeg could not be found"); } + // Log EDL settings + EdlManager.LogConfiguration(); + // Include the previously processed episodes in the percentage reported to the UI. var totalProcessed = CountProcessedEpisodes(); @@ -145,6 +148,7 @@ public class FingerprinterTask : IScheduledTask MaxDegreeOfParallelism = Plugin.Instance!.Configuration.MaxParallelism }; + // Analyze all episodes in the queue using the degrees of parallelism the user specified. Parallel.ForEach(queue, options, (season) => { var first = season.Value[0]; @@ -156,7 +160,7 @@ public class FingerprinterTask : IScheduledTask // (instead of just using the number of episodes in the current season). var analyzed = AnalyzeSeason(season, cancellationToken); Interlocked.Add(ref totalProcessed, analyzed); - writeEdl = analyzed > 0; + writeEdl = analyzed > 0 || Plugin.Instance!.Configuration.RegenerateEdlFiles; } catch (FingerprintException ex) { @@ -193,6 +197,14 @@ public class FingerprinterTask : IScheduledTask progress.Report((totalProcessed * 100) / Plugin.Instance!.TotalQueued); }); + // Turn the regenerate EDL flag off after the scan completes. + if (Plugin.Instance!.Configuration.RegenerateEdlFiles) + { + _logger.LogInformation("Turning EDL file regeneration flag off"); + Plugin.Instance!.Configuration.RegenerateEdlFiles = false; + Plugin.Instance!.SaveConfiguration(); + } + return Task.CompletedTask; } From e33ff529b6fb5be6434c64bf92ede8224eb3fb84 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Fri, 24 Jun 2022 00:04:11 -0500 Subject: [PATCH 4/5] Add EDL documentation --- docs/edl.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 docs/edl.md diff --git a/docs/edl.md b/docs/edl.md new file mode 100644 index 0000000..81f5d0c --- /dev/null +++ b/docs/edl.md @@ -0,0 +1,16 @@ +# EDL support + +The timestamps of discovered introductions can be written to [EDL](https://kodi.wiki/view/Edit_decision_list) files alongside your media files. EDL files are saved when: +* Scanning an episode for the first time, or +* If requested with the regenerate checkbox + +## Configuration + +Jellyfin must have read/write access to your TV show libraries in order to make use of this feature. + +## Usage + +To have the plugin create EDL files: +1. Change the EDL action from the default of None to any of the other supported EDL actions +2. Check the "Regenerate EDL files during next analysis" checkbox + 1. If this option is not selected, only seasons with a newly analyzed episode will have EDL files created. From 667c1441a9dc14ec046fe77686db3391043d5252 Mon Sep 17 00:00:00 2001 From: ConfusedPolarBear <33811686+ConfusedPolarBear@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:02:40 -0500 Subject: [PATCH 5/5] Check intro validity before writing an EDL file --- ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs index ac2386f..0620c78 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/EdlManager.cs @@ -68,6 +68,11 @@ public static class EdlManager _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); + continue; + } var edlPath = GetEdlPath(Plugin.Instance!.GetItemPath(id));