From 485de0d7b4ba200108d7dc370825b8e5572c5d30 Mon Sep 17 00:00:00 2001 From: PartyDonut <42371342+PartyDonut@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:17:05 +0100 Subject: [PATCH] chore: Play BigBuckBunny for every fake item (#243) Co-authored-by: PartyDonut --- lib/fake/fake_jellyfin_open_api.dart | 64 ++++++++++++++++++ lib/models/playback/playback_model.dart | 14 ++-- lib/util/bitrate_helper.dart | 67 ++++++++++--------- .../item_base_model/play_item_helpers.dart | 1 + 4 files changed, 110 insertions(+), 36 deletions(-) diff --git a/lib/fake/fake_jellyfin_open_api.dart b/lib/fake/fake_jellyfin_open_api.dart index 5e00665..a40d7e6 100644 --- a/lib/fake/fake_jellyfin_open_api.dart +++ b/lib/fake/fake_jellyfin_open_api.dart @@ -641,6 +641,44 @@ class FakeJellyfinOpenApi extends JellyfinOpenApi { const BaseItemDtoQueryResult(), ); } + + @override + Future> itemsItemIdPlaybackInfoPost({ + required String? itemId, + String? userId, + int? maxStreamingBitrate, + int? startTimeTicks, + int? audioStreamIndex, + int? subtitleStreamIndex, + int? maxAudioChannels, + String? mediaSourceId, + String? liveStreamId, + bool? autoOpenLiveStream, + bool? enableDirectPlay, + bool? enableDirectStream, + bool? enableTranscoding, + bool? allowVideoStreamCopy, + bool? allowAudioStreamCopy, + required PlaybackInfoDto? body, + }) async { + return chopper.Response( + FakeHelper.fakeCorrectResponse, + FakeHelper.bigBuckBunny, + ); + } + + @override + Future> mediaSegmentsItemIdGet({ + required String? itemId, + List? includeSegmentTypes, + }) async { + return chopper.Response( + FakeHelper.fakeCorrectResponse, + const MediaSegmentDtoQueryResult( + items: [], + ), + ); + } } class FakeHelper { @@ -700,4 +738,30 @@ class FakeHelper { accessToken: 'A_TOTALLY_REAL_TOKEN', serverId: "1", ); + + static PlaybackInfoResponse bigBuckBunny = PlaybackInfoResponse.fromJson({ + "MediaSources": [ + { + "Protocol": "File", + "Id": "234sdfsdf234", + "Path": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + "Type": "Default", + "Container": "mkv", + "Size": 513949601, + "Name": "Big Buck Bunny", + "IsRemote": false, + "ETag": "sdfsdfsd", + "RunTimeTicks": 26540060000, + "SupportsTranscoding": false, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "VideoType": "VideoFile", + "MediaAttachments": [], + "Formats": [], + "Bitrate": 1741204, + "HasSegments": true + } + ], + "PlaySessionId": "asdf234qwafsdfsdf" + }); } diff --git a/lib/models/playback/playback_model.dart b/lib/models/playback/playback_model.dart index 673bb69..902afe7 100644 --- a/lib/models/playback/playback_model.dart +++ b/lib/models/playback/playback_model.dart @@ -190,8 +190,8 @@ class PlaybackModelHelper { Map qualityOptions = getVideoQualityOptions( VideoQualitySettings( maxBitRate: ref.read(videoPlayerSettingsProvider.select((value) => value.maxHomeBitrate)), - videoBitRate: firstItemToPlay.streamModel?.videoStreams.first.bitRate ?? 0, - videoCodec: firstItemToPlay.streamModel?.videoStreams.first.codec, + videoBitRate: firstItemToPlay.streamModel?.videoStreams.firstOrNull?.bitRate ?? 0, + videoCodec: firstItemToPlay.streamModel?.videoStreams.firstOrNull?.codec, ), ); @@ -215,9 +215,12 @@ class PlaybackModelHelper { ); PlaybackInfoResponse? playbackInfo = response.body; + if (playbackInfo == null) return null; - final mediaSource = playbackInfo.mediaSources?.first; + final mediaSource = playbackInfo.mediaSources?[streamModel?.versionStreamIndex ?? 0]; + + if (mediaSource == null) return null; final mediaStreamsWithUrls = MediaStreamsModel.fromMediaStreamsList(playbackInfo.mediaSources, ref).copyWith( defaultAudioStreamIndex: streamModel?.defaultAudioStreamIndex, @@ -228,9 +231,7 @@ class PlaybackModelHelper { final trickPlay = (await api.getTrickPlay(item: fullItem.body, ref: ref))?.body; final chapters = fullItem.body?.overview.chapters ?? []; - final mediaPath = isValidVideoUrl(mediaSource?.path ?? ""); - - if (mediaSource == null) return null; + final mediaPath = isValidVideoUrl(mediaSource.path ?? ""); if ((mediaSource.supportsDirectStream ?? false) || (mediaSource.supportsDirectPlay ?? false)) { final Map directOptions = { @@ -271,6 +272,7 @@ class PlaybackModelHelper { playbackInfo: playbackInfo, media: Media(url: "${ref.read(userProvider)?.server ?? ""}${mediaSource.transcodingUrl ?? ""}"), mediaStreams: mediaStreamsWithUrls, + bitRateOptions: qualityOptions, ); } return null; diff --git a/lib/util/bitrate_helper.dart b/lib/util/bitrate_helper.dart index 5e7daef..b78aefc 100644 --- a/lib/util/bitrate_helper.dart +++ b/lib/util/bitrate_helper.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/widgets.dart'; import 'package:collection/collection.dart'; @@ -49,42 +51,47 @@ class VideoQualitySettings { } Map getVideoQualityOptions(VideoQualitySettings options) { - final maxStreamingBitrate = options.maxBitRate; - final videoBitRate = options.videoBitRate; - final videoCodec = options.videoCodec; - double referenceBitRate = videoBitRate.toDouble(); + try { + final maxStreamingBitrate = options.maxBitRate; + final videoBitRate = options.videoBitRate; + final videoCodec = options.videoCodec; + double referenceBitRate = videoBitRate.toDouble(); - final bitRateValues = Bitrate.values.where((value) => value.calculatedBitRate > 0).toSet(); + final bitRateValues = Bitrate.values.where((value) => value.calculatedBitRate > 0).toSet(); - final qualityOptions = {Bitrate.original, Bitrate.auto}; + final qualityOptions = {Bitrate.original, Bitrate.auto}; - if (videoBitRate > 0 && videoBitRate < bitRateValues.first.calculatedBitRate) { - if (videoCodec != null && ['hevc', 'av1', 'vp9'].contains(videoCodec) && referenceBitRate <= 20000000) { - referenceBitRate *= 1.5; + if (videoBitRate > 0 && videoBitRate < bitRateValues.first.calculatedBitRate) { + if (videoCodec != null && ['hevc', 'av1', 'vp9'].contains(videoCodec) && referenceBitRate <= 20000000) { + referenceBitRate *= 1.5; + } + + final sourceOption = bitRateValues.where((value) => value.calculatedBitRate > referenceBitRate).lastOrNull; + + if (sourceOption != null) { + qualityOptions.add(sourceOption); + } } - final sourceOption = bitRateValues.where((value) => value.calculatedBitRate > referenceBitRate).lastOrNull; + qualityOptions + .addAll(bitRateValues.where((value) => videoBitRate <= 0 || value.calculatedBitRate <= referenceBitRate)); - if (sourceOption != null) { - qualityOptions.add(sourceOption); + Bitrate? selectedQualityOption; + if (maxStreamingBitrate != null && maxStreamingBitrate != Bitrate.original) { + selectedQualityOption = qualityOptions + .where((value) => + value.calculatedBitRate > 0 && value.calculatedBitRate <= maxStreamingBitrate.calculatedBitRate) + .firstOrNull; } + + return qualityOptions.toList().asMap().map( + (_, bitrate) => MapEntry( + bitrate, + bitrate == maxStreamingBitrate || bitrate == selectedQualityOption, + ), + ); + } catch (e) { + log(e.toString()); + return {}; } - - qualityOptions - .addAll(bitRateValues.where((value) => videoBitRate <= 0 || value.calculatedBitRate <= referenceBitRate)); - - Bitrate? selectedQualityOption; - if (maxStreamingBitrate != null && maxStreamingBitrate != Bitrate.original) { - selectedQualityOption = qualityOptions - .where( - (value) => value.calculatedBitRate > 0 && value.calculatedBitRate <= maxStreamingBitrate.calculatedBitRate) - .firstOrNull; - } - - return qualityOptions.toList().asMap().map( - (_, bitrate) => MapEntry( - bitrate, - bitrate == maxStreamingBitrate || bitrate == selectedQualityOption, - ), - ); } diff --git a/lib/util/item_base_model/play_item_helpers.dart b/lib/util/item_base_model/play_item_helpers.dart index deb38f9..c152ee5 100644 --- a/lib/util/item_base_model/play_item_helpers.dart +++ b/lib/util/item_base_model/play_item_helpers.dart @@ -1,3 +1,4 @@ + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart';