diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/VideoPlayerHelper.g.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/VideoPlayerHelper.g.kt index 0a96969..16cb934 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/api/VideoPlayerHelper.g.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/api/VideoPlayerHelper.g.kt @@ -80,6 +80,18 @@ class FlutterError ( val details: Any? = null ) : Throwable() +enum class PlaybackType(val raw: Int) { + DIRECT(0), + TRANSCODED(1), + OFFLINE(2); + + companion object { + fun ofRaw(raw: Int): PlaybackType? { + return values().firstOrNull { it.raw == raw } + } + } +} + enum class MediaSegmentType(val raw: Int) { COMMERCIAL(0), PREVIEW(1), @@ -137,6 +149,37 @@ data class SimpleItemModel ( override fun hashCode(): Int = toList().hashCode() } +/** Generated class from Pigeon that represents data sent in messages. */ +data class MediaInfo ( + val playbackType: PlaybackType, + val videoInformation: String +) + { + companion object { + fun fromList(pigeonVar_list: List): MediaInfo { + val playbackType = pigeonVar_list[0] as PlaybackType + val videoInformation = pigeonVar_list[1] as String + return MediaInfo(playbackType, videoInformation) + } + } + fun toList(): List { + return listOf( + playbackType, + videoInformation, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is MediaInfo) { + return false + } + if (this === other) { + return true + } + return VideoPlayerHelperPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} + /** Generated class from Pigeon that represents data sent in messages. */ data class PlayableData ( val currentItem: SimpleItemModel, @@ -151,6 +194,7 @@ data class PlayableData ( val segments: List, val previousVideo: SimpleItemModel? = null, val nextVideo: SimpleItemModel? = null, + val mediaInfo: MediaInfo, val url: String ) { @@ -168,8 +212,9 @@ data class PlayableData ( val segments = pigeonVar_list[9] as List val previousVideo = pigeonVar_list[10] as SimpleItemModel? val nextVideo = pigeonVar_list[11] as SimpleItemModel? - val url = pigeonVar_list[12] as String - return PlayableData(currentItem, description, startPosition, defaultAudioTrack, audioTracks, defaultSubtrack, subtitleTracks, trickPlayModel, chapters, segments, previousVideo, nextVideo, url) + val mediaInfo = pigeonVar_list[12] as MediaInfo + val url = pigeonVar_list[13] as String + return PlayableData(currentItem, description, startPosition, defaultAudioTrack, audioTracks, defaultSubtrack, subtitleTracks, trickPlayModel, chapters, segments, previousVideo, nextVideo, mediaInfo, url) } } fun toList(): List { @@ -186,6 +231,7 @@ data class PlayableData ( segments, previousVideo, nextVideo, + mediaInfo, url, ) } @@ -482,50 +528,60 @@ private open class VideoPlayerHelperPigeonCodec : StandardMessageCodec() { return when (type) { 129.toByte() -> { return (readValue(buffer) as Long?)?.let { - MediaSegmentType.ofRaw(it.toInt()) + PlaybackType.ofRaw(it.toInt()) } } 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - SimpleItemModel.fromList(it) + return (readValue(buffer) as Long?)?.let { + MediaSegmentType.ofRaw(it.toInt()) } } 131.toByte() -> { return (readValue(buffer) as? List)?.let { - PlayableData.fromList(it) + SimpleItemModel.fromList(it) } } 132.toByte() -> { return (readValue(buffer) as? List)?.let { - MediaSegment.fromList(it) + MediaInfo.fromList(it) } } 133.toByte() -> { return (readValue(buffer) as? List)?.let { - AudioTrack.fromList(it) + PlayableData.fromList(it) } } 134.toByte() -> { return (readValue(buffer) as? List)?.let { - SubtitleTrack.fromList(it) + MediaSegment.fromList(it) } } 135.toByte() -> { return (readValue(buffer) as? List)?.let { - Chapter.fromList(it) + AudioTrack.fromList(it) } } 136.toByte() -> { return (readValue(buffer) as? List)?.let { - TrickPlayModel.fromList(it) + SubtitleTrack.fromList(it) } } 137.toByte() -> { return (readValue(buffer) as? List)?.let { - StartResult.fromList(it) + Chapter.fromList(it) } } 138.toByte() -> { + return (readValue(buffer) as? List)?.let { + TrickPlayModel.fromList(it) + } + } + 139.toByte() -> { + return (readValue(buffer) as? List)?.let { + StartResult.fromList(it) + } + } + 140.toByte() -> { return (readValue(buffer) as? List)?.let { PlaybackState.fromList(it) } @@ -535,46 +591,54 @@ private open class VideoPlayerHelperPigeonCodec : StandardMessageCodec() { } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is MediaSegmentType -> { + is PlaybackType -> { stream.write(129) writeValue(stream, value.raw) } - is SimpleItemModel -> { + is MediaSegmentType -> { stream.write(130) - writeValue(stream, value.toList()) + writeValue(stream, value.raw) } - is PlayableData -> { + is SimpleItemModel -> { stream.write(131) writeValue(stream, value.toList()) } - is MediaSegment -> { + is MediaInfo -> { stream.write(132) writeValue(stream, value.toList()) } - is AudioTrack -> { + is PlayableData -> { stream.write(133) writeValue(stream, value.toList()) } - is SubtitleTrack -> { + is MediaSegment -> { stream.write(134) writeValue(stream, value.toList()) } - is Chapter -> { + is AudioTrack -> { stream.write(135) writeValue(stream, value.toList()) } - is TrickPlayModel -> { + is SubtitleTrack -> { stream.write(136) writeValue(stream, value.toList()) } - is StartResult -> { + is Chapter -> { stream.write(137) writeValue(stream, value.toList()) } - is PlaybackState -> { + is TrickPlayModel -> { stream.write(138) writeValue(stream, value.toList()) } + is StartResult -> { + stream.write(139) + writeValue(stream, value.toList()) + } + is PlaybackState -> { + stream.write(140) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt index 426a5c7..6d05c4e 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/ProgressBar.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth @@ -22,6 +23,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme @@ -65,6 +67,7 @@ import androidx.compose.ui.util.fastCoerceIn import androidx.media3.exoplayer.ExoPlayer import kotlinx.coroutines.delay import nl.jknaapen.fladder.objects.VideoPlayerObject +import nl.jknaapen.fladder.utility.capitalize import nl.jknaapen.fladder.utility.formatTime import kotlin.math.max import kotlin.math.min @@ -117,7 +120,7 @@ internal fun ProgressBar( trickPlayModel = playbackData?.trickPlayModel ) Row( - horizontalArrangement = Arrangement.spacedBy(16.dp) + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { val progressBarTopLabel = listOf( playableData?.currentItem?.subTitle, @@ -134,6 +137,11 @@ internal fun ProgressBar( ), ) } + + Spacer(modifier = Modifier.weight(1f)) + + Videolabel(playableData?.mediaInfo?.playbackType?.name?.capitalize) + Videolabel(playableData?.mediaInfo?.videoInformation) } Row( horizontalArrangement = Arrangement.spacedBy( @@ -177,7 +185,30 @@ internal fun ProgressBar( ) } } +} +@Composable +private fun Videolabel(value: String?) { + if (value.isNullOrBlank()) return + + Box( + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.surfaceContainer, + shape = RoundedCornerShape(8.dp) + ) + .wrapContentSize() + .padding(horizontal = 6.dp, vertical = 4.dp), + contentAlignment = Alignment.Center + ) { + Text( + value, + style = MaterialTheme.typography.bodySmall.copy( + fontWeight = FontWeight.SemiBold, + ), + color = MaterialTheme.colorScheme.onSurface + ) + } } @Composable diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt index 8342a0a..601085f 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/controls/VideoPlayerControls.kt @@ -476,11 +476,15 @@ internal fun RowScope.RightButtons( showAudioDialog: MutableState, showSubDialog: MutableState ) { + val hasSubtitles by VideoPlayerObject.hasSubtracks.collectAsState(false) + val hasAudioTracks by VideoPlayerObject.hasAudioTracks.collectAsState(false) + Row( modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(12.dp, alignment = Alignment.End) ) { CustomButton( + enabled = hasAudioTracks, onClick = { showAudioDialog.value = true }, @@ -491,6 +495,7 @@ internal fun RowScope.RightButtons( ) } CustomButton( + enabled = hasSubtitles, onClick = { showSubDialog.value = true }, diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt index 8ba6573..ec4f175 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/AudioSelection.kt @@ -31,6 +31,8 @@ fun AudioPicker( val audioTracks by VideoPlayerObject.audioTracks.collectAsState(listOf()) val internalAudioTracks by VideoPlayerObject.exoAudioTracks + if (internalAudioTracks.isEmpty()) return + val focusOffTrack = remember { FocusRequester() } val focusRequesters = remember(internalAudioTracks) { internalAudioTracks.associateWith { FocusRequester() } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt index 728412b..2b64ad6 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/composables/dialogs/SubtitlePicker.kt @@ -32,6 +32,8 @@ fun SubtitlePicker( val subTitles by VideoPlayerObject.subtitleTracks.collectAsState(listOf()) val internalSubTracks by VideoPlayerObject.exoSubTracks + if (internalSubTracks.isEmpty()) return + val focusOffTrack = remember { FocusRequester() } val focusRequesters = remember(internalSubTracks) { diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt index 0ed35ee..e5707fa 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/objects/VideoPlayerObject.kt @@ -72,6 +72,10 @@ object VideoPlayerObject { val subtitleTracks = implementation.playbackData.map { it?.subtitleTracks ?: listOf() } val audioTracks = implementation.playbackData.map { it?.audioTracks ?: listOf() } + val hasSubtracks = subtitleTracks.map { it.isNotEmpty() && exoSubTracks.value.isNotEmpty() } + val hasAudioTracks = audioTracks.map { it.isNotEmpty() && exoAudioTracks.value.isNotEmpty() } + + fun setPlaybackState(state: PlaybackState) { _currentState.value = state videoPlayerListener?.onPlaybackStateChanged( diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/Modifiers.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/Modifiers.kt index cb64fa0..0dd551a 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/Modifiers.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/Modifiers.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged @@ -115,11 +114,9 @@ fun Modifier.visible( alpha = alphaAnimated } .then( - if (!visible) { - //Collapse composable to disable input blocking + if (alphaAnimated == 0f) { Modifier .size(0.dp) - .clipToBounds() } else { Modifier } diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/TestPlaybackData.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/TestPlaybackData.kt index 4ad3ef2..667cc29 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/TestPlaybackData.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/TestPlaybackData.kt @@ -2,7 +2,9 @@ package nl.jknaapen.fladder.utility import AudioTrack import Chapter +import MediaInfo import PlayableData +import PlaybackType import SimpleItemModel import SubtitleTrack import kotlin.time.Duration.Companion.seconds @@ -99,5 +101,9 @@ val testPlaybackData = PlayableData( primaryPoster = "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.ytimg.com%2Fvi%2Faqz-KE-bpKQ%2Fmaxresdefault.jpg&f=1&nofb=1&ipt=4e375598bf8cc78e681ee62de9111dea32b85972ae756e40a1eddac01aa79f80" ), segments = listOf(), + mediaInfo = MediaInfo( + videoInformation = "SDR HD", + playbackType = PlaybackType.DIRECT, + ), url = "https://github.com/ietf-wg-cellar/matroska-test-files/raw/refs/heads/master/test_files/test5.mkv", ) \ No newline at end of file diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/capitalize.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/capitalize.kt new file mode 100644 index 0000000..abba470 --- /dev/null +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/utility/capitalize.kt @@ -0,0 +1,5 @@ +package nl.jknaapen.fladder.utility + +val String.capitalize: String + get() = this.mapIndexed { index, char -> if (index == 0) char.uppercase() else char.lowercase() } + .joinToString(separator = "") \ No newline at end of file diff --git a/lib/models/items/media_streams_model.dart b/lib/models/items/media_streams_model.dart index 547c912..c79ea0a 100644 --- a/lib/models/items/media_streams_model.dart +++ b/lib/models/items/media_streams_model.dart @@ -69,6 +69,8 @@ class MediaStreamsModel { return "${stream.width}x${stream.height}"; } + String? get mediaInfoTag => '${displayProfile?.value} ${resolution?.value}'; + Widget? audioIcon( BuildContext context, Function()? onTap, diff --git a/lib/screens/video_player/video_player_controls.dart b/lib/screens/video_player/video_player_controls.dart index 6b940d0..1ce8316 100644 --- a/lib/screens/video_player/video_player_controls.dart +++ b/lib/screens/video_player/video_player_controls.dart @@ -443,7 +443,7 @@ class _DesktopControlsState extends ConsumerState { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Text( - '${item.streamModel?.displayProfile?.value} ${item.streamModel?.resolution?.value}', + item.streamModel?.mediaInfoTag ?? "", ), ), ), diff --git a/lib/src/video_player_helper.g.dart b/lib/src/video_player_helper.g.dart index b8f9716..6cc9f68 100644 --- a/lib/src/video_player_helper.g.dart +++ b/lib/src/video_player_helper.g.dart @@ -39,6 +39,12 @@ bool _deepEquals(Object? a, Object? b) { } +enum PlaybackType { + direct, + transcoded, + offline, +} + enum MediaSegmentType { commercial, preview, @@ -113,6 +119,52 @@ class SimpleItemModel { ; } +class MediaInfo { + MediaInfo({ + required this.playbackType, + required this.videoInformation, + }); + + PlaybackType playbackType; + + String videoInformation; + + List _toList() { + return [ + playbackType, + videoInformation, + ]; + } + + Object encode() { + return _toList(); } + + static MediaInfo decode(Object result) { + result as List; + return MediaInfo( + playbackType: result[0]! as PlaybackType, + videoInformation: result[1]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! MediaInfo || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + class PlayableData { PlayableData({ required this.currentItem, @@ -127,6 +179,7 @@ class PlayableData { required this.segments, this.previousVideo, this.nextVideo, + required this.mediaInfo, required this.url, }); @@ -154,6 +207,8 @@ class PlayableData { SimpleItemModel? nextVideo; + MediaInfo mediaInfo; + String url; List _toList() { @@ -170,6 +225,7 @@ class PlayableData { segments, previousVideo, nextVideo, + mediaInfo, url, ]; } @@ -192,7 +248,8 @@ class PlayableData { segments: (result[9] as List?)!.cast(), previousVideo: result[10] as SimpleItemModel?, nextVideo: result[11] as SimpleItemModel?, - url: result[12]! as String, + mediaInfo: result[12]! as MediaInfo, + url: result[13]! as String, ); } @@ -644,36 +701,42 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is MediaSegmentType) { + } else if (value is PlaybackType) { buffer.putUint8(129); writeValue(buffer, value.index); - } else if (value is SimpleItemModel) { + } else if (value is MediaSegmentType) { buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is PlayableData) { + writeValue(buffer, value.index); + } else if (value is SimpleItemModel) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is MediaSegment) { + } else if (value is MediaInfo) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is AudioTrack) { + } else if (value is PlayableData) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is SubtitleTrack) { + } else if (value is MediaSegment) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is Chapter) { + } else if (value is AudioTrack) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is TrickPlayModel) { + } else if (value is SubtitleTrack) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is StartResult) { + } else if (value is Chapter) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PlaybackState) { + } else if (value is TrickPlayModel) { buffer.putUint8(138); writeValue(buffer, value.encode()); + } else if (value is StartResult) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is PlaybackState) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -684,24 +747,29 @@ class _PigeonCodec extends StandardMessageCodec { switch (type) { case 129: final int? value = readValue(buffer) as int?; - return value == null ? null : MediaSegmentType.values[value]; + return value == null ? null : PlaybackType.values[value]; case 130: - return SimpleItemModel.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : MediaSegmentType.values[value]; case 131: - return PlayableData.decode(readValue(buffer)!); + return SimpleItemModel.decode(readValue(buffer)!); case 132: - return MediaSegment.decode(readValue(buffer)!); + return MediaInfo.decode(readValue(buffer)!); case 133: - return AudioTrack.decode(readValue(buffer)!); + return PlayableData.decode(readValue(buffer)!); case 134: - return SubtitleTrack.decode(readValue(buffer)!); + return MediaSegment.decode(readValue(buffer)!); case 135: - return Chapter.decode(readValue(buffer)!); + return AudioTrack.decode(readValue(buffer)!); case 136: - return TrickPlayModel.decode(readValue(buffer)!); + return SubtitleTrack.decode(readValue(buffer)!); case 137: - return StartResult.decode(readValue(buffer)!); + return Chapter.decode(readValue(buffer)!); case 138: + return TrickPlayModel.decode(readValue(buffer)!); + case 139: + return StartResult.decode(readValue(buffer)!); + case 140: return PlaybackState.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); diff --git a/lib/wrappers/players/native_player.dart b/lib/wrappers/players/native_player.dart index 4607d8e..cec48da 100644 --- a/lib/wrappers/players/native_player.dart +++ b/lib/wrappers/players/native_player.dart @@ -3,7 +3,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:fladder/models/items/media_streams_model.dart'; +import 'package:fladder/models/playback/direct_playback_model.dart'; +import 'package:fladder/models/playback/offline_playback_model.dart'; import 'package:fladder/models/playback/playback_model.dart'; +import 'package:fladder/models/playback/transcode_playback_model.dart'; import 'package:fladder/models/settings/video_player_settings.dart'; import 'package:fladder/src/video_player_helper.g.dart'; import 'package:fladder/wrappers/players/base_player.dart'; @@ -157,6 +160,15 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { ?.map((e) => Chapter(name: e.name, url: e.imageUrl, time: e.startPosition.inMilliseconds)) .toList() ?? [], + mediaInfo: MediaInfo( + playbackType: switch (model) { + DirectPlaybackModel() => PlaybackType.direct, + OfflinePlaybackModel() => PlaybackType.offline, + TranscodePlaybackModel() => PlaybackType.transcoded, + _ => PlaybackType.direct, + }, + videoInformation: model.item.streamModel?.mediaInfoTag ?? " ", + ), url: model.media?.url ?? "", ); player.sendPlayableModel(playableData); diff --git a/pigeons/video_player.dart b/pigeons/video_player.dart index 835b108..4064fb1 100644 --- a/pigeons/video_player.dart +++ b/pigeons/video_player.dart @@ -27,6 +27,22 @@ class SimpleItemModel { }); } +enum PlaybackType { + direct, + transcoded, + offline, +} + +class MediaInfo { + final PlaybackType playbackType; + final String videoInformation; + + const MediaInfo({ + required this.playbackType, + required this.videoInformation, + }); +} + class PlayableData { final SimpleItemModel currentItem; final String description; @@ -40,6 +56,7 @@ class PlayableData { final List segments; final SimpleItemModel? previousVideo; final SimpleItemModel? nextVideo; + final MediaInfo mediaInfo; final String url; PlayableData({ @@ -55,6 +72,7 @@ class PlayableData { this.segments = const [], this.previousVideo, this.nextVideo, + required this.mediaInfo, required this.url, }); }