Merge pull request #56 from RepoDevil/master
Add parameters for ffmpeg options
This commit is contained in:
commit
77f43ed96e
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,3 +5,7 @@ BenchmarkDotNet.Artifacts/
|
||||
|
||||
# Ignore pre compiled web interface
|
||||
docker/dist
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
UpgradeLog*.htm
|
||||
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Chapter name analyzer.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
/// </summary>
|
||||
public string AutoSkipNotificationText { get; set; } = "Intro skipped";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of threads for an ffmpeg process.
|
||||
/// </summary>
|
||||
public int ProcessThreads { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the relative priority for an ffmpeg process.
|
||||
/// </summary>
|
||||
public ProcessPriorityClass ProcessPriority { get; set; } = ProcessPriorityClass.BelowNormal;
|
||||
}
|
||||
|
@ -59,77 +59,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="CacheFingerprints" type="checkbox" is="emby-checkbox" />
|
||||
<span>Cache episode fingerprints</span>
|
||||
</label>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If checked, episode fingerprints will be cached to the filesystem
|
||||
<br />
|
||||
<strong>WARNING: Disabling cache may result in lengthy detection</strong>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details id="edl">
|
||||
<summary>EDL file generation</summary>
|
||||
|
||||
<br />
|
||||
<div class="selectContainer">
|
||||
<label class="selectLabel" for="EdlAction">EDL Action</label>
|
||||
<select is="emby-select" id="EdlAction" class="emby-select-withcolor emby-select">
|
||||
<option value="None">
|
||||
None (do not create or modify EDL files)
|
||||
</option>
|
||||
|
||||
<option value="CommercialBreak">
|
||||
Commercial Break (recommended, skips past the intro once)
|
||||
</option>
|
||||
|
||||
<option value="Cut">
|
||||
Cut (player will remove the intro from the video)
|
||||
</option>
|
||||
|
||||
<option value="Intro">
|
||||
Intro (show a skip button, *experimental*)
|
||||
</option>
|
||||
|
||||
<option value="Mute">
|
||||
Mute (audio will be muted)
|
||||
</option>
|
||||
|
||||
<option value="SceneMarker">
|
||||
Scene Marker (create a chapter marker)
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If set to a value other than None, specifies which action to write to
|
||||
<a href="https://kodi.wiki/view/Edit_decision_list">MPlayer compatible EDL files</a>
|
||||
alongside your episode files. <br />
|
||||
|
||||
If this value is changed after EDL files are generated, you must check the
|
||||
"Regenerate EDL files" checkbox below.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="RegenerateEdlFiles" type="checkbox" is="emby-checkbox" />
|
||||
<span>Regenerate EDL files during next scan</span>
|
||||
</label>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If checked, the plugin will <strong>overwrite all EDL files</strong> associated with
|
||||
your episodes with the currently discovered introduction timestamps and EDL action.
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details id="intro_reqs">
|
||||
<summary>Modify introduction requirements</summary>
|
||||
<summary>Modify Intro Parameters</summary>
|
||||
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
@ -193,8 +124,63 @@
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details id="edl">
|
||||
<summary>EDL File Generation</summary>
|
||||
|
||||
<br />
|
||||
<div class="selectContainer">
|
||||
<label class="selectLabel" for="EdlAction">EDL Action</label>
|
||||
<select is="emby-select" id="EdlAction" class="emby-select-withcolor emby-select">
|
||||
<option value="None">
|
||||
None (do not create or modify EDL files)
|
||||
</option>
|
||||
|
||||
<option value="CommercialBreak">
|
||||
Commercial Break (recommended, skips past the intro once)
|
||||
</option>
|
||||
|
||||
<option value="Cut">
|
||||
Cut (player will remove the intro from the video)
|
||||
</option>
|
||||
|
||||
<option value="Intro">
|
||||
Intro (show a skip button, *experimental*)
|
||||
</option>
|
||||
|
||||
<option value="Mute">
|
||||
Mute (audio will be muted)
|
||||
</option>
|
||||
|
||||
<option value="SceneMarker">
|
||||
Scene Marker (create a chapter marker)
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If set to a value other than None, specifies which action to write to
|
||||
<a href="https://kodi.wiki/view/Edit_decision_list">MPlayer compatible EDL files</a>
|
||||
alongside your episode files. <br />
|
||||
|
||||
If this value is changed after EDL files are generated, you must check the
|
||||
"Regenerate EDL files" checkbox below.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="RegenerateEdlFiles" type="checkbox" is="emby-checkbox" />
|
||||
<span>Regenerate EDL files during next scan</span>
|
||||
</label>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If checked, the plugin will <strong>overwrite all EDL files</strong> associated with
|
||||
your episodes with the currently discovered introduction timestamps and EDL action.
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details id="silence">
|
||||
<summary>Silence detection options</summary>
|
||||
<summary>Silence Detection Options</summary>
|
||||
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
@ -219,6 +205,73 @@
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details id="detection">
|
||||
<summary>Process Configuration</summary>
|
||||
|
||||
<br/>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label class="emby-checkbox-label">
|
||||
<input id="CacheFingerprints" type="checkbox" is="emby-checkbox" />
|
||||
<span>Cache episode fingerprints</span>
|
||||
</label>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If checked, episode fingerprints will be saved on the filesystem to improve analysis speed.
|
||||
<br />
|
||||
<strong>WARNING: May result in lengthy detection! Not recommended for large libraries!</strong>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<label class="selectLabel" for="ProcessPriority">ffmpeg Priority</label>
|
||||
<select is="emby-select" id="ProcessPriority" class="emby-select-withcolor emby-select">
|
||||
<option value="Idle">
|
||||
Idle
|
||||
</option>
|
||||
|
||||
<option value="BelowNormal">
|
||||
Below Normal
|
||||
</option>
|
||||
|
||||
<option value="Normal">
|
||||
Normal
|
||||
</option>
|
||||
|
||||
<option value="AboveNormal">
|
||||
Above Normal
|
||||
</option>
|
||||
|
||||
<option value="High">
|
||||
High
|
||||
</option>
|
||||
|
||||
<option value="RealTime">
|
||||
Highest
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<div class="fieldDescription">
|
||||
Sets the relative priority of the analysis ffmpeg process to other parallel operations
|
||||
(ie. transcoding, chapter detection, etc).
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<label class="inputLabel inputLabelUnfocused" for="ProcessThreads">
|
||||
ffmpeg Threads
|
||||
</label>
|
||||
<input id="ProcessThreads" type="number" is="emby-input" min="0"
|
||||
max="16" />
|
||||
<div class="fieldDescription">
|
||||
Number of simultaneous processes to use for ffmpeg operations.
|
||||
<br />
|
||||
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.
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="verticalSection-extrabottompadding">
|
||||
@ -268,7 +321,7 @@
|
||||
</label>
|
||||
|
||||
<div class="fieldDescription">
|
||||
If checked, skip button will appear through entire intro (offset and timeout are ignored).<br />
|
||||
If checked, skip button will appear throughout entire intro (offset and timeout are ignored).<br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -388,13 +441,13 @@
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<button id="btnEraseSeasonTimestamps" type="button">
|
||||
Erase all timestamps for this season
|
||||
</button>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<button id="btnEraseSeasonTimestamps" type="button" style="display:none;">
|
||||
Erase all timestamps for this season
|
||||
</button>
|
||||
<hr />
|
||||
|
||||
<button id="btnEraseIntroTimestamps">
|
||||
Erase all introduction timestamps (globally)
|
||||
</button>
|
||||
@ -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
|
||||
|
@ -1,25 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>ConfusedPolarBear.Plugin.IntroSkipper</RootNamespace>
|
||||
<AssemblyVersion>0.1.14.0</AssemblyVersion>
|
||||
<FileVersion>0.1.14.0</FileVersion>
|
||||
<AssemblyVersion>0.1.15.0</AssemblyVersion>
|
||||
<FileVersion>0.1.15.0</FileVersion>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
|
||||
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Jellyfin.Controller" Version="10.8.*" />
|
||||
<PackageReference Include="Jellyfin.Model" Version="10.8.*" />
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.556" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Configuration\configPage.html" />
|
||||
<EmbeddedResource Include="Configuration\configPage.html" />
|
||||
@ -27,5 +24,4 @@
|
||||
<EmbeddedResource Include="Configuration\inject.js" />
|
||||
<EmbeddedResource Include="Configuration\version.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
@ -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];
|
||||
|
10
ConfusedPolarBear.Plugin.IntroSkipper/GlobalSuppressions.cs
Normal file
10
ConfusedPolarBear.Plugin.IntroSkipper/GlobalSuppressions.cs
Normal file
@ -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")]
|
@ -331,7 +331,7 @@ public class Plugin : BasePlugin<PluginConfiguration>, 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);
|
||||
|
@ -43,13 +43,6 @@ public class QueueManager
|
||||
/// <returns>Queued media items.</returns>
|
||||
public ReadOnlyDictionary<Guid, List<QueuedEpisode>> 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();
|
||||
|
@ -53,6 +53,13 @@ public class BaseItemAnalyzerTask
|
||||
IProgress<double> 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<QueueManager>(),
|
||||
_libraryManager);
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user