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:
parent
4b725aaaad
commit
0b27b6e297
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
docs/edl.md
16
docs/edl.md
@ -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.
|
|
Loading…
x
Reference in New Issue
Block a user