diff --git a/.gitignore b/.gitignore index 295855b..6908286 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ BenchmarkDotNet.Artifacts/ # Ignore pre compiled web interface docker/dist + +# Visual Studio +.vs/ +UpgradeLog*.htm diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs index b22e428..763d461 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs @@ -7,8 +7,8 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading; -using Microsoft.Extensions.Logging; using MediaBrowser.Model.Entities; +using Microsoft.Extensions.Logging; /// /// Chapter name analyzer. diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs index c230c1b..2a03321 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs @@ -275,10 +275,10 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer { var modifiedPoint = (uint)(originalPoint + i); - if (rhsIndex.ContainsKey(modifiedPoint)) + if (rhsIndex.TryGetValue(modifiedPoint, out var value)) { var lhsFirst = (int)lhsIndex[originalPoint]; - var rhsFirst = (int)rhsIndex[modifiedPoint]; + var rhsFirst = (int)value; indexShifts.Add(rhsFirst - lhsFirst); } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs index 294f565..0d4407e 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/PluginConfiguration.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using MediaBrowser.Model.Plugins; namespace ConfusedPolarBear.Plugin.IntroSkipper.Configuration; @@ -181,4 +182,14 @@ public class PluginConfiguration : BasePluginConfiguration /// Gets or sets the notification text sent after automatically skipping an introduction. /// public string AutoSkipNotificationText { get; set; } = "Intro skipped"; + + /// + /// Gets or sets the number of threads for an ffmpeg process. + /// + public int ProcessThreads { get; set; } = 0; + + /// + /// Gets or sets the relative priority for an ffmpeg process. + /// + public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal; } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index 0475d82..151a77d 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -59,77 +59,8 @@ -
- - -
- If checked, episode fingerprints will be cached to the filesystem -
- WARNING: Disabling cache may result in lengthy detection -
-
-
- -
- EDL file generation - -
-
- - - -
- 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 after EDL files are generated, you must check the - "Regenerate EDL files" checkbox below. -
-
- -
- - -
- If checked, the plugin will overwrite all EDL files associated with - your episodes with the currently discovered introduction timestamps and EDL action. -
-
-
-
- Modify introduction requirements + Modify Intro Parameters
@@ -193,8 +124,63 @@

+
+ EDL File Generation + +
+
+ + + +
+ 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 after EDL files are generated, you must check the + "Regenerate EDL files" checkbox below. +
+
+ +
+ + +
+ If checked, the plugin will overwrite all EDL files associated with + your episodes with the currently discovered introduction timestamps and EDL action. +
+
+
+
- Silence detection options + Silence Detection Options
@@ -219,6 +205,73 @@
+ +
+ Process Configuration + +
+
+ + +
+ If checked, episode fingerprints will be saved on the filesystem to improve analysis speed. +
+ WARNING: May result in lengthy detection! Not recommended for large libraries! +
+
+
+ +
+ + + +
+ Sets the relative priority of the analysis ffmpeg process to other parallel operations + (ie. transcoding, chapter detection, etc). +
+
+ +
+ + +
+ Number of simultaneous processes to use for ffmpeg operations. +
+ This value is most often defined as 1 thread per CPU core, + but setting a value of 0 (default) will use the maximum threads available. +
+
+
@@ -268,7 +321,7 @@
- If checked, skip button will appear through entire intro (offset and timeout are ignored).
+ If checked, skip button will appear throughout entire intro (offset and timeout are ignored).
@@ -388,13 +441,13 @@

- - -
+ +
+ @@ -491,6 +544,8 @@ "MinimumIntroDuration", "MaximumIntroDuration", "EdlAction", + "ProcessPriority", + "ProcessThreads", // playback "ShowPromptAdjustment", "HidePromptAdjustment", @@ -506,8 +561,8 @@ var booleanConfigurationFields = [ "AnalyzeSeasonZero", - "CacheFingerprints", "RegenerateEdlFiles", + "CacheFingerprints", "AutoSkip", "SkipFirstEpisode", "PersistSkipButton", @@ -618,6 +673,7 @@ // show changed, populate seasons async function showChanged() { clearSelect(selectSeason); + btnSeasonEraseTimestamps.style.display = "none"; // add all seasons from this show to the season select for (var season of shows[selectShow.value]) { @@ -634,6 +690,7 @@ clearSelect(selectEpisode1); clearSelect(selectEpisode2); + btnSeasonEraseTimestamps.style.display = "block"; let i = 1; for (let episode of episodes) { @@ -714,7 +771,7 @@ // make an authenticated GET to the server and parse the response as JSON async function getJson(url) { - return await fetchWithAuth(url, "GET").then(r => { return r.json(); }); + return await fetchWithAuth(url, "GET").then(r => { return r.json(); }).catch(err => { console.debug(err) }); } // make an authenticated fetch to the server diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ConfusedPolarBear.Plugin.IntroSkipper.csproj b/ConfusedPolarBear.Plugin.IntroSkipper/ConfusedPolarBear.Plugin.IntroSkipper.csproj index 51ee209..1f3c386 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ConfusedPolarBear.Plugin.IntroSkipper.csproj +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ConfusedPolarBear.Plugin.IntroSkipper.csproj @@ -1,25 +1,22 @@ - net6.0 ConfusedPolarBear.Plugin.IntroSkipper - 0.1.14.0 - 0.1.14.0 + 0.1.15.0 + 0.1.15.0 true true enable AllEnabledByDefault ../jellyfin.ruleset - - + - @@ -27,5 +24,4 @@ - - + \ No newline at end of file diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs index fc0c9d4..95779e6 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs @@ -400,8 +400,9 @@ public static class FFmpegWrapper // for each file that is fingerprinted. var prependArgument = string.Format( CultureInfo.InvariantCulture, - "-hide_banner -loglevel {0} ", - logLevel); + "-hide_banner -loglevel {0} -threads {1} ", + logLevel, + Plugin.Instance?.Configuration.ProcessThreads ?? 0); var info = new ProcessStartInfo(ffmpegPath, args.Insert(0, prependArgument)) { @@ -425,6 +426,17 @@ public static class FFmpegWrapper ffmpeg.Start(); + try + { + ffmpeg.PriorityClass = Plugin.Instance?.Configuration.ProcessPriority ?? ProcessPriorityClass.BelowNormal; + } + catch (Exception e) + { + Logger?.LogDebug( + "ffmpeg priority could not be modified. {Message}", + e.Message); + } + using (MemoryStream ms = new MemoryStream()) { var buf = new byte[4096]; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/GlobalSuppressions.cs b/ConfusedPolarBear.Plugin.IntroSkipper/GlobalSuppressions.cs new file mode 100644 index 0000000..2967ca7 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper/GlobalSuppressions.cs @@ -0,0 +1,10 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.WarningManager")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.IntroWithMetadata")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.TimeRangeHelpers")] diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs index 7f57af8..5fd0b8d 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs @@ -331,7 +331,7 @@ public class Plugin : BasePlugin, IHasWebPages private void InjectSkipButton(string indexPath) { // Parts of this code are based off of JellyScrub's script injection code. - // https://github.com/nicknsy/jellyscrub/blob/4ce806f602988a662cfe3cdbaac35ee8046b7ec4/Nick.Plugin.Jellyscrub/JellyscrubPlugin.cs + // https://github.com/nicknsy/jellyscrub/blob/main/Nick.Plugin.Jellyscrub/JellyscrubPlugin.cs#L38 _logger.LogDebug("Reading index.html from {Path}", indexPath); var contents = File.ReadAllText(indexPath); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs index 30239a4..c27ff62 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/QueueManager.cs @@ -43,13 +43,6 @@ public class QueueManager /// Queued media items. public ReadOnlyDictionary> GetMediaItems() { - // Assert that ffmpeg with chromaprint is installed - if (!FFmpegWrapper.CheckFFmpegVersion()) - { - throw new FingerprintException( - "ffmpeg with chromaprint is not installed on this system - episodes will not be analyzed. If Jellyfin is running natively, install jellyfin-ffmpeg5. If Jellyfin is running in a container, upgrade it to the latest version of 10.8.0."); - } - Plugin.Instance!.TotalQueued = 0; LoadAnalysisSettings(); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs index 940e0a0..ca72342 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/ScheduledTasks/BaseItemAnalyzerTask.cs @@ -53,6 +53,13 @@ public class BaseItemAnalyzerTask IProgress progress, CancellationToken cancellationToken) { + // Assert that ffmpeg with chromaprint is installed + if (!FFmpegWrapper.CheckFFmpegVersion()) + { + throw new FingerprintException( + "ffmpeg with chromaprint is not installed on this system - episodes will not be analyzed. If Jellyfin is running natively, install jellyfin-ffmpeg5. If Jellyfin is running in a container, upgrade it to the latest version of 10.8.0."); + } + var queueManager = new QueueManager( _loggerFactory.CreateLogger(), _libraryManager); diff --git a/README.md b/README.md index 7048bf4..d7ed50c 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ This fork doesn't ship the custom web interface on your server. So the plugin ne * Debian Linux based native installs: provided by the `jellyfin-ffmpeg5` package * MacOS native installs: build ffmpeg with chromaprint support ([instructions](#installation-instructions-for-macos)) -## Introduction requirements +## Introduction parameters -Show introductions will only be detected if they are: +Show introductions will be detected if they are: -* Located within the first 25% of an episode, or the first 10 minutes, whichever is smaller +* Located within the first 25% of an episode or the first 10 minutes, whichever is smaller * Between 15 seconds and 2 minutes long -Ending credits will only be detected if they are shorter than 4 minutes. +Ending credits will be detected if they are shorter than 4 minutes. All of these requirements can be customized as needed. diff --git a/manifest.json b/manifest.json index d23259b..9dde9b2 100644 --- a/manifest.json +++ b/manifest.json @@ -4,10 +4,18 @@ "name": "Intro Skipper", "overview": "Automatically detect and skip intros in television episodes", "description": "Analyzes the audio of television episodes and detects introduction sequences.", - "owner": "ConfusedPolarBear", + "owner": "jumoog, AbandonedCart (forked from ConfusedPolarBear)", "category": "General", "imageUrl": "https://raw.githubusercontent.com/jumoog/intro-skipper/master/images/logo.png", "versions": [ + { + "version": "0.1.15.0", + "changelog": "- See the full changelog at [GitHub](https://github.com/jumoog/intro-skipper/blob/master/CHANGELOG.md)\n", + "targetAbi": "10.8.4.0", + "sourceUrl": "https://github.com/jumoog/intro-skipper/releases/download/v0.1.15/intro-skipper-v0.1.15.zip", + "checksum": "cf05593afbb2be39b8de31dcb7fd8a50", + "timestamp": "2024-03-03T09:08:10Z" + }, { "version": "0.1.14.0", "changelog": "- See the full changelog at [GitHub](https://github.com/jumoog/intro-skipper/blob/master/CHANGELOG.md)\n",