All checks were successful
release / build-release (push) Successful in 1m19s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
98 lines
4.2 KiB
C#
98 lines
4.2 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Jellyfin.Plugin.Floatplane.Playback;
|
|
using Xunit;
|
|
|
|
namespace Jellyfin.Plugin.Floatplane.Tests;
|
|
|
|
public class HlsRewriterTests
|
|
{
|
|
private static readonly Uri PlaylistUrl = new("https://cdn-vod-drm2.floatplane.com/Videos/dcm2jUIg1J/1080.mp4/chunk.m3u8?token=X");
|
|
|
|
[Fact]
|
|
public void Media_playlist_key_is_proxied_and_segments_made_absolute()
|
|
{
|
|
var original = Fixture.Text("chunk.m3u8");
|
|
string? seenKey = null;
|
|
|
|
var rewritten = HlsRewriter.Rewrite(
|
|
original,
|
|
PlaylistUrl,
|
|
key =>
|
|
{
|
|
seenKey = key;
|
|
return "http://jellyfin/Floatplane/Stream/dcm2jUIg1J/Key?u=abc&t=sig";
|
|
});
|
|
|
|
var lines = rewritten.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
Assert.Equal("https://www.floatplane.com/api/video/watchKey?token=eyJwYXRoIjoiL1ZpZGVvcy9kY20yalVJZzFKLzEwODAubXA0IiwidGltZXN0YW1wIjoxNzkyODg2NDAwMDAwfQ", seenKey);
|
|
Assert.Contains("#EXT-X-KEY:METHOD=AES-128,URI=\"http://jellyfin/Floatplane/Stream/dcm2jUIg1J/Key?u=abc&t=sig\"", lines);
|
|
Assert.DoesNotContain(lines, l => l.Contains("watchKey", StringComparison.Ordinal));
|
|
|
|
var segments = lines.Where(l => !l.StartsWith('#')).ToList();
|
|
Assert.Equal(6, segments.Count);
|
|
for (var i = 0; i < segments.Count; i++)
|
|
{
|
|
Assert.StartsWith("https://cdn-vod-drm2.floatplane.com/Videos/dcm2jUIg1J/1080.mp4/", segments[i]);
|
|
Assert.EndsWith($".ts?token=SEGTOKEN{i + 1}", segments[i]);
|
|
}
|
|
|
|
Assert.Equal(6, lines.Count(l => l.StartsWith("#EXTINF:", StringComparison.Ordinal)));
|
|
Assert.Equal("#EXTM3U", lines[0]);
|
|
Assert.Equal("#EXT-X-ENDLIST", lines[^1]);
|
|
Assert.Contains("#EXT-X-TARGETDURATION:12", lines);
|
|
Assert.EndsWith("\n", rewritten);
|
|
}
|
|
|
|
[Fact]
|
|
public void Segment_mapper_is_applied_when_given()
|
|
{
|
|
var rewritten = HlsRewriter.Rewrite(
|
|
Fixture.Text("chunk.m3u8"),
|
|
PlaylistUrl,
|
|
key => "KEY",
|
|
mapSegmentUri: seg => "http://proxy/seg?u=" + Uri.EscapeDataString(seg));
|
|
|
|
var segments = rewritten.Split('\n', StringSplitOptions.RemoveEmptyEntries).Where(l => !l.StartsWith('#')).ToList();
|
|
Assert.Equal(6, segments.Count);
|
|
Assert.All(segments, s => Assert.StartsWith("http://proxy/seg?u=https%3A%2F%2Fcdn-vod-drm2.floatplane.com", s));
|
|
}
|
|
|
|
[Fact]
|
|
public void Master_playlist_variants_are_mapped_through_playlist_mapper()
|
|
{
|
|
const string master = "#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=1800000,RESOLUTION=1920x1080\n1080.mp4/chunk.m3u8?token=A\n#EXT-X-STREAM-INF:BANDWIDTH=900000,RESOLUTION=1280x720\nhttps://other.cdn/720/chunk.m3u8?token=B\n";
|
|
var masterUrl = new Uri("https://cdn-vod-drm2.floatplane.com/Videos/dcm2jUIg1J/master.m3u8?token=M");
|
|
|
|
var rewritten = HlsRewriter.Rewrite(
|
|
master,
|
|
masterUrl,
|
|
key => "KEY",
|
|
playlistUri => "http://proxy/pl?u=" + playlistUri);
|
|
|
|
var lines = rewritten.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
|
Assert.Equal("http://proxy/pl?u=https://cdn-vod-drm2.floatplane.com/Videos/dcm2jUIg1J/1080.mp4/chunk.m3u8?token=A", lines[2]);
|
|
Assert.Equal("http://proxy/pl?u=https://other.cdn/720/chunk.m3u8?token=B", lines[4]);
|
|
Assert.Equal("#EXT-X-STREAM-INF:BANDWIDTH=1800000,RESOLUTION=1920x1080", lines[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Master_playlist_without_mapper_keeps_absolute_urls()
|
|
{
|
|
const string master = "#EXTM3U\n#EXT-X-STREAM-INF:BANDWIDTH=1\nsub/chunk.m3u8\n";
|
|
|
|
var rewritten = HlsRewriter.Rewrite(master, new Uri("https://cdn.example/dir/master.m3u8"), key => "KEY");
|
|
|
|
Assert.Contains("https://cdn.example/dir/sub/chunk.m3u8", rewritten);
|
|
}
|
|
|
|
[Fact]
|
|
public void Absolute_resolves_relative_and_keeps_absolute()
|
|
{
|
|
Assert.Equal("https://cdn.example/a/b.ts", HlsRewriter.Absolute(new Uri("https://cdn.example/a/p.m3u8?x=1"), "b.ts"));
|
|
Assert.Equal("https://cdn.example/b.ts", HlsRewriter.Absolute(new Uri("https://cdn.example/a/p.m3u8"), "/b.ts"));
|
|
Assert.Equal("https://other/b.ts", HlsRewriter.Absolute(new Uri("https://cdn.example/a/p.m3u8"), "https://other/b.ts"));
|
|
}
|
|
}
|