Skip to content

Commit 369b709

Browse files
committed
Merge branch 'wip'
2 parents 24cbad3 + 960c683 commit 369b709

File tree

3 files changed

+102
-2
lines changed

3 files changed

+102
-2
lines changed

Jellyfin.Plugin.DoViRemux/Configuration/PluginConfiguration.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
public class PluginConfiguration : BasePluginConfiguration
44
{
5+
/// <summary>
6+
/// Comma-separated list of library IDs that we'll constrain remuxing to
7+
/// </summary>
8+
public string? OnlyRemuxLibraries { get; set; }
9+
510
public PluginConfiguration()
611
{
7-
812
}
913
}

Jellyfin.Plugin.DoViRemux/DoViRemuxPlugin.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

55
public class DoViRemuxPlugin : BasePlugin<PluginConfiguration>
66
{
7+
public static Guid OurGuid = Guid.Parse("2f215b63-1a73-4193-9102-78f84d027014");
78
public override string Name => nameof(DoViRemuxPlugin);
8-
public override Guid Id { get; } = Guid.Parse("2f215b63-1a73-4193-9102-78f84d027014");
9+
public override Guid Id => OurGuid;
910

1011
public DoViRemuxPlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer)
1112
{
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using Jellyfin.Data.Enums;
2+
using MediaBrowser.Common.Plugins;
3+
using MediaBrowser.Controller.Entities;
4+
using MediaBrowser.Controller.Library;
5+
using MediaBrowser.Controller.MediaEncoding;
6+
using MediaBrowser.Controller.Persistence;
7+
using MediaBrowser.Controller.Streaming;
8+
using MediaBrowser.Model.Tasks;
9+
10+
public class RemuxLibraryTask(IItemRepository _itemRepo,
11+
IMediaSourceManager _sourceManager,
12+
ITranscodeManager _transcodeManager,
13+
IPluginManager _pluginManager)
14+
: IScheduledTask
15+
{
16+
public string Name => "Remux Dolby Vision MKVs";
17+
18+
public string Key => nameof(RemuxLibraryTask);
19+
20+
public string Description => "";
21+
22+
public string Category => "Library";
23+
24+
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
25+
{
26+
return [new TaskTriggerInfo(){ Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = 0 }];
27+
}
28+
29+
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
30+
{
31+
var plugin = _pluginManager.GetPlugin(DoViRemuxPlugin.OurGuid)?.Instance as DoViRemuxPlugin
32+
?? throw new Exception("Can't get DoViRemuxPlugin instance");
33+
34+
var configuration = plugin.Configuration;
35+
36+
var allItems = _itemRepo.GetItems(new InternalItemsQuery
37+
{
38+
MediaTypes = [MediaType.Video],
39+
AncestorIds = configuration.OnlyRemuxLibraries?.Split(",").Select(Guid.Parse).ToArray()
40+
?? []
41+
});
42+
43+
foreach (var item in allItems.Items)
44+
{
45+
await ProcessOneItem(item, cancellationToken);
46+
}
47+
}
48+
49+
private async Task ProcessOneItem(BaseItem item, CancellationToken cancellationToken)
50+
{
51+
if (item.Container != "mkv") return;
52+
53+
var streams = _sourceManager.GetMediaStreams(item.Id);
54+
var doviStream = streams.FirstOrDefault(s => s.Type == MediaBrowser.Model.Entities.MediaStreamType.Video
55+
&& s.DvProfile.HasValue);
56+
57+
if (doviStream is not {DvProfile: 8, DvVersionMajor: 1}) return;
58+
59+
// if there's an existing MP4 source, assume we made it.
60+
// also I can't decide if I like that the model object comes back with services inside it
61+
// which can run lookups like this. The API is sort of clean, actually, but... am I just a hater?
62+
var otherSources = item.GetMediaSources(true);
63+
if (otherSources.Any(s => s.Container == "mp4")) return;
64+
65+
var ourSource = otherSources.First(s => s.Container == "mkv");
66+
67+
var inputPath = ourSource.Path;
68+
var outputPath = $"{inputPath}.mp4";
69+
70+
var remuxRequest = new StreamState(_sourceManager, TranscodingJobType.Progressive, _transcodeManager);
71+
72+
remuxRequest.MediaSource = ourSource;
73+
remuxRequest.Request = new StreamingRequestDto
74+
{
75+
LiveStreamId = null // i don't remember why this has to be null
76+
};
77+
78+
remuxRequest.MediaPath = outputPath;
79+
80+
// avoids NREs and changes the log filename prefix from "Transcode" to "Remux"
81+
remuxRequest.OutputContainer = "mp4";
82+
remuxRequest.OutputAudioCodec = "copy";
83+
remuxRequest.OutputVideoCodec = "copy";
84+
85+
string cli = "-analyzeduration 200M -probesize 1G -fflags +genpts ";
86+
cli += $"-i \"{inputPath}\" ";
87+
cli += "-map_metadata -1 -map_chapters -1 -threads 0 -map 0:0 -map 0:1 -map -0:s ";
88+
cli += "-codec:v:0 copy -tag:v:0 dvh1 -strict -2 -bsf:v hevc_mp4toannexb -start_at_zero ";
89+
cli += "-codec:a:0 copy -copyts -avoid_negative_ts disabled -max_muxing_queue_size 2048 ";
90+
cli += $"\"{outputPath}\"";
91+
92+
var remuxCancelToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
93+
var job = await _transcodeManager.StartFfMpeg(remuxRequest, outputPath, cli, Guid.Empty, TranscodingJobType.Progressive, remuxCancelToken);
94+
}
95+
}

0 commit comments

Comments
 (0)