@@ -10,6 +10,10 @@ import 'plex_mappers.dart';
|
||||
|
||||
const _streamReader = PlexFileInfoStreamReader();
|
||||
|
||||
void _logMalformedStream(Object error, StackTrace stackTrace, Map<String, dynamic> _) {
|
||||
appLogger.w('Skipping malformed Plex stream metadata', error: error, stackTrace: stackTrace);
|
||||
}
|
||||
|
||||
List<Map> _mapList(Object? raw) {
|
||||
final values = flexibleList(raw);
|
||||
if (values == null || values.isEmpty) return const [];
|
||||
@@ -122,7 +126,7 @@ PlexVideoPlaybackData parsePlexVideoPlaybackDataFromJson(
|
||||
if (partKey != null) {
|
||||
videoUrl = '$baseUrl$partKey'.withPlexToken(token);
|
||||
|
||||
final streams = walkStreams(flexibleList(part['Stream']), _streamReader);
|
||||
final streams = walkStreams(flexibleList(part['Stream']), _streamReader, onMalformed: _logMalformedStream);
|
||||
final chapters = plexChaptersFromCacheJson(metadataJson);
|
||||
|
||||
mediaInfo = MediaSourceInfo(
|
||||
@@ -161,7 +165,7 @@ MediaFileInfo? parsePlexFileInfoFromJson(Map<String, dynamic>? metadataJson) {
|
||||
// One pass over the streams array, capturing both the raw video / audio
|
||||
// map pointers (for fields the parsed track classes don't carry —
|
||||
// colorSpace, bitDepth, …) and the parsed track lists.
|
||||
final parsedTracks = walkStreams(flexibleList(part?['Stream']), _streamReader);
|
||||
final parsedTracks = walkStreams(flexibleList(part?['Stream']), _streamReader, onMalformed: _logMalformedStream);
|
||||
final videoStream = parsedTracks.videoStream;
|
||||
final audioStream = parsedTracks.audioStream;
|
||||
|
||||
|
||||
@@ -333,6 +333,41 @@ void main() {
|
||||
expect(criteria.primaries, 'bt2020');
|
||||
expect(criteria.matrix, 'bt2020nc');
|
||||
});
|
||||
|
||||
test('skips unidentifiable subtitle streams without blocking playback', () {
|
||||
final result = parsePlexVideoPlaybackDataFromJson(
|
||||
{
|
||||
'Media': [
|
||||
{
|
||||
'id': 1,
|
||||
'Part': [
|
||||
{
|
||||
'id': 10,
|
||||
'key': '/library/parts/10/file.mp4',
|
||||
'accessible': 1,
|
||||
'exists': 1,
|
||||
'Stream': [
|
||||
{'streamType': 1, 'id': 100},
|
||||
{'streamType': 2, 'id': 301, 'selected': true},
|
||||
{'streamType': 3, 'languageCode': 'eng'},
|
||||
{'streamType': 3, 'id': 'cc1', 'languageCode': 'eng'},
|
||||
{'streamType': 3, 'id': '401', 'languageCode': 'spa', 'selected': true},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
baseUrl: 'http://plex:32400',
|
||||
token: 'token',
|
||||
);
|
||||
|
||||
expect(result.videoUrl, 'http://plex:32400/library/parts/10/file.mp4?X-Plex-Token=token');
|
||||
expect(result.mediaInfo, isNotNull);
|
||||
expect(result.mediaInfo!.audioTracks.map((track) => track.id), [301]);
|
||||
expect(result.mediaInfo!.subtitleTracks.map((track) => track.id), [401]);
|
||||
expect(result.mediaInfo!.subtitleTracks.single.selected, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('parsePlexFileInfoFromJson', () {
|
||||
@@ -395,5 +430,30 @@ void main() {
|
||||
expect(info?.audioTracks.single.selected, isTrue);
|
||||
expect(info?.subtitleTracks.single.key, '/subtitles/401');
|
||||
});
|
||||
|
||||
test('skips unidentifiable subtitle streams in file info', () {
|
||||
final info = parsePlexFileInfoFromJson({
|
||||
'Media': [
|
||||
{
|
||||
'container': 'mp4',
|
||||
'Part': [
|
||||
{
|
||||
'file': '/media/movie.mp4',
|
||||
'Stream': [
|
||||
{'streamType': 1, 'id': 100},
|
||||
{'streamType': 3, 'id': null, 'languageCode': 'eng'},
|
||||
{'streamType': 3, 'id': 'cea-608', 'languageCode': 'eng'},
|
||||
{'streamType': 3, 'id': 402, 'languageCode': 'spa'},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(info, isNotNull);
|
||||
expect(info!.filePath, '/media/movie.mp4');
|
||||
expect(info.subtitleTracks.map((track) => track.id), [402]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user