fix: Incorrect progress updating for native player

This commit is contained in:
PartyDonut 2025-10-26 18:07:26 +01:00
parent fa61ce2e40
commit a3ccb6009c
6 changed files with 57 additions and 32 deletions

View file

@ -714,8 +714,8 @@ interface NativeVideoActivity {
} }
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface VideoPlayerApi { interface VideoPlayerApi {
fun sendPlayableModel(playableData: PlayableData): Boolean fun sendPlayableModel(playableData: PlayableData, callback: (Result<Boolean>) -> Unit)
fun open(url: String, play: Boolean) fun open(url: String, play: Boolean, callback: (Result<Boolean>) -> Unit)
fun setLooping(looping: Boolean) fun setLooping(looping: Boolean)
/** Sets the volume, with 0.0 being muted and 1.0 being full volume. */ /** Sets the volume, with 0.0 being muted and 1.0 being full volume. */
fun setVolume(volume: Double) fun setVolume(volume: Double)
@ -743,12 +743,15 @@ interface VideoPlayerApi {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val playableDataArg = args[0] as PlayableData val playableDataArg = args[0] as PlayableData
val wrapped: List<Any?> = try { api.sendPlayableModel(playableDataArg) { result: Result<Boolean> ->
listOf(api.sendPlayableModel(playableDataArg)) val error = result.exceptionOrNull()
} catch (exception: Throwable) { if (error != null) {
VideoPlayerHelperPigeonUtils.wrapError(exception) reply.reply(VideoPlayerHelperPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(VideoPlayerHelperPigeonUtils.wrapResult(data))
}
} }
reply.reply(wrapped)
} }
} else { } else {
channel.setMessageHandler(null) channel.setMessageHandler(null)
@ -761,13 +764,15 @@ interface VideoPlayerApi {
val args = message as List<Any?> val args = message as List<Any?>
val urlArg = args[0] as String val urlArg = args[0] as String
val playArg = args[1] as Boolean val playArg = args[1] as Boolean
val wrapped: List<Any?> = try { api.open(urlArg, playArg) { result: Result<Boolean> ->
api.open(urlArg, playArg) val error = result.exceptionOrNull()
listOf(null) if (error != null) {
} catch (exception: Throwable) { reply.reply(VideoPlayerHelperPigeonUtils.wrapError(error))
VideoPlayerHelperPigeonUtils.wrapError(exception) } else {
val data = result.getOrNull()
reply.reply(VideoPlayerHelperPigeonUtils.wrapResult(data))
}
} }
reply.reply(wrapped)
} }
} else { } else {
channel.setMessageHandler(null) channel.setMessageHandler(null)

View file

@ -26,18 +26,23 @@ class VideoPlayerImplementation(
var player: ExoPlayer? = null var player: ExoPlayer? = null
val playbackData: MutableStateFlow<PlayableData?> = MutableStateFlow(null) val playbackData: MutableStateFlow<PlayableData?> = MutableStateFlow(null)
override fun sendPlayableModel(playableData: PlayableData): Boolean { override fun sendPlayableModel(
playableData: PlayableData,
callback: (Result<Boolean>) -> Unit
) {
try { try {
println("Send playable data") println("Send playable data")
playbackData.value = playableData playbackData.value = playableData
return true callback(Result.success(true))
return
} catch (e: Exception) { } catch (e: Exception) {
println("Error loading data $e") 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<Boolean>) -> Unit) {
Handler(Looper.getMainLooper()).postDelayed(delayInMillis = 1.seconds.inWholeMilliseconds) { Handler(Looper.getMainLooper()).postDelayed(delayInMillis = 1.seconds.inWholeMilliseconds) {
try { try {
playbackData.value?.let { playbackData.value?.let {
@ -67,17 +72,21 @@ class VideoPlayerImplementation(
player?.prepare() player?.prepare()
val startPosition = playbackData.value?.startPosition ?: 0L val startPosition = playbackData.value?.startPosition ?: 0L
if (startPosition > 0) { if (startPosition > 0L) {
player?.seekTo(startPosition) player?.seekTo(startPosition)
player?.playWhenReady = play
} }
player?.playWhenReady = play
callback(Result.success(true))
return@postDelayed
} catch (e: Exception) { } catch (e: Exception) {
println("Error playing video $e") println("Error playing video $e")
callback(Result.success(false))
return@postDelayed
} }
} }
} }
override fun setLooping(looping: Boolean) { override fun setLooping(looping: Boolean) {
player?.repeatMode = if (looping) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF player?.repeatMode = if (looping) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
} }
@ -110,10 +119,9 @@ class VideoPlayerImplementation(
player = exoPlayer player = exoPlayer
//exoPlayer initializes after the playbackData is set for the first load //exoPlayer initializes after the playbackData is set for the first load
playbackData.value?.let { playbackData.value?.let {
sendPlayableModel(it)
VideoPlayerObject.setAudioTrackIndex(it.defaultAudioTrack.toInt(), true) VideoPlayerObject.setAudioTrackIndex(it.defaultAudioTrack.toInt(), true)
VideoPlayerObject.setSubtitleTrackIndex(it.defaultSubtrack.toInt(), true) VideoPlayerObject.setSubtitleTrackIndex(it.defaultSubtrack.toInt(), true)
open(it.url, true) open(it.url, true, callback = {})
} }
} }
} }

View file

@ -911,7 +911,7 @@ class VideoPlayerApi {
} }
} }
Future<void> open(String url, bool play) async { Future<bool> open(String url, bool play) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.nl_jknaapen_fladder.video.VideoPlayerApi.open$pigeonVar_messageChannelSuffix'; final String pigeonVar_channelName = 'dev.flutter.pigeon.nl_jknaapen_fladder.video.VideoPlayerApi.open$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>( final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName, pigeonVar_channelName,
@ -929,8 +929,13 @@ class VideoPlayerApi {
message: pigeonVar_replyList[1] as String?, message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2], 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 { } else {
return; return (pigeonVar_replyList[0] as bool?)!;
} }
} }

View file

@ -264,7 +264,7 @@ class MediaControlsWrapper extends BaseAudioHandler implements VideoPlayerContro
final totalDuration = _player?.lastState.duration; final totalDuration = _player?.lastState.duration;
// //Small delay so we don't post right after playback/progress update // //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); await playbackModel.playbackStopped(position ?? Duration.zero, totalDuration, ref);
ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(position: Duration.zero)); ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(position: Duration.zero));

View file

@ -15,11 +15,12 @@ import 'package:fladder/wrappers/players/player_states.dart';
class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback { class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
final player = VideoPlayerApi(); final player = VideoPlayerApi();
final activity = NativeVideoActivity();
@override @override
Future<void> dispose() async { Future<void> dispose() async {
nativeActivityStarted = false; nativeActivityStarted = false;
return NativeVideoActivity().disposeActivity(); return activity.disposeActivity();
} }
@override @override
@ -36,7 +37,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
@override @override
Future<StartResult> open(BuildContext newContext) async { Future<StartResult> open(BuildContext newContext) async {
nativeActivityStarted = true; nativeActivityStarted = true;
return NativeVideoActivity().launchActivity(); return activity.launchActivity();
} }
@override @override
@ -49,7 +50,11 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
@override @override
Future<void> playOrPause() async { Future<void> playOrPause() async {
return; if (lastState.playing) {
return player.pause();
} else {
return player.play();
}
} }
@override @override
@ -59,7 +64,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
@override @override
Future<int> setAudioTrack(AudioStreamModel? model, PlaybackModel playbackModel) async { Future<int> setAudioTrack(AudioStreamModel? model, PlaybackModel playbackModel) async {
return 0; return model?.index ?? 0;
} }
@override @override
@ -67,7 +72,7 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
@override @override
Future<int> setSubtitleTrack(SubStreamModel? model, PlaybackModel playbackModel) async { Future<int> setSubtitleTrack(SubStreamModel? model, PlaybackModel playbackModel) async {
return 0; return model?.index ?? 0;
} }
@override @override
@ -177,6 +182,6 @@ class NativePlayer extends BasePlayer implements VideoPlayerListenerCallback {
), ),
url: model.media?.url ?? "", url: model.media?.url ?? "",
); );
player.sendPlayableModel(playableData); await player.sendPlayableModel(playableData);
} }
} }

View file

@ -184,9 +184,11 @@ abstract class NativeVideoActivity {
@HostApi() @HostApi()
abstract class VideoPlayerApi { abstract class VideoPlayerApi {
@async
bool sendPlayableModel(PlayableData playableData); bool sendPlayableModel(PlayableData playableData);
void open(String url, bool play); @async
bool open(String url, bool play);
void setLooping(bool looping); void setLooping(bool looping);