From a15b426e73afb46d7337b98f8279e83847e20f2d Mon Sep 17 00:00:00 2001 From: Piotr Niełacny Date: Tue, 24 Mar 2026 11:02:10 +0100 Subject: Fix external subtitle stream mapping for multi-stream containers Compute the in-file stream index for external subtitles instead of hardcoding -map 1:0. For single-stream files (SRT/ASS/VTT) the index is always 0, preserving existing behavior. For multi-stream containers like MKS, the correct track is selected by counting sibling streams that share the same Path. Add unit tests for GetMapArgs covering internal subs, external SRT, multiple external files, and multi-stream MKS containers. --- .../MediaEncoding/EncodingHelperTests.cs | 216 +++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs (limited to 'tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs') diff --git a/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs new file mode 100644 index 0000000000..e6276f0e9e --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using Jellyfin.Data.Enums; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.Streaming; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using Moq; +using Xunit; + +using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; + +namespace Jellyfin.Controller.Tests.MediaEncoding +{ + public class EncodingHelperTests + { + [Fact] + public void GetMapArgs_NoSubtitle_ExcludesAllSubs() + { + var state = BuildState(subtitle: null, deliveryMethod: null); + var args = CreateHelper().GetMapArgs(state); + + Assert.Contains("-map -0:s", args, StringComparison.Ordinal); + Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_InternalSrt_MapsFromPrimaryInput() + { + var sub = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; + var state = BuildState(sub, SubtitleDeliveryMethod.Embed); + var args = CreateHelper().GetMapArgs(state); + + Assert.Contains("-map 0:2", args, StringComparison.Ordinal); + Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_InternalSubAtHigherIndex_MapsCorrectIndex() + { + var sub0 = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; + var sub1 = new MediaStream { Index = 3, Type = MediaStreamType.Subtitle, Codec = "ass" }; + var state = BuildState(sub1, SubtitleDeliveryMethod.Embed, additionalStreams: [sub0, sub1]); + var args = CreateHelper().GetMapArgs(state); + + Assert.Contains("-map 0:3", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_ExternalSrt_MapsFirstStreamFromInput1() + { + var sub = new MediaStream + { + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.en.srt" + }; + var state = BuildState(sub, SubtitleDeliveryMethod.Embed); + var args = CreateHelper().GetMapArgs(state); + + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_SecondExternalSrt_StillMaps1Colon0() + { + var ext1 = new MediaStream + { + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.en.srt" + }; + var ext2 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.fr.srt" + }; + var state = BuildState(ext2, SubtitleDeliveryMethod.Embed, additionalStreams: [ext1, ext2]); + var args = CreateHelper().GetMapArgs(state); + + // Different file from ext1, so in-file index is 0 + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_MksFirstTrack_MapsInFileIndex0() + { + var mks0 = new MediaStream + { + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks1 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "ass", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var state = BuildState(mks0, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1]); + var args = CreateHelper().GetMapArgs(state); + + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_MksSecondTrack_MapsInFileIndex1() + { + var mks0 = new MediaStream + { + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks1 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "ass", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks2 = new MediaStream + { + Index = 4, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var state = BuildState(mks1, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1, mks2]); + var args = CreateHelper().GetMapArgs(state); + + // Second track in the same .mks file → in-file index 1 + Assert.Contains("-map 1:1", args, StringComparison.Ordinal); + } + + private static EncodingJobInfo BuildState( + MediaStream? subtitle, + SubtitleDeliveryMethod? deliveryMethod, + MediaStream[]? additionalStreams = null) + { + var video = new MediaStream { Index = 0, Type = MediaStreamType.Video, Codec = "h264" }; + var audio = new MediaStream { Index = 1, Type = MediaStreamType.Audio, Codec = "aac" }; + var streams = new List { video, audio }; + + if (additionalStreams is not null) + { + streams.AddRange(additionalStreams); + } + else if (subtitle is not null) + { + streams.Add(subtitle); + } + + return new EncodingJobInfo(TranscodingJobType.Progressive) + { + MediaSource = new MediaSourceInfo + { + Container = "mkv", + MediaStreams = streams, + }, + VideoStream = video, + AudioStream = audio, + SubtitleStream = subtitle, + SubtitleDeliveryMethod = deliveryMethod ?? SubtitleDeliveryMethod.Drop, + BaseRequest = new VideoRequestDto(), + IsVideoRequest = true, + IsInputVideo = true, + }; + } + + private static EncodingHelper CreateHelper() + { + var appPaths = Mock.Of(); + var mediaEncoder = new Mock(); + var subtitleEncoder = new Mock(); + var config = new Mock(); + var configurationManager = new Mock(); + var pathManager = new Mock(); + + return new EncodingHelper( + appPaths, + mediaEncoder.Object, + subtitleEncoder.Object, + config.Object, + configurationManager.Object, + pathManager.Object); + } + } +} -- cgit v1.2.3 From 405d987557b2638afc89edf3dff20360e39cb09a Mon Sep 17 00:00:00 2001 From: Piotr Niełacny Date: Tue, 24 Mar 2026 14:26:25 +0100 Subject: Normalize VobSub .sub to .idx for embedding, add EncodingHelper tests Move the .sub to .idx path normalization outside the burn-in check so it applies to subtitle embedding as well. ffmpeg requires the .idx file to read VobSub subtitles. Add unit tests for GetMapArgs and GetInputArgument covering internal subs, external SRT, multi-file SRT, multi-stream MKS containers, and VobSub .sub/.idx path normalization. --- .../MediaEncoding/EncodingHelper.cs | 16 +++----- .../MediaEncoding/EncodingHelperTests.cs | 47 +++++++++++++++++++++- 2 files changed, 51 insertions(+), 12 deletions(-) (limited to 'tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs') diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 40bb3913e9..6ff2873cc5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1272,18 +1272,14 @@ namespace MediaBrowser.Controller.MediaEncoding var subtitlePath = state.SubtitleStream.Path; var isGraphicalBurnIn = ShouldEncodeSubtitle(state) && !state.SubtitleStream.IsTextSubtitleStream; - if (isGraphicalBurnIn) + // dvdsub/vobsub graphical subtitles use .sub+.idx pairs + var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan()); + if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase)) { - var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan()); - - // dvdsub/vobsub graphical subtitles use .sub+.idx pairs - if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase)) + var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); + if (File.Exists(idxFile)) { - var idxFile = Path.ChangeExtension(subtitlePath, ".idx"); - if (File.Exists(idxFile)) - { - subtitlePath = idxFile; - } + subtitlePath = idxFile; } } diff --git a/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs index e6276f0e9e..6563dd63ab 100644 --- a/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs +++ b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Streaming; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -71,6 +73,8 @@ namespace Jellyfin.Controller.Tests.MediaEncoding [Fact] public void GetMapArgs_SecondExternalSrt_StillMaps1Colon0() { + // Two separate .srt files — selecting the second one still maps 1:0 + // because Jellyfin feeds only the selected file as ffmpeg input 1. var ext1 = new MediaStream { Index = 2, @@ -92,7 +96,6 @@ namespace Jellyfin.Controller.Tests.MediaEncoding var state = BuildState(ext2, SubtitleDeliveryMethod.Embed, additionalStreams: [ext1, ext2]); var args = CreateHelper().GetMapArgs(state); - // Different file from ext1, so in-file index is 0 Assert.Contains("-map 1:0", args, StringComparison.Ordinal); } @@ -156,10 +159,50 @@ namespace Jellyfin.Controller.Tests.MediaEncoding var state = BuildState(mks1, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1, mks2]); var args = CreateHelper().GetMapArgs(state); - // Second track in the same .mks file → in-file index 1 Assert.Contains("-map 1:1", args, StringComparison.Ordinal); } + [Theory] + [InlineData(SubtitleDeliveryMethod.Embed, true, "movie.idx")] + [InlineData(SubtitleDeliveryMethod.Encode, true, "movie.idx")] + [InlineData(SubtitleDeliveryMethod.Embed, false, "movie.sub")] + [InlineData(SubtitleDeliveryMethod.Encode, false, "movie.sub")] + public void GetInputArgument_VobSub_UsesCorrectPath( + SubtitleDeliveryMethod deliveryMethod, + bool createIdxFile, + string expectedFilename) + { + var tempDir = Directory.CreateTempSubdirectory("jellyfin-test-"); + try + { + var subFile = Path.Combine(tempDir.FullName, "movie.sub"); + File.WriteAllText(subFile, "dummy"); + + if (createIdxFile) + { + File.WriteAllText(Path.Combine(tempDir.FullName, "movie.idx"), "dummy"); + } + + var sub = new MediaStream + { + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "dvdsub", + IsExternal = true, + SupportsExternalStream = true, + Path = subFile + }; + var state = BuildState(sub, deliveryMethod); + var inputArgs = CreateHelper().GetInputArgument(state, new EncodingOptions(), null); + + Assert.Contains(expectedFilename, inputArgs, StringComparison.Ordinal); + } + finally + { + tempDir.Delete(true); + } + } + private static EncodingJobInfo BuildState( MediaStream? subtitle, SubtitleDeliveryMethod? deliveryMethod, -- cgit v1.2.3 From f6af1a9fb626e4a2f39f0caceebb590340d54c72 Mon Sep 17 00:00:00 2001 From: Piotr Niełacny Date: Tue, 19 May 2026 13:01:25 +0200 Subject: Use file-scoped namespace in EncodingHelperTests --- .../MediaEncoding/EncodingHelperTests.cs | 405 ++++++++++----------- 1 file changed, 202 insertions(+), 203 deletions(-) (limited to 'tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs') diff --git a/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs index 6563dd63ab..d7ae6a8a18 100644 --- a/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs +++ b/tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs @@ -16,244 +16,243 @@ using Xunit; using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; -namespace Jellyfin.Controller.Tests.MediaEncoding +namespace Jellyfin.Controller.Tests.MediaEncoding; + +public class EncodingHelperTests { - public class EncodingHelperTests + [Fact] + public void GetMapArgs_NoSubtitle_ExcludesAllSubs() { - [Fact] - public void GetMapArgs_NoSubtitle_ExcludesAllSubs() - { - var state = BuildState(subtitle: null, deliveryMethod: null); - var args = CreateHelper().GetMapArgs(state); + var state = BuildState(subtitle: null, deliveryMethod: null); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map -0:s", args, StringComparison.Ordinal); - Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); - } + Assert.Contains("-map -0:s", args, StringComparison.Ordinal); + Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); + } - [Fact] - public void GetMapArgs_InternalSrt_MapsFromPrimaryInput() - { - var sub = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; - var state = BuildState(sub, SubtitleDeliveryMethod.Embed); - var args = CreateHelper().GetMapArgs(state); + [Fact] + public void GetMapArgs_InternalSrt_MapsFromPrimaryInput() + { + var sub = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; + var state = BuildState(sub, SubtitleDeliveryMethod.Embed); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map 0:2", args, StringComparison.Ordinal); - Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); - } + Assert.Contains("-map 0:2", args, StringComparison.Ordinal); + Assert.DoesNotContain("-map 1:", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_InternalSubAtHigherIndex_MapsCorrectIndex() + { + var sub0 = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; + var sub1 = new MediaStream { Index = 3, Type = MediaStreamType.Subtitle, Codec = "ass" }; + var state = BuildState(sub1, SubtitleDeliveryMethod.Embed, additionalStreams: [sub0, sub1]); + var args = CreateHelper().GetMapArgs(state); - [Fact] - public void GetMapArgs_InternalSubAtHigherIndex_MapsCorrectIndex() + Assert.Contains("-map 0:3", args, StringComparison.Ordinal); + } + + [Fact] + public void GetMapArgs_ExternalSrt_MapsFirstStreamFromInput1() + { + var sub = new MediaStream { - var sub0 = new MediaStream { Index = 2, Type = MediaStreamType.Subtitle, Codec = "srt" }; - var sub1 = new MediaStream { Index = 3, Type = MediaStreamType.Subtitle, Codec = "ass" }; - var state = BuildState(sub1, SubtitleDeliveryMethod.Embed, additionalStreams: [sub0, sub1]); - var args = CreateHelper().GetMapArgs(state); + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.en.srt" + }; + var state = BuildState(sub, SubtitleDeliveryMethod.Embed); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map 0:3", args, StringComparison.Ordinal); - } + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } - [Fact] - public void GetMapArgs_ExternalSrt_MapsFirstStreamFromInput1() + [Fact] + public void GetMapArgs_SecondExternalSrt_StillMaps1Colon0() + { + // Two separate .srt files — selecting the second one still maps 1:0 + // because Jellyfin feeds only the selected file as ffmpeg input 1. + var ext1 = new MediaStream { - var sub = new MediaStream - { - Index = 2, - Type = MediaStreamType.Subtitle, - Codec = "srt", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.en.srt" - }; - var state = BuildState(sub, SubtitleDeliveryMethod.Embed); - var args = CreateHelper().GetMapArgs(state); + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.en.srt" + }; + var ext2 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "srt", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.fr.srt" + }; + var state = BuildState(ext2, SubtitleDeliveryMethod.Embed, additionalStreams: [ext1, ext2]); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map 1:0", args, StringComparison.Ordinal); - } + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } - [Fact] - public void GetMapArgs_SecondExternalSrt_StillMaps1Colon0() + [Fact] + public void GetMapArgs_MksFirstTrack_MapsInFileIndex0() + { + var mks0 = new MediaStream { - // Two separate .srt files — selecting the second one still maps 1:0 - // because Jellyfin feeds only the selected file as ffmpeg input 1. - var ext1 = new MediaStream - { - Index = 2, - Type = MediaStreamType.Subtitle, - Codec = "srt", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.en.srt" - }; - var ext2 = new MediaStream - { - Index = 3, - Type = MediaStreamType.Subtitle, - Codec = "srt", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.fr.srt" - }; - var state = BuildState(ext2, SubtitleDeliveryMethod.Embed, additionalStreams: [ext1, ext2]); - var args = CreateHelper().GetMapArgs(state); + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks1 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "ass", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var state = BuildState(mks0, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1]); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map 1:0", args, StringComparison.Ordinal); - } + Assert.Contains("-map 1:0", args, StringComparison.Ordinal); + } - [Fact] - public void GetMapArgs_MksFirstTrack_MapsInFileIndex0() + [Fact] + public void GetMapArgs_MksSecondTrack_MapsInFileIndex1() + { + var mks0 = new MediaStream { - var mks0 = new MediaStream - { - Index = 2, - Type = MediaStreamType.Subtitle, - Codec = "subrip", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.mks" - }; - var mks1 = new MediaStream - { - Index = 3, - Type = MediaStreamType.Subtitle, - Codec = "ass", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.mks" - }; - var state = BuildState(mks0, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1]); - var args = CreateHelper().GetMapArgs(state); + Index = 2, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks1 = new MediaStream + { + Index = 3, + Type = MediaStreamType.Subtitle, + Codec = "ass", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var mks2 = new MediaStream + { + Index = 4, + Type = MediaStreamType.Subtitle, + Codec = "subrip", + IsExternal = true, + SupportsExternalStream = true, + Path = "/media/movie.mks" + }; + var state = BuildState(mks1, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1, mks2]); + var args = CreateHelper().GetMapArgs(state); - Assert.Contains("-map 1:0", args, StringComparison.Ordinal); - } + Assert.Contains("-map 1:1", args, StringComparison.Ordinal); + } - [Fact] - public void GetMapArgs_MksSecondTrack_MapsInFileIndex1() + [Theory] + [InlineData(SubtitleDeliveryMethod.Embed, true, "movie.idx")] + [InlineData(SubtitleDeliveryMethod.Encode, true, "movie.idx")] + [InlineData(SubtitleDeliveryMethod.Embed, false, "movie.sub")] + [InlineData(SubtitleDeliveryMethod.Encode, false, "movie.sub")] + public void GetInputArgument_VobSub_UsesCorrectPath( + SubtitleDeliveryMethod deliveryMethod, + bool createIdxFile, + string expectedFilename) + { + var tempDir = Directory.CreateTempSubdirectory("jellyfin-test-"); + try { - var mks0 = new MediaStream - { - Index = 2, - Type = MediaStreamType.Subtitle, - Codec = "subrip", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.mks" - }; - var mks1 = new MediaStream + var subFile = Path.Combine(tempDir.FullName, "movie.sub"); + File.WriteAllText(subFile, "dummy"); + + if (createIdxFile) { - Index = 3, - Type = MediaStreamType.Subtitle, - Codec = "ass", - IsExternal = true, - SupportsExternalStream = true, - Path = "/media/movie.mks" - }; - var mks2 = new MediaStream + File.WriteAllText(Path.Combine(tempDir.FullName, "movie.idx"), "dummy"); + } + + var sub = new MediaStream { - Index = 4, + Index = 2, Type = MediaStreamType.Subtitle, - Codec = "subrip", + Codec = "dvdsub", IsExternal = true, SupportsExternalStream = true, - Path = "/media/movie.mks" + Path = subFile }; - var state = BuildState(mks1, SubtitleDeliveryMethod.Embed, additionalStreams: [mks0, mks1, mks2]); - var args = CreateHelper().GetMapArgs(state); + var state = BuildState(sub, deliveryMethod); + var inputArgs = CreateHelper().GetInputArgument(state, new EncodingOptions(), null); - Assert.Contains("-map 1:1", args, StringComparison.Ordinal); + Assert.Contains(expectedFilename, inputArgs, StringComparison.Ordinal); } - - [Theory] - [InlineData(SubtitleDeliveryMethod.Embed, true, "movie.idx")] - [InlineData(SubtitleDeliveryMethod.Encode, true, "movie.idx")] - [InlineData(SubtitleDeliveryMethod.Embed, false, "movie.sub")] - [InlineData(SubtitleDeliveryMethod.Encode, false, "movie.sub")] - public void GetInputArgument_VobSub_UsesCorrectPath( - SubtitleDeliveryMethod deliveryMethod, - bool createIdxFile, - string expectedFilename) + finally { - var tempDir = Directory.CreateTempSubdirectory("jellyfin-test-"); - try - { - var subFile = Path.Combine(tempDir.FullName, "movie.sub"); - File.WriteAllText(subFile, "dummy"); - - if (createIdxFile) - { - File.WriteAllText(Path.Combine(tempDir.FullName, "movie.idx"), "dummy"); - } + tempDir.Delete(true); + } + } - var sub = new MediaStream - { - Index = 2, - Type = MediaStreamType.Subtitle, - Codec = "dvdsub", - IsExternal = true, - SupportsExternalStream = true, - Path = subFile - }; - var state = BuildState(sub, deliveryMethod); - var inputArgs = CreateHelper().GetInputArgument(state, new EncodingOptions(), null); + private static EncodingJobInfo BuildState( + MediaStream? subtitle, + SubtitleDeliveryMethod? deliveryMethod, + MediaStream[]? additionalStreams = null) + { + var video = new MediaStream { Index = 0, Type = MediaStreamType.Video, Codec = "h264" }; + var audio = new MediaStream { Index = 1, Type = MediaStreamType.Audio, Codec = "aac" }; + var streams = new List { video, audio }; - Assert.Contains(expectedFilename, inputArgs, StringComparison.Ordinal); - } - finally - { - tempDir.Delete(true); - } + if (additionalStreams is not null) + { + streams.AddRange(additionalStreams); } - - private static EncodingJobInfo BuildState( - MediaStream? subtitle, - SubtitleDeliveryMethod? deliveryMethod, - MediaStream[]? additionalStreams = null) + else if (subtitle is not null) { - var video = new MediaStream { Index = 0, Type = MediaStreamType.Video, Codec = "h264" }; - var audio = new MediaStream { Index = 1, Type = MediaStreamType.Audio, Codec = "aac" }; - var streams = new List { video, audio }; - - if (additionalStreams is not null) - { - streams.AddRange(additionalStreams); - } - else if (subtitle is not null) - { - streams.Add(subtitle); - } - - return new EncodingJobInfo(TranscodingJobType.Progressive) - { - MediaSource = new MediaSourceInfo - { - Container = "mkv", - MediaStreams = streams, - }, - VideoStream = video, - AudioStream = audio, - SubtitleStream = subtitle, - SubtitleDeliveryMethod = deliveryMethod ?? SubtitleDeliveryMethod.Drop, - BaseRequest = new VideoRequestDto(), - IsVideoRequest = true, - IsInputVideo = true, - }; + streams.Add(subtitle); } - private static EncodingHelper CreateHelper() + return new EncodingJobInfo(TranscodingJobType.Progressive) { - var appPaths = Mock.Of(); - var mediaEncoder = new Mock(); - var subtitleEncoder = new Mock(); - var config = new Mock(); - var configurationManager = new Mock(); - var pathManager = new Mock(); + MediaSource = new MediaSourceInfo + { + Container = "mkv", + MediaStreams = streams, + }, + VideoStream = video, + AudioStream = audio, + SubtitleStream = subtitle, + SubtitleDeliveryMethod = deliveryMethod ?? SubtitleDeliveryMethod.Drop, + BaseRequest = new VideoRequestDto(), + IsVideoRequest = true, + IsInputVideo = true, + }; + } - return new EncodingHelper( - appPaths, - mediaEncoder.Object, - subtitleEncoder.Object, - config.Object, - configurationManager.Object, - pathManager.Object); - } + private static EncodingHelper CreateHelper() + { + var appPaths = Mock.Of(); + var mediaEncoder = new Mock(); + var subtitleEncoder = new Mock(); + var config = new Mock(); + var configurationManager = new Mock(); + var pathManager = new Mock(); + + return new EncodingHelper( + appPaths, + mediaEncoder.Object, + subtitleEncoder.Object, + config.Object, + configurationManager.Object, + pathManager.Object); } } -- cgit v1.2.3