From a3ccb6009ce87e786ec558dd539ceb29ef4a236e Mon Sep 17 00:00:00 2001 From: PartyDonut Date: Sun, 26 Oct 2025 18:07:26 +0100 Subject: [PATCH] fix: Incorrect progress updating for native player --- .../fladder/api/VideoPlayerHelper.g.kt | 31 +++++++++++-------- .../messengers/VideoPlayerImplementation.kt | 26 ++++++++++------ lib/src/video_player_helper.g.dart | 9 ++++-- lib/wrappers/media_control_wrapper.dart | 2 +- lib/wrappers/players/native_player.dart | 17 ++++++---- pigeons/video_player.dart | 4 ++- 6 files changed, 57 insertions(+), 32 deletions(-) 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 16cb934..7a9f2ee 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 @@ -714,8 +714,8 @@ interface NativeVideoActivity { } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface VideoPlayerApi { - fun sendPlayableModel(playableData: PlayableData): Boolean - fun open(url: String, play: Boolean) + fun sendPlayableModel(playableData: PlayableData, callback: (Result) -> Unit) + fun open(url: String, play: Boolean, callback: (Result) -> Unit) fun setLooping(looping: Boolean) /** Sets the volume, with 0.0 being muted and 1.0 being full volume. */ fun setVolume(volume: Double) @@ -743,12 +743,15 @@ interface VideoPlayerApi { channel.setMessageHandler { message, reply -> val args = message as List val playableDataArg = args[0] as PlayableData - val wrapped: List = try { - listOf(api.sendPlayableModel(playableDataArg)) - } catch (exception: Throwable) { - VideoPlayerHelperPigeonUtils.wrapError(exception) + api.sendPlayableModel(playableDataArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(VideoPlayerHelperPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(VideoPlayerHelperPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -761,13 +764,15 @@ interface VideoPlayerApi { val args = message as List val urlArg = args[0] as String val playArg = args[1] as Boolean - val wrapped: List = try { - api.open(urlArg, playArg) - listOf(null) - } catch (exception: Throwable) { - VideoPlayerHelperPigeonUtils.wrapError(exception) + api.open(urlArg, playArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(VideoPlayerHelperPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(VideoPlayerHelperPigeonUtils.wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) diff --git a/android/app/src/main/kotlin/nl/jknaapen/fladder/messengers/VideoPlayerImplementation.kt b/android/app/src/main/kotlin/nl/jknaapen/fladder/messengers/VideoPlayerImplementation.kt index dfa2703..228803c 100644 --- a/android/app/src/main/kotlin/nl/jknaapen/fladder/messengers/VideoPlayerImplementation.kt +++ b/android/app/src/main/kotlin/nl/jknaapen/fladder/messengers/VideoPlayerImplementation.kt @@ -26,18 +26,23 @@ class VideoPlayerImplementation( var player: ExoPlayer? = null val playbackData: MutableStateFlow = MutableStateFlow(null) - override fun sendPlayableModel(playableData: PlayableData): Boolean { + override fun sendPlayableModel( + playableData: PlayableData, + callback: (Result) -> Unit + ) { try { println("Send playable data") playbackData.value = playableData - return true + callback(Result.success(true)) + return } catch (e: Exception) { println("Error loading data $e") - return false + callback(Result.success(false)) + return } } - override fun open(url: String, play: Boolean) { + override fun open(url: String, play: Boolean, callback: (Result) -> Unit) { Handler(Looper.getMainLooper()).postDelayed(delayInMillis = 1.seconds.inWholeMilliseconds) { try { playbackData.value?.let { @@ -67,17 +72,21 @@ class VideoPlayerImplementation( player?.prepare() val startPosition = playbackData.value?.startPosition ?: 0L - if (startPosition > 0) { + if (startPosition > 0L) { player?.seekTo(startPosition) - - player?.playWhenReady = play } + player?.playWhenReady = play + callback(Result.success(true)) + return@postDelayed } catch (e: Exception) { println("Error playing video $e") + callback(Result.success(false)) + return@postDelayed } } } + override fun setLooping(looping: Boolean) { player?.repeatMode = if (looping) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF } @@ -110,10 +119,9 @@ class VideoPlayerImplementation( player = exoPlayer //exoPlayer initializes after the playbackData is set for the first load playbackData.value?.let { - sendPlayableModel(it) VideoPlayerObject.setAudioTrackIndex(it.defaultAudioTrack.toInt(), true) VideoPlayerObject.setSubtitleTrackIndex(it.defaultSubtrack.toInt(), true) - open(it.url, true) + open(it.url, true, callback = {}) } } } diff --git a/lib/src/video_player_helper.g.dart b/lib/src/video_player_helper.g.dart index 6cc9f68..243e686 100644 --- a/lib/src/video_player_helper.g.dart +++ b/lib/src/video_player_helper.g.dart @@ -911,7 +911,7 @@ class VideoPlayerApi { } } - Future open(String url, bool play) async { + Future open(String url, bool play) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.nl_jknaapen_fladder.video.VideoPlayerApi.open$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -929,8 +929,13 @@ class VideoPlayerApi { message: pigeonVar_replyList[1] as String?, details: pigeonVar_replyList[2], ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (pigeonVar_replyList[0] as bool?)!; } } diff --git a/lib/wrappers/media_control_wrapper.dart b/lib/wrappers/media_control_wrapper.dart index 94b18ee..7c7a687 100644 --- a/lib/wrappers/media_control_wrapper.dart +++ b/lib/wrappers/media_control_wrapper.dart @@ -264,7 +264,7 @@ class MediaControlsWrapper extends BaseAudioHandler implements VideoPlayerContro final totalDuration = _player?.lastState.duration; // //Small delay so we don't post right after playback/progress update - await Future.delayed(const Duration(seconds: 2)); + await Future.delayed(const Duration(seconds: 1)); await playbackModel.playbackStopped(position ?? Duration.zero, totalDuration, ref); ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(position: Duration.zero)); diff --git a/lib/wrappers/players/native_player.dart b/lib/wrappers/players/native_player.dart index d995db9..e8f1101 100644 --- a/lib/wrappers/players/native_player.dart +++ b/lib/wrappers/players/native_player.dart @@ -15,11 +15,12 @@ import 'package:fladder/wrappers/players/player_states.dart'; class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { final player = VideoPlayerApi(); + final activity = NativeVideoActivity(); @override Future dispose() async { nativeActivityStarted = false; - return NativeVideoActivity().disposeActivity(); + return activity.disposeActivity(); } @override @@ -36,7 +37,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { @override Future open(BuildContext newContext) async { nativeActivityStarted = true; - return NativeVideoActivity().launchActivity(); + return activity.launchActivity(); } @override @@ -49,7 +50,11 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { @override Future playOrPause() async { - return; + if (lastState.playing) { + return player.pause(); + } else { + return player.play(); + } } @override @@ -59,7 +64,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { @override Future setAudioTrack(AudioStreamModel? model, PlaybackModel playbackModel) async { - return 0; + return model?.index ?? 0; } @override @@ -67,7 +72,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { @override Future setSubtitleTrack(SubStreamModel? model, PlaybackModel playbackModel) async { - return 0; + return model?.index ?? 0; } @override @@ -177,6 +182,6 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { ), url: model.media?.url ?? "", ); - player.sendPlayableModel(playableData); + await player.sendPlayableModel(playableData); } } diff --git a/pigeons/video_player.dart b/pigeons/video_player.dart index 4064fb1..40795b1 100644 --- a/pigeons/video_player.dart +++ b/pigeons/video_player.dart @@ -184,9 +184,11 @@ abstract class NativeVideoActivity { @HostApi() abstract class VideoPlayerApi { + @async bool sendPlayableModel(PlayableData playableData); - void open(String url, bool play); + @async + bool open(String url, bool play); void setLooping(bool looping);