Skip to content

Commit

Permalink
Release 0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
endrl committed Aug 31, 2024
1 parent fbc75e3 commit eeb29c7
Show file tree
Hide file tree
Showing 18 changed files with 726 additions and 94 deletions.
8 changes: 4 additions & 4 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"name": "Launch",
"request": "launch",
"preLaunchTask": "build-and-copy",
"program": "${config:jellyfinDir}/bin/Debug/net7.0/jellyfin.dll",
"program": "${config:jellyfinDir}/bin/Debug/net8.0/jellyfin.dll",
"args": [
//"--nowebclient"
"--webdir",
"${config:jellyfinWebDir}/dist/"
//"--nowebclient"
"--webdir",
"${config:jellyfinWebDir}/dist/"
],
"cwd": "${config:jellyfinDir}",
}
Expand Down
4 changes: 2 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"args": [
"/I",
"/y",
".\\${config:pluginName}\\bin\\Debug\\net7.0\\publish",
".\\${config:pluginName}\\bin\\Debug\\net8.0\\publish",
"${config:jellyfinDataDirWin}\\plugins\\${config:pluginName}\\"
]

Expand All @@ -65,7 +65,7 @@
"type": "shell",
"command": "cp",
"args": [
"./${config:pluginName}/bin/Debug/net7.0/publish/*",
"./${config:pluginName}/bin/Debug/net8.0/publish/*",
"${config:jellyfinDataDir}/plugins/${config:pluginName}/"
]

Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

-

## [0.3.0] - 2024-08-31

### Added

- Added http api endpoints to view and create edl files (with Segment Editor)

### Fixed

- No longer write empty files
- Crash during segment sorting

### Breaking

- Requires now Jellyfin 10.10 unstable

## [0.2.0] - 2023-11-03

- Binary update

## [0.1.0] - 2023-05-08

- Initial Release
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>0.2.0.0</Version>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.2.0.0</FileVersion>
<Version>0.3.0.0</Version>
<AssemblyVersion>0.3.0.0</AssemblyVersion>
<FileVersion>0.3.0.0</FileVersion>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion Jellyfin.Plugin.Edl.Tests/Jellyfin.Plugin.Edl.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
Expand Down
13 changes: 6 additions & 7 deletions Jellyfin.Plugin.Edl.Tests/TestEdl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ namespace Jellyfin.Plugin.Edl.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 \n")]
[InlineData(15, 16.7, EdlAction.Mute, "15 16.7 1 \n")]
[InlineData(420, 822, EdlAction.CommercialBreak, "420 822 3 \n")]
[InlineData(1, 255.3, EdlAction.SceneMarker, "1 255.3 2 \n")]
[InlineData(1.123456789, 5.654647987, EdlAction.CommercialBreak, "1.12 5.65 3 \n")]
public void TestEdlSerialization(double start, double end, EdlAction action, string expected)
[InlineData(53000000, 71000000, EdlAction.Cut, "5.3 7.1 0 \n")]
[InlineData(150000000, 167000000, EdlAction.Mute, "15 16.7 1 \n")]
[InlineData(4200000000, 8220000000, EdlAction.CommercialBreak, "420 822 3 \n")]
[InlineData(10000009, 2553000000, EdlAction.SceneMarker, "1 255.3 2 \n")]
[InlineData(11234568, 56546475, EdlAction.CommercialBreak, "1.123 5.655 3 \n")]
public void TestEdlSerialization(long start, long end, EdlAction action, string expected)
{
var actual = EdlManager.ToEdlString(start, end, action);

Expand Down
25 changes: 23 additions & 2 deletions Jellyfin.Plugin.Edl/Configuration/PluginConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary>
public PluginConfiguration()
{
UnknownEdlAction = EdlAction.None;
IntroEdlAction = EdlAction.None;
OutroEdlAction = EdlAction.None;
PreviewEdlAction = EdlAction.None;
RecapEdlAction = EdlAction.None;
CommercialEdlAction = EdlAction.CommercialBreak;
}

/// <summary>
/// Gets or sets an Unknown Action option.
/// </summary>
public EdlAction UnknownEdlAction { get; set; }

/// <summary>
/// Gets or sets an Intro Action option.
/// </summary>
public EdlAction IntroEdlAction { get; set; }
public EdlAction IntroEdlAction { get; set; } = EdlAction.Cut;

/// <summary>
/// Gets or sets an Outro Action option.
Expand All @@ -42,7 +48,7 @@ public PluginConfiguration()
/// <summary>
/// Gets or sets an Commercial Action option.
/// </summary>
public EdlAction CommercialEdlAction { get; set; }
public EdlAction CommercialEdlAction { get; set; } = EdlAction.CommercialBreak;

/// <summary>
/// Gets or sets a value indicating whether to overwrite existing edl files. Which keeps the file in sync with media segment edits.
Expand All @@ -53,4 +59,19 @@ public PluginConfiguration()
/// Gets or sets the max degree of parallelism used when creating edl files.
/// </summary>
public int MaxParallelism { get; set; } = 2;

/// <summary>
/// Gets or sets the comma separated list of library names to analyze. If empty, all libraries will be analyzed.
/// </summary>
public string SelectedLibraries { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the comma separated list of tv shows and seasons to skip the analyze. Format: "My Show;S01;S02, Another Show".
/// </summary>
public string SkippedTvShows { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the comma separated list of movies to skip the analyze.".
/// </summary>
public string SkippedMovies { get; set; } = string.Empty;
}
48 changes: 48 additions & 0 deletions Jellyfin.Plugin.Edl/Configuration/configPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,46 @@
alongside your media files.
</div>
</div>
<div class="selectContainer">
<label class="selectLabel" for="UnknownEdlAction">Unknown EDL Action</label>
<select is="emby-select" id="UnknownEdlAction" class="emby-select-withcolor emby-select">
<option value="None">None (Don't add an action for preview)</option>

<option value="CommercialBreak">Commercial Break (Skip)</option>

<option value="Cut">Cut (player will remove segment 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">
EDL Action for "Unknown" Media Segments <br />
Which action to write to
<a href="https://kodi.wiki/view/Edit_decision_list">MPlayer compatible EDL files</a>
alongside your media files.
</div>
</div>

<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SelectedLibraries"> Limit analysis to the following libraries </label>
<input id="SelectedLibraries" type="text" is="emby-input" />
<div class="fieldDescription">Enter the names of libraries it should create EDL files for, separated by commas. If this field is left blank, all libraries on the server containing movies or television episodes will be used.</div>
</div>

<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SkippedTvShows"> Skip TV Shows </label>
<input id="SkippedTvShows" type="text" is="emby-input" />
<div class="fieldDescription">Enter the names of tv shows to skip the EDL creation, separated by commas. You can also skip seasons. Format: "My Show;S01;S02, Another Show, Third Show;S03;S05"</div>
</div>

<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="SkippedMovies"> Skip Movies </label>
<input id="SkippedMovies" type="text" is="emby-input" />
<div class="fieldDescription">Enter the names of Movies to skip the EDL creation, separated by commas. Format: "The Godfather, Spiderman, Matrix"</div>
</div>

<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="MaxParallelism"> Maximum degree of parallelism </label>
<input id="MaxParallelism" type="number" is="emby-input" min="1" />
Expand Down Expand Up @@ -143,27 +183,35 @@
document.querySelector("#TemplateConfigPage").addEventListener("pageshow", function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(TemplateConfig.pluginUniqueId).then(function (config) {
document.querySelector("#UnknownEdlAction").value = config.UnknownEdlAction;
document.querySelector("#IntroEdlAction").value = config.IntroEdlAction;
document.querySelector("#OutroEdlAction").value = config.OutroEdlAction;
document.querySelector("#PreviewEdlAction").value = config.PreviewEdlAction;
document.querySelector("#RecapEdlAction").value = config.RecapEdlAction;
document.querySelector("#CommercialEdlAction").value = config.CommercialEdlAction;
document.querySelector("#OverwriteEdlFiles").checked = config.OverwriteEdlFiles;
document.querySelector("#MaxParallelism").value = config.MaxParallelism;
document.querySelector("#SelectedLibraries").value = config.SelectedLibraries;
document.querySelector("#SkippedTvShows").value = config.SkippedTvShows;
document.querySelector("#SkippedMovies").value = config.SkippedMovies;
Dashboard.hideLoadingMsg();
});
});

document.querySelector("#TemplateConfigForm").addEventListener("submit", function (e) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(TemplateConfig.pluginUniqueId).then(function (config) {
config.UnknownEdlAction = document.querySelector("#UnknownEdlAction").value;
config.IntroEdlAction = document.querySelector("#IntroEdlAction").value;
config.OutroEdlAction = document.querySelector("#OutroEdlAction").value;
config.PreviewEdlAction = document.querySelector("#PreviewEdlAction").value;
config.RecapEdlAction = document.querySelector("#RecapEdlAction").value;
config.CommercialEdlAction = document.querySelector("#CommercialEdlAction").value;
config.OverwriteEdlFiles = document.querySelector("#OverwriteEdlFiles").checked;
config.MaxParallelism = document.querySelector("#MaxParallelism").value;
config.SelectedLibraries = document.querySelector("#SelectedLibraries").value;
config.SkippedTvShows = document.querySelector("#SkippedTvShows").value;
config.SkippedMovies = document.querySelector("#SkippedMovies").value;
ApiClient.updatePluginConfiguration(TemplateConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
Expand Down
133 changes: 133 additions & 0 deletions Jellyfin.Plugin.Edl/Controllers/PluginEdlController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.EdlManager;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.MediaSegments;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.Edl.Controllers;

/// <summary>
/// PluginEdl controller.
/// </summary>
[Authorize(Policy = "RequiresElevation")]
[ApiController]
[Produces(MediaTypeNames.Application.Json)]
[Route("PluginEdl")]
public class PluginEdlController : ControllerBase
{
private readonly ILoggerFactory _loggerFactory;
private readonly ILibraryManager _libraryManager;
private readonly IMediaSegmentManager _mediaSegmentManager;

/// <summary>
/// Initializes a new instance of the <see cref="PluginEdlController"/> class.
/// </summary>
/// <param name="loggerFactory">Logger factory.</param>
/// <param name="libraryManager">LibraryManager.</param>
/// <param name="mediaSegmentManager">MediaSegmentsManager.</param>
public PluginEdlController(
ILoggerFactory loggerFactory,
ILibraryManager libraryManager,
IMediaSegmentManager mediaSegmentManager)
{
_loggerFactory = loggerFactory;
_libraryManager = libraryManager;
_mediaSegmentManager = mediaSegmentManager;
}

/// <summary>
/// Plugin meta endpoint.
/// </summary>
/// <returns>The version info.</returns>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public JsonResult GetPluginMetadata()
{
var json = new
{
version = Plugin.Instance!.Version.ToString(3),
};

return new JsonResult(json);
}

/// <summary>
/// Get Edl data based on itemId.
/// </summary>
/// <param name="itemId">ItemId.</param>
/// <returns>The edl data.</returns>
[HttpGet("Edl/{itemId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<JsonResult> GetEdlData(
[FromRoute, Required] Guid itemId)
{
var queueManager = new QueueManager(_loggerFactory.CreateLogger<QueueManager>(), _libraryManager);

var segmentsList = new List<MediaSegmentDto>();
// get ItemIds
var mediaItems = queueManager.GetMediaItemsById([itemId]);
// get MediaSegments from itemIds
foreach (var kvp in mediaItems)
{
foreach (var media in kvp.Value)
{
segmentsList.AddRange(await _mediaSegmentManager.GetSegmentsAsync(media.ItemId, null).ConfigureAwait(false));
}
}

var rawstring = EdlManager.ToEdl(segmentsList.AsReadOnly());

var json = new
{
itemId,
edl = rawstring
};

return new JsonResult(json);
}

/// <summary>
/// Force edl recreation for itemIds.
/// </summary>
/// <param name="itemIds">ItemIds.</param>
/// <returns>Ok.</returns>
[HttpPost("Edl")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<OkResult> GenerateData(
[FromBody, Required] Guid[] itemIds)
{
var baseEdlTask = new BaseEdlTask(
_loggerFactory.CreateLogger<CreateEdlTask>());

var queueManager = new QueueManager(_loggerFactory.CreateLogger<QueueManager>(), _libraryManager);

var segmentsList = new List<MediaSegmentDto>();
// get ItemIds
var mediaItems = queueManager.GetMediaItemsById(itemIds);
// get MediaSegments from itemIds
foreach (var kvp in mediaItems)
{
foreach (var media in kvp.Value)
{
segmentsList.AddRange(await _mediaSegmentManager.GetSegmentsAsync(media.ItemId, null).ConfigureAwait(false));
}
}

IProgress<double> progress = new Progress<double>();
CancellationToken cancellationToken = CancellationToken.None;

// write edl files
baseEdlTask.CreateEdls(progress, segmentsList.AsReadOnly(), true, cancellationToken);

return new OkResult();
}
}
Loading

0 comments on commit eeb29c7

Please sign in to comment.