aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Niełacny <piotr.nielacny@gmail.com>2026-03-24 14:26:25 +0100
committerPiotr Niełacny <piotr.nielacny@gmail.com>2026-05-19 13:03:07 +0200
commit405d987557b2638afc89edf3dff20360e39cb09a (patch)
treeba756d44d0853db68ffe8c8b98a1bfeaace482bf
parenta15b426e73afb46d7337b98f8279e83847e20f2d (diff)
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.
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs16
-rw-r--r--tests/Jellyfin.Controller.Tests/MediaEncoding/EncodingHelperTests.cs47
2 files changed, 51 insertions, 12 deletions
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,