Skip to content

Commit aab2c2d

Browse files
committed
fix: playback and reporting for merged/multi-version media
1 parent f057b1d commit aab2c2d

6 files changed

Lines changed: 43 additions & 24 deletions

File tree

lib/models/information_model.dart

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// ignore_for_file: constant_identifier_names
2-
1+
import 'package:collection/collection.dart';
32
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
43
import 'package:fladder/util/bitrate_formatting.dart';
54
import 'package:fladder/util/size_formatting.dart';
@@ -16,17 +15,24 @@ class InformationModel {
1615
required this.subStreams,
1716
});
1817

19-
static InformationModel? fromResponse(BaseItemDto? item) {
18+
static InformationModel? fromResponse(BaseItemDto? item, {String? selectedMediaSourceId}) {
2019
if (item == null) return null;
21-
var videoStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.video).toList() ?? [];
22-
var audioStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.audio).toList() ?? [];
23-
var subStreams = item.mediaStreams?.where((element) => element.type == MediaStreamType.subtitle).toList() ?? [];
20+
21+
final source = (selectedMediaSourceId != null)
22+
? (item.mediaSources?.firstWhereOrNull((e) => e.id == selectedMediaSourceId) ?? item.mediaSources?.firstOrNull)
23+
: item.mediaSources?.firstOrNull;
24+
25+
final streams = source?.mediaStreams ?? item.mediaStreams ?? [];
26+
27+
var videoStreams = streams.where((element) => element.type == MediaStreamType.video).toList();
28+
var audioStreams = streams.where((element) => element.type == MediaStreamType.audio).toList();
29+
var subStreams = streams.where((element) => element.type == MediaStreamType.subtitle).toList();
2430
return InformationModel(
2531
baseInformation: {
2632
"Title": item.name,
27-
"Container": item.container,
28-
"Path": item.path,
29-
"Size": item.mediaSources?.firstOrNull?.size.byteFormat,
33+
"Container": source?.container ?? item.container,
34+
"Path": source?.path ?? item.path,
35+
"Size": source?.size.byteFormat,
3036
},
3137
videoStreams: videoStreams
3238
.map(

lib/models/playback/direct_playback_model.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class DirectPlaybackModel extends PlaybackModel {
6262
body: PlaybackStartInfo(
6363
canSeek: true,
6464
itemId: item.id,
65-
mediaSourceId: item.id,
65+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
6666
playSessionId: playbackInfo?.playSessionId,
6767
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
6868
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
@@ -84,7 +84,7 @@ class DirectPlaybackModel extends PlaybackModel {
8484
await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
8585
body: PlaybackStopInfo(
8686
itemId: item.id,
87-
mediaSourceId: item.id,
87+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
8888
playSessionId: playbackInfo?.playSessionId,
8989
positionTicks: position.toRuntimeTicks,
9090
),
@@ -100,7 +100,7 @@ class DirectPlaybackModel extends PlaybackModel {
100100
body: PlaybackProgressInfo(
101101
canSeek: true,
102102
itemId: item.id,
103-
mediaSourceId: item.id,
103+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
104104
playSessionId: playbackInfo?.playSessionId,
105105
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
106106
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,

lib/models/playback/playback_model.dart

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:fladder/models/items/channel_model.dart';
1313
import 'package:fladder/models/items/chapters_model.dart';
1414
import 'package:fladder/models/items/episode_model.dart';
1515
import 'package:fladder/models/items/item_shared_models.dart';
16+
import 'package:fladder/models/items/item_stream_model.dart';
1617
import 'package:fladder/models/items/media_segments_model.dart';
1718
import 'package:fladder/models/items/media_streams_model.dart';
1819
import 'package:fladder/models/items/season_model.dart';
@@ -344,14 +345,17 @@ class PlaybackModelHelper {
344345
oldModel?.mediaStreams?.currentSubStream,
345346
newStreamModel?.subStreams,
346347
newStreamModel?.defaultSubStreamIndex);
348+
final actualItem = (item is ItemStreamModel && newStreamModel != null)
349+
? item.copyWith(mediaStreams: newStreamModel)
350+
: item;
347351

348352
//Native player does not allow for loading external subtitles with transcoding
349353
final isNativePlayer =
350354
ref.read(videoPlayerSettingsProvider.select((value) => value.wantedPlayer == PlayerOptions.nativePlayer));
351355
final isExternalSub = newStreamModel?.currentSubStream?.isExternal == true;
352356

353357
final Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
354-
itemId: item.id,
358+
itemId: actualItem.id,
355359
body: PlaybackInfoDto(
356360
startTimeTicks: startPosition?.toRuntimeTicks,
357361
audioStreamIndex: audioStreamIndex,
@@ -374,7 +378,11 @@ class PlaybackModelHelper {
374378
return null;
375379
}
376380

377-
final mediaSource = playbackInfo.mediaSources?[newStreamModel?.versionStreamIndex ?? 0];
381+
final requestedMediaSourceId = newStreamModel?.currentVersionStream?.id;
382+
final mediaSource = (requestedMediaSourceId != null)
383+
? (playbackInfo.mediaSources?.firstWhereOrNull((element) => element.id == requestedMediaSourceId) ??
384+
playbackInfo.mediaSources?.firstOrNull)
385+
: playbackInfo.mediaSources?[newStreamModel?.versionStreamIndex ?? 0];
378386

379387
if (mediaSource == null) {
380388
return null;
@@ -396,12 +404,13 @@ class PlaybackModelHelper {
396404

397405
if (type == PlaybackType.tv && mediaPath != null) {
398406
final tvModel = TvPlaybackModel(
399-
channel: item as ChannelModel,
407+
channel: actualItem as ChannelModel,
400408
isNativePlayerBackend: isNativePlayer,
401-
item: item,
409+
item: actualItem,
402410
queue: libraryQueue,
403411
playbackInfo: playbackInfo,
404412
media: Media(url: mediaPath),
413+
mediaStreams: mediaStreamsWithUrls,
405414
);
406415
tvModel.startTracking(ref);
407416
return tvModel;
@@ -429,7 +438,7 @@ class PlaybackModelHelper {
429438
);
430439

431440
return DirectPlaybackModel(
432-
item: item,
441+
item: actualItem,
433442
queue: libraryQueue,
434443
mediaSegments: mediaSegments?.body,
435444
chapters: chapters,
@@ -441,7 +450,7 @@ class PlaybackModelHelper {
441450
);
442451
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
443452
return TranscodePlaybackModel(
444-
item: item,
453+
item: actualItem,
445454
queue: libraryQueue,
446455
mediaSegments: mediaSegments?.body,
447456
chapters: chapters,

lib/models/playback/transcode_playback_model.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class TranscodePlaybackModel extends PlaybackModel {
6060
body: PlaybackStartInfo(
6161
canSeek: true,
6262
itemId: item.id,
63-
mediaSourceId: item.id,
63+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
6464
playSessionId: playbackInfo?.playSessionId,
6565
sessionId: playbackInfo?.playSessionId,
6666
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
@@ -83,7 +83,7 @@ class TranscodePlaybackModel extends PlaybackModel {
8383
await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
8484
body: PlaybackStopInfo(
8585
itemId: item.id,
86-
mediaSourceId: item.id,
86+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
8787
playSessionId: playbackInfo?.playSessionId,
8888
positionTicks: position.toRuntimeTicks,
8989
),
@@ -99,7 +99,7 @@ class TranscodePlaybackModel extends PlaybackModel {
9999
body: PlaybackProgressInfo(
100100
canSeek: true,
101101
itemId: item.id,
102-
mediaSourceId: item.id,
102+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
103103
playSessionId: playbackInfo?.playSessionId,
104104
sessionId: playbackInfo?.playSessionId,
105105
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,

lib/models/playback/tv_playback_model.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class TvPlaybackModel extends PlaybackModel {
5151
this.isNativePlayerBackend = false,
5252
super.media,
5353
super.queue,
54+
super.mediaStreams,
5455
});
5556

5657
void startTracking(Ref ref) {
@@ -261,7 +262,7 @@ class TvPlaybackModel extends PlaybackModel {
261262
body: PlaybackStartInfo(
262263
canSeek: true,
263264
itemId: item.id,
264-
mediaSourceId: item.id,
265+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
265266
playSessionId: playbackInfo?.playSessionId,
266267
subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex,
267268
audioStreamIndex: item.streamModel?.defaultAudioStreamIndex,
@@ -283,7 +284,7 @@ class TvPlaybackModel extends PlaybackModel {
283284
await ref.read(jellyApiProvider).sessionsPlayingStoppedPost(
284285
body: PlaybackStopInfo(
285286
itemId: item.id,
286-
mediaSourceId: item.id,
287+
mediaSourceId: mediaStreams?.currentVersionStream?.id ?? item.id,
287288
playSessionId: playbackInfo?.playSessionId,
288289
),
289290
);
@@ -334,5 +335,6 @@ class TvPlaybackModel extends PlaybackModel {
334335
duration: duration ?? this.duration,
335336
media: media ?? this.media,
336337
queue: queue ?? this.queue,
338+
mediaStreams: mediaStreams,
337339
);
338340
}

lib/providers/items/information_provider.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ class InformationNotifier extends StateNotifier<InformationProviderModel> {
4040
Future<Response> getItemInformation(ItemBaseModel item) async {
4141
state = state.copyWith(loading: true);
4242
final response = await api.usersUserIdItemsItemIdGetBaseItem(itemId: item.id);
43+
final selectedMediaSourceId = item.streamModel?.currentVersionStream?.id;
4344
await Future.delayed(const Duration(milliseconds: 250));
44-
state = state.copyWith(loading: false, model: InformationModel.fromResponse(response.body));
45+
state = state.copyWith(
46+
loading: false, model: InformationModel.fromResponse(response.body, selectedMediaSourceId: selectedMediaSourceId));
4547
return response;
4648
}
4749
}

0 commit comments

Comments
 (0)