fix(jellyfin): avoid side-loading negotiated subtitles

This commit is contained in:
edde746
2026-05-25 23:39:55 +02:00
parent cec8993b80
commit 8e53232961
9 changed files with 132 additions and 14 deletions
@@ -177,9 +177,20 @@ void main() {
'Language': 'eng',
'DisplayLanguage': 'English',
'DisplayTitle': 'English - SRT',
'IsExternal': true,
'DeliveryMethod': 'External',
'DeliveryUrl': '/Videos/item-1/src-2/Subtitles/3/Stream.srt',
},
{
'Index': 4,
'Type': 'Subtitle',
'Codec': 'srt',
'Language': 'fra',
'DisplayLanguage': 'French',
'DisplayTitle': 'French - SRT',
'DeliveryMethod': 'External',
'DeliveryUrl': '/Videos/item-1/src-2/Subtitles/4/Stream.srt',
},
],
},
],
@@ -332,6 +343,8 @@ void main() {
expect(uri.queryParameters['api_key'], 'tok-abc');
expect(uri.queryParameters.containsKey('StartTimeTicks'), isFalse);
expect(result.mediaInfo!.subtitleTracks, hasLength(1));
expect(result.mediaInfo!.subtitleTracks.single.isExternalFile, isFalse);
expect(result.mediaInfo!.subtitleTracks.single.usesExternalDelivery, isTrue);
expect(result.externalSubtitles, hasLength(1));
expect(result.externalSubtitles.single.title, 'English');
expect(result.externalSubtitles.single.language, 'eng');
@@ -444,6 +457,7 @@ void main() {
'Codec': 'srt',
'Language': 'eng',
'DisplayTitle': 'English - SRT',
'IsExternal': true,
'DeliveryMethod': 'External',
'DeliveryUrl': '/Videos/item-1/src-1/Subtitles/3/Stream.srt',
},
@@ -495,6 +509,85 @@ void main() {
expect(subtitleUri.queryParameters['api_key'], 'tok-abc');
});
test('getPlaybackInitialization skips negotiated subtitle delivery for original playback', () async {
final scoped = JellyfinClient.forTesting(
connection: _conn(),
httpClient: MockClient((request) async {
if (request.url.path == '/Users/user-1/Items/item-1') {
return http.Response(
jsonEncode({
'Id': 'item-1',
'Type': 'Movie',
'Name': 'Movie',
'MediaSources': [
{
'Id': 'src-1',
'Container': 'mkv',
'MediaStreams': [
{'Index': 0, 'Type': 'Video'},
],
},
],
}),
200,
headers: {'content-type': 'application/json'},
);
}
if (request.url.path == '/Items/item-1/PlaybackInfo') {
return http.Response(
jsonEncode({
'PlaySessionId': 'play-session-direct',
'MediaSources': [
{
'Id': 'src-1',
'Container': 'mkv',
'DirectStreamUrl': '/Videos/item-1/stream?MediaSourceId=src-1&PlaySessionId=play-session-direct',
'MediaStreams': [
{'Index': 0, 'Type': 'Video'},
{
'Index': 3,
'Type': 'Subtitle',
'Codec': 'srt',
'Language': 'eng',
'DisplayTitle': 'English - SRT',
'DeliveryMethod': 'External',
'DeliveryUrl': '/Videos/item-1/src-1/Subtitles/3/Stream.srt',
},
{
'Index': 4,
'Type': 'Subtitle',
'Codec': 'srt',
'Language': 'fra',
'DisplayTitle': 'French - SRT',
'DeliveryMethod': 'External',
'DeliveryUrl': '/Videos/item-1/src-1/Subtitles/4/Stream.srt',
},
],
},
],
}),
200,
headers: {'content-type': 'application/json'},
);
}
return http.Response('{}', 404);
}),
);
addTearDown(scoped.close);
final result = await scoped.getPlaybackInitialization(
PlaybackInitializationOptions(
metadata: MediaItem(id: 'item-1', backend: MediaBackend.jellyfin, kind: MediaKind.movie, serverId: 'srv-1'),
selectedMediaIndex: 0,
),
);
expect(result.playMethod, 'DirectStream');
expect(result.mediaInfo!.subtitleTracks, hasLength(2));
expect(result.mediaInfo!.subtitleTracks.every((track) => track.usesExternalDelivery), isTrue);
expect(result.externalSubtitles, isEmpty);
});
test('getPlaybackInitialization ignores TranscodingUrl for original playback static fallback', () async {
final requests = <Uri>[];
final scoped = JellyfinClient.forTesting(
@@ -214,6 +214,8 @@ void main() {
final sub = info.subtitleTracks.single;
expect(sub.key, '/Videos/item-1/src-1/Subtitles/2/Stream.srt');
expect(sub.isExternal, isTrue);
expect(sub.isExternalFile, isFalse);
expect(sub.usesExternalDelivery, isTrue);
});
test('preserves external Jellyfin audio streams', () {
@@ -247,6 +249,8 @@ void main() {
final sub = info.subtitleTracks.single;
expect(sub.key, isNull);
expect(sub.isExternal, isTrue);
expect(sub.isExternalFile, isTrue);
expect(sub.usesExternalDelivery, isFalse);
});
test('captures mediaSourceId from source Id field', () {