remove EDL function and point to endrl's EDL plugin (#352)

Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
Kilian von Pflugk 2024-10-21 09:19:51 +02:00 committed by GitHub
parent 4b725aaaad
commit 0b27b6e297
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 7 additions and 297 deletions

View File

@ -1,47 +0,0 @@
using System;
using IntroSkipper.Data;
using IntroSkipper.Manager;
using Xunit;
namespace 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<ArgumentException>(() =>
{
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 static Segment MakeIntro(double start, double end)
{
return new Segment(Guid.Empty, new TimeRange(start, end));
}
}

View File

@ -39,7 +39,6 @@ Selenium is used to verify that the plugin's web interface works as expected. It
* Changing settings (will be added in the future) * Changing settings (will be added in the future)
* Maximum degree of parallelism * Maximum degree of parallelism
* Selecting libraries for analysis * Selecting libraries for analysis
* EDL settings
* Introduction requirements * Introduction requirements
* Auto skip * Auto skip
* Show/hide skip prompt * Show/hide skip prompt

View File

@ -76,26 +76,12 @@ public class PluginConfiguration : BasePluginConfiguration
public bool UpdateMediaSegments { get; set; } = true; public bool UpdateMediaSegments { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to regenerate all EDL files during the next scan. /// Gets or sets a value indicating whether to regenerate all Media Segments during the next scan.
/// By default, EDL files are only written for a season if the season had at least one newly analyzed episode. /// By default, Media Segments 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. /// If this is set, all Media Segments will be regenerated and overwrite any existing Media Segemnts.
/// </summary> /// </summary>
public bool RegenerateMediaSegments { get; set; } = true; public bool RegenerateMediaSegments { get; set; } = true;
// ===== EDL handling =====
/// <summary>
/// Gets or sets a value indicating the action to write to created EDL files.
/// </summary>
public EdlAction EdlAction { get; set; } = EdlAction.None;
/// <summary>
/// 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.
/// </summary>
public bool RegenerateEdlFiles { get; set; }
// ===== Custom analysis settings ===== // ===== Custom analysis settings =====
/// <summary> /// <summary>

View File

@ -186,35 +186,7 @@
<br /> <br />
<div class="selectContainer"> <div class="selectContainer">
<label class="selectLabel" for="EdlAction">EDL Action</label> The EDL file generation has been removed. Please use endrl's <a href="https://github.com/endrl/jellyfin-plugin-edl">EDL plugin</a>.
<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="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/credit timestamps and EDL action.</div>
</div> </div>
</details> </details>
@ -680,7 +652,6 @@
"MinimumCreditsDuration", "MinimumCreditsDuration",
"MaximumCreditsDuration", "MaximumCreditsDuration",
"MaximumMovieCreditsDuration", "MaximumMovieCreditsDuration",
"EdlAction",
"ProcessPriority", "ProcessPriority",
"ProcessThreads", "ProcessThreads",
// playback // playback
@ -699,7 +670,7 @@
"AutoSkipCreditsNotificationText", "AutoSkipCreditsNotificationText",
]; ];
var booleanConfigurationFields = ["AutoDetectIntros", "AutoDetectCredits", "AnalyzeMovies", "AnalyzeSeasonZero", "SelectAllLibraries", "UpdateMediaSegments", "RegenerateMediaSegments", "RegenerateEdlFiles", "CacheFingerprints", "AutoSkip", "AutoSkipCredits", "SkipFirstEpisode", "PersistSkipButton", "SkipButtonVisible"]; var booleanConfigurationFields = ["AutoDetectIntros", "AutoDetectCredits", "AnalyzeMovies", "AnalyzeSeasonZero", "SelectAllLibraries", "UpdateMediaSegments", "RegenerateMediaSegments", "CacheFingerprints", "AutoSkip", "AutoSkipCredits", "SkipFirstEpisode", "PersistSkipButton", "SkipButtonVisible"];
// visualizer elements // visualizer elements
var ignorelistSection = document.querySelector("div#ignorelistSection"); var ignorelistSection = document.querySelector("div#ignorelistSection");

View File

@ -1,32 +0,0 @@
namespace IntroSkipper.Data;
/// <summary>
/// Taken from https://kodi.wiki/view/Edit_decision_list#MPlayer_EDL.
/// </summary>
public enum EdlAction
{
/// <summary>
/// Do not create EDL files.
/// </summary>
None = -1,
/// <summary>
/// Completely remove the segment from playback as if it was never in the original video.
/// </summary>
Cut = 0,
/// <summary>
/// Mute audio, continue playback.
/// </summary>
Mute = 1,
/// <summary>
/// Inserts a new scene marker.
/// </summary>
SceneMarker = 2,
/// <summary>
/// Automatically skip once during playback.
/// </summary>
CommercialBreak = 3
}

View File

@ -93,22 +93,4 @@ public class Segment
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public double Duration => End - Start; public double Duration => End - Start;
/// <summary>
/// Convert this Intro object to a Kodi compatible EDL entry.
/// </summary>
/// <param name="action">User specified configuration EDL action.</param>
/// <returns>String.</returns>
public string ToEdl(EdlAction action)
{
if (action == EdlAction.None)
{
throw new ArgumentException("Cannot serialize an EdlAction of None");
}
var start = Math.Round(Start, 2);
var end = Math.Round(End, 2);
return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action);
}
} }

View File

@ -1,117 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using IntroSkipper.Data;
using Microsoft.Extensions.Logging;
namespace IntroSkipper.Manager
{
/// <summary>
/// Update EDL files associated with a list of episodes.
/// </summary>
public static class EdlManager
{
private static ILogger? _logger;
/// <summary>
/// Initialize EDLManager with a logger.
/// </summary>
/// <param name="logger">ILogger.</param>
public static void Initialize(ILogger logger)
{
_logger = logger;
}
/// <summary>
/// Logs the configuration that will be used during EDL file creation.
/// </summary>
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);
}
/// <summary>
/// If the EDL action is set to a value other than None, update EDL files for the provided episodes.
/// </summary>
/// <param name="episodes">Episodes to update EDL files for.</param>
public static void UpdateEDLFiles(IReadOnlyList<QueuedEpisode> episodes)
{
var regenerate = Plugin.Instance!.Configuration.RegenerateEdlFiles;
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;
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} has neither a valid intro nor credit, skipping", id);
continue;
}
var edlPath = GetEdlPath(Plugin.Instance.GetItemPath(id));
_logger?.LogTrace("Episode {Id} has EDL path {Path}", id, edlPath);
if (!regenerate && File.Exists(edlPath))
{
_logger?.LogTrace("Refusing to overwrite existing EDL file {Path}", edlPath);
continue;
}
var edlContent = string.Empty;
if (hasIntro)
{
edlContent += intro?.ToEdl(action);
}
if (hasCredit)
{
if (edlContent.Length > 0)
{
edlContent += Environment.NewLine;
}
edlContent += credit?.ToEdl(action);
}
File.WriteAllText(edlPath, edlContent);
}
}
/// <summary>
/// Given the path to an episode, return the path to the associated EDL file.
/// </summary>
/// <param name="mediaPath">Full path to episode.</param>
/// <returns>Full path to EDL file.</returns>
public static string GetEdlPath(string mediaPath)
{
return Path.ChangeExtension(mediaPath, "edl");
}
}
}

View File

@ -43,11 +43,6 @@ public class BaseItemAnalyzerTask
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_mediaSegmentUpdateManager = mediaSegmentUpdateManager; _mediaSegmentUpdateManager = mediaSegmentUpdateManager;
if (Plugin.Instance!.Configuration.EdlAction != EdlAction.None)
{
EdlManager.Initialize(_logger);
}
} }
/// <summary> /// <summary>
@ -89,11 +84,6 @@ public class BaseItemAnalyzerTask
"No libraries selected for analysis. Please visit the plugin settings to configure."); "No libraries selected for analysis. Please visit the plugin settings to configure.");
} }
if (Plugin.Instance!.Configuration.EdlAction != EdlAction.None)
{
EdlManager.LogConfiguration();
}
var totalProcessed = 0; var totalProcessed = 0;
var options = new ParallelOptions var options = new ParallelOptions
{ {
@ -169,18 +159,12 @@ public class BaseItemAnalyzerTask
{ {
await _mediaSegmentUpdateManager.UpdateMediaSegmentsAsync(episodes, ct).ConfigureAwait(false); await _mediaSegmentUpdateManager.UpdateMediaSegmentsAsync(episodes, ct).ConfigureAwait(false);
} }
if (Plugin.Instance.Configuration.RegenerateEdlFiles || (updateManagers && Plugin.Instance.Configuration.EdlAction != EdlAction.None))
{
EdlManager.UpdateEDLFiles(episodes);
}
}).ConfigureAwait(false); }).ConfigureAwait(false);
if (Plugin.Instance.Configuration.RegenerateMediaSegments || Plugin.Instance.Configuration.RegenerateEdlFiles) if (Plugin.Instance.Configuration.RegenerateMediaSegments)
{ {
_logger.LogInformation("Turning Mediasegment/EDL file regeneration flag off"); _logger.LogInformation("Turning Mediasegment");
Plugin.Instance.Configuration.RegenerateMediaSegments = false; Plugin.Instance.Configuration.RegenerateMediaSegments = false;
Plugin.Instance.Configuration.RegenerateEdlFiles = false;
Plugin.Instance.SaveConfiguration(); Plugin.Instance.SaveConfiguration();
} }
} }

View File

@ -1,16 +0,0 @@
# 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.