mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
feat: Added playbackinformation to native player ui
This commit is contained in:
parent
37496f87e3
commit
25304d0a5b
14 changed files with 266 additions and 50 deletions
|
|
@ -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<Any?>): MediaInfo {
|
||||
val playbackType = pigeonVar_list[0] as PlaybackType
|
||||
val videoInformation = pigeonVar_list[1] as String
|
||||
return MediaInfo(playbackType, videoInformation)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
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<MediaSegment>,
|
||||
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<MediaSegment>
|
||||
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<Any?> {
|
||||
|
|
@ -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<Any?>)?.let {
|
||||
SimpleItemModel.fromList(it)
|
||||
return (readValue(buffer) as Long?)?.let {
|
||||
MediaSegmentType.ofRaw(it.toInt())
|
||||
}
|
||||
}
|
||||
131.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
PlayableData.fromList(it)
|
||||
SimpleItemModel.fromList(it)
|
||||
}
|
||||
}
|
||||
132.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
MediaSegment.fromList(it)
|
||||
MediaInfo.fromList(it)
|
||||
}
|
||||
}
|
||||
133.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
AudioTrack.fromList(it)
|
||||
PlayableData.fromList(it)
|
||||
}
|
||||
}
|
||||
134.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
SubtitleTrack.fromList(it)
|
||||
MediaSegment.fromList(it)
|
||||
}
|
||||
}
|
||||
135.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
Chapter.fromList(it)
|
||||
AudioTrack.fromList(it)
|
||||
}
|
||||
}
|
||||
136.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
TrickPlayModel.fromList(it)
|
||||
SubtitleTrack.fromList(it)
|
||||
}
|
||||
}
|
||||
137.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
StartResult.fromList(it)
|
||||
Chapter.fromList(it)
|
||||
}
|
||||
}
|
||||
138.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
TrickPlayModel.fromList(it)
|
||||
}
|
||||
}
|
||||
139.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
StartResult.fromList(it)
|
||||
}
|
||||
}
|
||||
140.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -476,11 +476,15 @@ internal fun RowScope.RightButtons(
|
|||
showAudioDialog: MutableState<Boolean>,
|
||||
showSubDialog: MutableState<Boolean>
|
||||
) {
|
||||
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
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)
|
||||
|
|
@ -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 = "")
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Text(
|
||||
'${item.streamModel?.displayProfile?.value} ${item.streamModel?.resolution?.value}',
|
||||
item.streamModel?.mediaInfoTag ?? "",
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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<Object?> _toList() {
|
||||
return <Object?>[
|
||||
playbackType,
|
||||
videoInformation,
|
||||
];
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList(); }
|
||||
|
||||
static MediaInfo decode(Object result) {
|
||||
result as List<Object?>;
|
||||
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<Object?> _toList() {
|
||||
|
|
@ -170,6 +225,7 @@ class PlayableData {
|
|||
segments,
|
||||
previousVideo,
|
||||
nextVideo,
|
||||
mediaInfo,
|
||||
url,
|
||||
];
|
||||
}
|
||||
|
|
@ -192,7 +248,8 @@ class PlayableData {
|
|||
segments: (result[9] as List<Object?>?)!.cast<MediaSegment>(),
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<MediaSegment> 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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue