aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Jellyfin.Api.Tests/Controllers/SubtitleControllerTests.cs40
-rw-r--r--tests/Jellyfin.Api.Tests/Helpers/DynamicHlsHelperTests.cs26
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs21
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs40
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions.json81
-rw-r--r--tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions_zero_dar.json83
-rw-r--r--tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs13
7 files changed, 304 insertions, 0 deletions
diff --git a/tests/Jellyfin.Api.Tests/Controllers/SubtitleControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/SubtitleControllerTests.cs
index 77321f0b05..36b4e7156e 100644
--- a/tests/Jellyfin.Api.Tests/Controllers/SubtitleControllerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Controllers/SubtitleControllerTests.cs
@@ -134,4 +134,44 @@ public class SubtitleControllerTests
fileSystem.Object,
NullLogger<SubtitleController>.Instance);
}
+
+ [Theory]
+ [MemberData(nameof(GetVttTimestampMap_Success_TestData))]
+ public static void GetVttTimestampMap_Success(long mpegTimestamp, string expected)
+ {
+ Assert.Equal(expected, SubtitleController.GetVttTimestampMap(mpegTimestamp));
+ }
+
+ public static TheoryData<long, string> GetVttTimestampMap_Success_TestData()
+ {
+ var data = new TheoryData<long, string>();
+ data.Add(900000, "X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000");
+ data.Add(0, "X-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000");
+ return data;
+ }
+
+ [Theory]
+ [MemberData(nameof(GetSubtitleSegmentUrl_Success_TestData))]
+ public static void GetSubtitleSegmentUrl_Success(long positionTicks, long endPositionTicks, string accessToken, long vttTimestampMapMpegts, string expected)
+ {
+ Assert.Equal(expected, SubtitleController.GetSubtitleSegmentUrl(positionTicks, endPositionTicks, accessToken, vttTimestampMapMpegts));
+ }
+
+ public static TheoryData<long, long, string, long, string> GetSubtitleSegmentUrl_Success_TestData()
+ {
+ var data = new TheoryData<long, long, string, long, string>();
+ data.Add(
+ 0,
+ 300000000,
+ "abc123",
+ 0,
+ "stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks=0&EndPositionTicks=300000000&ApiKey=abc123&VttTimestampMapMpegts=0");
+ data.Add(
+ 300000000,
+ 600000000,
+ "abc123",
+ 900000,
+ "stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks=300000000&EndPositionTicks=600000000&ApiKey=abc123&VttTimestampMapMpegts=900000");
+ return data;
+ }
}
diff --git a/tests/Jellyfin.Api.Tests/Helpers/DynamicHlsHelperTests.cs b/tests/Jellyfin.Api.Tests/Helpers/DynamicHlsHelperTests.cs
new file mode 100644
index 0000000000..d17ce0f896
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Helpers/DynamicHlsHelperTests.cs
@@ -0,0 +1,26 @@
+using Jellyfin.Api.Helpers;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Helpers
+{
+ public static class DynamicHlsHelperTests
+ {
+ [Theory]
+ [MemberData(nameof(GetVttTimestampMapMpegts_Success_TestData))]
+ public static void GetVttTimestampMapMpegts_Success(string? segmentContainer, long expected)
+ {
+ Assert.Equal(expected, DynamicHlsHelper.GetVttTimestampMapMpegts(segmentContainer));
+ }
+
+ public static TheoryData<string?, long> GetVttTimestampMapMpegts_Success_TestData()
+ {
+ var data = new TheoryData<string?, long>();
+ data.Add(null, 900000);
+ data.Add(string.Empty, 900000);
+ data.Add("ts", 900000);
+ data.Add("mp4", 0);
+ data.Add("MP4", 0);
+ return data;
+ }
+ }
+}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
index 52e0b19700..cf9930abe1 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
@@ -242,6 +242,27 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
Assert.False(res.MediaStreams[0].IsAVC);
}
+ [Theory]
+ [InlineData("Test Data/Probing/video_ts_no_dimensions.json", null)]
+ [InlineData("Test Data/Probing/video_ts_no_dimensions_zero_dar.json", "0:1")]
+ public void GetMediaInfo_TSWithoutDimensions_Success(string path, string? expectedAspectRatio)
+ {
+ var bytes = File.ReadAllBytes(path);
+ var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+
+ MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "http://192.168.1.19:5004/auto/v4", MediaProtocol.Http);
+
+ Assert.Equal(2, res.MediaStreams.Count);
+
+ var video = res.MediaStreams[0];
+ Assert.Equal(MediaStreamType.Video, video.Type);
+ Assert.Equal("mpeg2video", video.Codec);
+ Assert.Null(video.Width);
+ Assert.Null(video.Height);
+ Assert.Equal(expectedAspectRatio, video.AspectRatio);
+ Assert.False(video.IsAnamorphic);
+ }
+
[Fact]
public void GetMediaInfo_WebM_Success()
{
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs
index 48850b2f67..13779851a9 100644
--- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs
+++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs
@@ -269,6 +269,46 @@ namespace Jellyfin.MediaEncoding.Subtitles.Tests
}
}
+ [Fact]
+ public async Task GetSubtitleStream_UnsupportedEncoding_DoesNotThrow()
+ {
+ // Regression test for issue #2200
+ // When the charset detector identifies an encoding not supported by .NET (e.g., ISO-8859-16),
+ // GetSubtitleStream should handle it gracefully instead of throwing ArgumentNullException
+ var cancellationToken = TestContext.Current.CancellationToken;
+ var srt = BuildGreekSrt();
+ var path = Path.GetTempFileName();
+ try
+ {
+ // Write as ISO-8859-7 (Greek) to create a non-UTF-8 file
+ await File.WriteAllTextAsync(path, srt, Encoding.GetEncoding("iso-8859-7"), cancellationToken);
+
+ var fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
+ var subtitleEncoder = fixture.Create<SubtitleEncoder>();
+
+ var fileInfo = new SubtitleEncoder.SubtitleInfo
+ {
+ Path = path,
+ Protocol = MediaProtocol.File,
+ Format = "srt",
+ IsExternal = true
+ };
+
+ // This should not throw even if the detected encoding was unsupported
+ using var stream = await subtitleEncoder.GetSubtitleStream(fileInfo, cancellationToken);
+ Assert.NotNull(stream);
+
+ // Verify we can read from the stream without error
+ using var reader = new StreamReader(stream);
+ var text = await reader.ReadToEndAsync(cancellationToken);
+ Assert.NotEmpty(text);
+ }
+ finally
+ {
+ File.Delete(path);
+ }
+ }
+
private static SubtitleEncoder CreateEncoder()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true });
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions.json
new file mode 100644
index 0000000000..cc45b48fff
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions.json
@@ -0,0 +1,81 @@
+{
+ "streams": [
+ {
+ "index": 0,
+ "codec_name": "mpeg2video",
+ "codec_long_name": "MPEG-2 video",
+ "profile": "Main",
+ "codec_type": "video",
+ "codec_tag_string": "[2][0][0][0]",
+ "codec_tag": "0x0002",
+ "pix_fmt": "yuv420p",
+ "level": 8,
+ "id": "0x259",
+ "r_frame_rate": "25/1",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 6181452600,
+ "start_time": "68682.806667",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "mp2",
+ "codec_long_name": "MP2 (MPEG audio layer 2)",
+ "codec_type": "audio",
+ "codec_tag_string": "[3][0][0][0]",
+ "codec_tag": "0x0003",
+ "sample_fmt": "fltp",
+ "sample_rate": "48000",
+ "channels": 2,
+ "channel_layout": "stereo",
+ "bits_per_sample": 0,
+ "id": "0x25a",
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 6181460000,
+ "start_time": "68682.888889",
+ "bit_rate": "256000",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ },
+ "tags": {
+ "language": "eng"
+ }
+ }
+ ],
+ "format": {
+ "filename": "http://192.168.1.19:5004/auto/v4",
+ "nb_streams": 2,
+ "nb_programs": 1,
+ "format_name": "mpegts",
+ "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)",
+ "start_time": "68682.806667",
+ "probe_score": 50
+ }
+}
diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions_zero_dar.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions_zero_dar.json
new file mode 100644
index 0000000000..c249efda3c
--- /dev/null
+++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts_no_dimensions_zero_dar.json
@@ -0,0 +1,83 @@
+{
+ "streams": [
+ {
+ "index": 0,
+ "codec_name": "mpeg2video",
+ "codec_long_name": "MPEG-2 video",
+ "profile": "Main",
+ "codec_type": "video",
+ "codec_tag_string": "[2][0][0][0]",
+ "codec_tag": "0x0002",
+ "sample_aspect_ratio": "0:1",
+ "display_aspect_ratio": "0:1",
+ "pix_fmt": "yuv420p",
+ "level": 8,
+ "id": "0x259",
+ "r_frame_rate": "25/1",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 6181452600,
+ "start_time": "68682.806667",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ }
+ },
+ {
+ "index": 1,
+ "codec_name": "mp2",
+ "codec_long_name": "MP2 (MPEG audio layer 2)",
+ "codec_type": "audio",
+ "codec_tag_string": "[3][0][0][0]",
+ "codec_tag": "0x0003",
+ "sample_fmt": "fltp",
+ "sample_rate": "48000",
+ "channels": 2,
+ "channel_layout": "stereo",
+ "bits_per_sample": 0,
+ "id": "0x25a",
+ "r_frame_rate": "0/0",
+ "avg_frame_rate": "0/0",
+ "time_base": "1/90000",
+ "start_pts": 6181460000,
+ "start_time": "68682.888889",
+ "bit_rate": "256000",
+ "disposition": {
+ "default": 0,
+ "dub": 0,
+ "original": 0,
+ "comment": 0,
+ "lyrics": 0,
+ "karaoke": 0,
+ "forced": 0,
+ "hearing_impaired": 0,
+ "visual_impaired": 0,
+ "clean_effects": 0,
+ "attached_pic": 0,
+ "timed_thumbnails": 0
+ },
+ "tags": {
+ "language": "eng"
+ }
+ }
+ ],
+ "format": {
+ "filename": "http://192.168.1.19:5004/auto/v4",
+ "nb_streams": 2,
+ "nb_programs": 1,
+ "format_name": "mpegts",
+ "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)",
+ "start_time": "68682.806667",
+ "probe_score": 50
+ }
+}
diff --git a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
index 8342621d97..6e36c08064 100644
--- a/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Tmdb/TmdbUtilsTests.cs
@@ -219,6 +219,19 @@ namespace Jellyfin.Providers.Tests.Tmdb
[Movie(1, "Some Movie", "Some Movie", 2015), Movie(2, "Some Movie", "Some Movie", 2010)],
2
},
+ // M. Night Shyamalan's Split premiered at a festival in 2016 and was released in 2017, so the obscure
+ // 2016 film of the same name must not outrank it. Years one apart tie and TMDb's own ordering decides.
+ {
+ "Split (2016)", "Split", 2016,
+ [Movie(381288, "Split", "Split", 2017), Movie(358364, "Split", "Split", 2016)],
+ 381288
+ },
+ // Two years apart is a different film, so the tie only covers a year off by one.
+ {
+ "Two years apart", "Some Movie", 2016,
+ [Movie(1, "Some Movie", "Some Movie", 2018), Movie(2, "Some Movie", "Some Movie", 2016)],
+ 2
+ },
// Nothing matches the name, so TMDb's own ordering is kept.
{
"A Christmas No. 1 (2021)", "A Christmas No. 1", 2021,