import 'package:flutter/widgets.dart'; import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart'; import 'package:fladder/models/item_base_model.dart'; import 'package:fladder/models/items/chapters_model.dart'; import 'package:fladder/models/items/media_segments_model.dart'; import 'package:fladder/models/items/media_streams_model.dart'; import 'package:fladder/models/items/trick_play_model.dart'; import 'package:fladder/models/playback/playback_model.dart'; import 'package:fladder/providers/api_provider.dart'; import 'package:fladder/providers/video_player_provider.dart'; import 'package:fladder/util/duration_extensions.dart'; import 'package:fladder/util/list_extensions.dart'; import 'package:fladder/wrappers/media_control_wrapper.dart'; class TranscodePlaybackModel implements PlaybackModel { TranscodePlaybackModel({ required this.item, required this.media, required this.playbackInfo, this.mediaStreams, this.mediaSegments, this.chapters, this.trickPlay, this.queue = const [], }); @override final ItemBaseModel item; @override final Media? media; @override final PlaybackInfoResponse playbackInfo; @override final MediaStreamsModel? mediaStreams; @override final MediaSegmentsModel? mediaSegments; @override final List? chapters; @override final TrickPlayModel? trickPlay; @override ItemBaseModel? get nextVideo => queue.nextOrNull(item); @override ItemBaseModel? get previousVideo => queue.previousOrNull(item); @override Future? startDuration() async => item.userData.playBackPosition; @override List get subStreams => [SubStreamModel.no(), ...mediaStreams?.subStreams ?? []]; List get itemsInQueue => queue.mapIndexed((index, element) => QueueItem(id: element.id, playlistItemId: "playlistItem$index")).toList(); @override Future setSubtitle(SubStreamModel? model, MediaControlsWrapper player) async { final newIndex = await player.setSubtitleTrack(model, this); return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultSubStreamIndex: newIndex)); } @override List get audioStreams => [AudioStreamModel.no(), ...mediaStreams?.audioStreams ?? []]; @override Future? setAudio(AudioStreamModel? model, MediaControlsWrapper player) async { final newIndex = await player.setAudioTrack(model, this); return copyWith(mediaStreams: () => mediaStreams?.copyWith(defaultAudioStreamIndex: newIndex)); } @override Future playbackStarted(Duration position, Ref ref) async { await ref.read(jellyApiProvider).sessionsPlayingPost( body: PlaybackStartInfo( canSeek: true, itemId: item.id, mediaSourceId: item.id, playSessionId: playbackInfo.playSessionId, sessionId: playbackInfo.playSessionId, subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex, audioStreamIndex: item.streamModel?.defaultAudioStreamIndex, volumeLevel: 100, playbackStartTimeTicks: position.toRuntimeTicks, playMethod: PlayMethod.transcode, isMuted: false, isPaused: false, repeatMode: RepeatMode.repeatall, ), ); return null; } @override Future playbackStopped(Duration position, Duration? totalDuration, Ref ref) async { ref.read(playBackModel.notifier).update((state) => null); await ref.read(jellyApiProvider).sessionsPlayingStoppedPost( body: PlaybackStopInfo( itemId: item.id, mediaSourceId: item.id, playSessionId: playbackInfo.playSessionId, positionTicks: position.toRuntimeTicks, failed: false, ), totalDuration: totalDuration, ); return null; } @override Future updatePlaybackPosition(Duration position, bool isPlaying, Ref ref) async { final api = ref.read(jellyApiProvider); await api.sessionsPlayingProgressPost( body: PlaybackProgressInfo( canSeek: true, itemId: item.id, mediaSourceId: item.id, playSessionId: playbackInfo.playSessionId, sessionId: playbackInfo.playSessionId, subtitleStreamIndex: item.streamModel?.defaultSubStreamIndex, audioStreamIndex: item.streamModel?.defaultAudioStreamIndex, volumeLevel: 100, positionTicks: position.toRuntimeTicks, playMethod: PlayMethod.transcode, isPaused: !isPlaying, isMuted: false, repeatMode: RepeatMode.repeatall, ), ); return this; } @override String toString() => 'TranscodePlaybackModel(item: $item, playbackInfo: $playbackInfo)'; @override final List queue; @override TranscodePlaybackModel copyWith({ ItemBaseModel? item, ValueGetter? media, ValueGetter? lastPosition, PlaybackInfoResponse? playbackInfo, ValueGetter? mediaStreams, ValueGetter? mediaSegments, ValueGetter?>? chapters, ValueGetter? trickPlay, List? queue, }) { return TranscodePlaybackModel( item: item ?? this.item, media: media != null ? media() : this.media, playbackInfo: playbackInfo ?? this.playbackInfo, mediaStreams: mediaStreams != null ? mediaStreams() : this.mediaStreams, mediaSegments: mediaSegments != null ? mediaSegments() : this.mediaSegments, chapters: chapters != null ? chapters() : this.chapters, trickPlay: trickPlay != null ? trickPlay() : this.trickPlay, queue: queue ?? this.queue, ); } }