From 8d93d6e969f9b785a7712e9099c4b8642bdc50ad Mon Sep 17 00:00:00 2001 From: syle Date: Fri, 13 Dec 2024 08:01:08 -0600 Subject: [PATCH] Change on_percent to on_position - send back position,duration,percent - throttle configurable --- packages/flet_video/lib/src/video.dart | 37 ++++++++------- .../packages/flet/src/flet/core/video.py | 46 +++++++++++++++---- 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/packages/flet_video/lib/src/video.dart b/packages/flet_video/lib/src/video.dart index f8f5f6ae1..b84f487cb 100644 --- a/packages/flet_video/lib/src/video.dart +++ b/packages/flet_video/lib/src/video.dart @@ -28,7 +28,7 @@ class VideoControl extends StatefulWidget { class _VideoControlState extends State with FletStoreMixin { int _lastProcessedIndex = -1; - int _lastPercent = -1; + Duration _lastEmittedPosition = Duration.zero; late final playerConfig = PlayerConfiguration( title: widget.control.attrString("title", "Flet Video")!, muted: widget.control.attrBool("muted", false)!, @@ -98,13 +98,18 @@ class _VideoControlState extends State with FletStoreMixin { .triggerControlEvent(widget.control.id, "track_changed", message ?? ""); } - void _onPercentChanged(String? message) { - // Let's not debug print this, cause to much traffic on console - // debugPrint("Video onPercentChanged: $message"); - widget.backend - .triggerControlEvent(widget.control.id, "percent_changed", message ?? ""); + void _onPositionChanged(Duration position, Duration duration, int percent) { + // commenting out, may be too verbose to display every 1 second + // debugPrint("New Position is ${position} and duration is ${duration} and percent is ${percent}"); + final data = { + "position": position.inSeconds, // Send position in seconds + "duration": duration.inSeconds, // Send duration in seconds + "percent": percent, + }; + widget.backend.triggerControlEvent(widget.control.id, "positionChanged", jsonEncode(data)); } + @override Widget build(BuildContext context) { debugPrint("Video build: ${widget.control.id}"); @@ -136,7 +141,9 @@ class _VideoControlState extends State with FletStoreMixin { bool onError = widget.control.attrBool("onError", false)!; bool onCompleted = widget.control.attrBool("onCompleted", false)!; bool onTrackChanged = widget.control.attrBool("onTrackChanged", false)!; - bool onPercentChanged = widget.control.attrBool("onPercentChanged", false)!; + bool onPositionChanged = widget.control.attrBool("onPositionChanged", false)!; + int throttle = widget.control.attrInt("throttle", 1000)!; + double? volume = widget.control.attrDouble("volume"); double? pitch = widget.control.attrDouble("pitch"); @@ -311,18 +318,16 @@ class _VideoControlState extends State with FletStoreMixin { } }); // Send percentage position change between 0-100 to use with flet slider. - // This will make flet event loop less busy sending round int numbers // as well as throttling to 1 second to not overload flet socket - player.stream.position.throttleTime(const Duration(seconds: 1)).listen((position) { - if (onPercentChanged) { + player.stream.position.throttleTime(Duration(milliseconds: throttle)).listen((position) { + if (onPositionChanged && position.inSeconds != _lastEmittedPosition.inSeconds) { try { - final int percent = (position.inMilliseconds / player.state.duration.inMilliseconds * 100).toInt(); - if (percent != _lastPercent) { - _lastPercent = percent; - _onPercentChanged(percent.toString()); - } + final duration = player.state.duration; + final int percent = (position.inMilliseconds / duration.inMilliseconds * 100).toInt(); + _lastEmittedPosition = position; + _onPositionChanged(position, duration, percent); } catch (e) { - debugPrint("Error calculating percentage: $e"); + debugPrint("Error in OnPositionChanged: $e"); } } }); diff --git a/sdk/python/packages/flet/src/flet/core/video.py b/sdk/python/packages/flet/src/flet/core/video.py index f06a2ce42..1d388eb9c 100644 --- a/sdk/python/packages/flet/src/flet/core/video.py +++ b/sdk/python/packages/flet/src/flet/core/video.py @@ -1,4 +1,5 @@ import dataclasses +import json from enum import Enum from typing import Any, Dict, List, Optional, Union, cast @@ -8,6 +9,8 @@ from flet.core.box import FilterQuality from flet.core.constrained_control import ConstrainedControl from flet.core.control import OptionalNumber +from flet.core.control_event import ControlEvent +from flet.core.event_handler import EventHandler from flet.core.ref import Ref from flet.core.text_style import TextStyle from flet.core.tooltip import TooltipValue @@ -58,6 +61,14 @@ class VideoSubtitleConfiguration: padding: Optional[PaddingValue] = dataclasses.field(default=None) visible: Optional[bool] = dataclasses.field(default=None) +class VideoPositionChangedEvent(ControlEvent): + def __init__(self, e: ControlEvent): + super().__init__(e.target, e.name, e.data, e.control, e.page) + d = json.loads(e.data) + self.position: int = d.get("position") + self.duration: int = d.get("duration") + self.percent: int = d.get("percent") + class Video(ConstrainedControl): """ @@ -81,6 +92,7 @@ def __init__( playlist_mode: Optional[PlaylistMode] = None, shuffle_playlist: Optional[bool] = None, volume: OptionalNumber = None, + throttle: Optional[int] = None, playback_rate: OptionalNumber = None, alignment: Optional[Alignment] = None, filter_quality: Optional[FilterQuality] = None, @@ -96,7 +108,7 @@ def __init__( on_error: OptionalControlEventCallable = None, on_completed: OptionalControlEventCallable = None, on_track_changed: OptionalControlEventCallable = None, - on_percent_changed: OptionalControlEventCallable = None, + on_position_changed: OptionalControlEventCallable = None, # # ConstrainedControl # @@ -165,6 +177,7 @@ def __init__( self.pitch = pitch self.fill_color = fill_color self.volume = volume + self.throttle = throttle self.playback_rate = playback_rate self.alignment = alignment self.wakelock = wakelock @@ -183,7 +196,13 @@ def __init__( self.on_error = on_error self.on_completed = on_completed self.on_track_changed = on_track_changed - self.on_percent_changed = on_percent_changed + self.__on_position_changed = EventHandler( + lambda e: VideoPositionChangedEvent(e) + ) + self._add_event_handler( + "positionChanged", self.__on_position_changed.get_handler() + ) + self.on_position_changed = on_position_changed def _get_control_name(self): return "video" @@ -418,6 +437,15 @@ def volume(self, value: OptionalNumber): assert value is None or 0 <= value <= 100, "volume must be between 0 and 100" self._set_attr("volume", value) + # throttle + @property + def throttle(self) -> Optional[int]: + return self._get_attr("throttle", data_type="int") + + @throttle.setter + def throttle(self, value: Optional[int]): + self._set_attr("throttle", value) + # playback_rate @property def playback_rate(self) -> OptionalNumber: @@ -553,12 +581,12 @@ def on_track_changed(self, handler: OptionalControlEventCallable): self._set_attr("onTrackChanged", True if handler is not None else None) self._add_event_handler("track_changed", handler) - # on_pos_changed + # on_position_changed @property - def on_percent_changed(self) -> OptionalControlEventCallable: - return self._get_event_handler("percent_changed") + def on_position_changed(self,) -> OptionalEventCallable[VideoPositionChangedEvent]: + return self.__on_position_changed.handler - @on_percent_changed.setter - def on_percent_changed(self, handler: OptionalControlEventCallable): - self._set_attr("onPercentChanged", True if handler is not None else None) - self._add_event_handler("percent_changed", handler) + @on_position_changed.setter + def on_position_changed(self, handler: OptionalEventCallable[VideoPositionChangedEvent]): + self.__on_position_changed.handler = handler + self._set_attr("onPositionChanged", True if handler is not None else None)