44
55from typing import Any , TypedDict
66
7- from reflex .components .component import NoSSRComponent
8- from reflex .event import EventHandler , no_args_event_spec , passthrough_event_spec
7+ from reflex .components .component import Component
8+ from reflex .components .core .cond import cond
9+ from reflex .event import EventHandler , no_args_event_spec
10+ from reflex .utils import console
911from reflex .vars .base import Var
12+ from reflex .vars .object import ObjectVar
13+
14+ ReactPlayerEvent = ObjectVar [dict [str , dict [str , dict [str , Any ]]]]
1015
1116
1217class Progress (TypedDict ):
@@ -16,21 +21,123 @@ class Progress(TypedDict):
1621 playedSeconds : float
1722 loaded : float
1823 loadedSeconds : float
24+ duration : float
25+
26+
27+ def _on_progress_signature (event : ReactPlayerEvent ) -> list [Var [Progress ]]:
28+ """Type signature for on_progress event.
29+
30+ Args:
31+ event: The event variable.
32+
33+ Returns:
34+ The progress information extracted from the event.
35+ """
36+ player_info = event ["target" ]["api" ]["playerInfo" ].to (dict )
37+ progress_state = player_info ["progressState" ].to (dict )
38+ current = progress_state ["current" ].to (float )
39+ loaded = progress_state ["loaded" ].to (float )
40+ duration = progress_state ["duration" ].to (float )
41+ return [
42+ cond (
43+ progress_state ,
44+ {
45+ "played" : cond (duration , current / duration , 0.0 ),
46+ "playedSeconds" : current ,
47+ "loaded" : cond (duration , loaded / duration , 0.0 ),
48+ "loadedSeconds" : loaded ,
49+ "duration" : duration ,
50+ },
51+ {
52+ "played" : 0.0 ,
53+ "playedSeconds" : 0.0 ,
54+ "loaded" : 0.0 ,
55+ "loadedSeconds" : 0.0 ,
56+ "duration" : 0.0 ,
57+ },
58+ ).to (Progress )
59+ ]
60+
61+
62+ def _player_info_key_or_zero (event : ReactPlayerEvent , key : str ) -> Var [float ]:
63+ """Helper to extract a value from playerInfo or return 0.0 if not available.
64+
65+ Args:
66+ event: The event variable.
67+ key: The key to extract from playerInfo.
68+
69+ Returns:
70+ The extracted value or 0.0 if not available.
71+ """
72+ player_info = event ["target" ]["api" ]["playerInfo" ].to (dict )
73+ return cond (
74+ player_info [key ],
75+ player_info [key ],
76+ 0.0 ,
77+ ).to (float )
78+
79+
80+ def _on_time_update_signature (event : ReactPlayerEvent ) -> list [Var [float ]]:
81+ """Type signature for on_time_update event.
82+
83+ Args:
84+ event: The event variable.
85+
86+ Returns:
87+ The current timestamp in seconds.
88+ """
89+ return [_player_info_key_or_zero (event , "currentTime" )]
90+
91+
92+ def _on_duration_change_signature (event : ReactPlayerEvent ) -> list [Var [float ]]:
93+ """Type signature for on_duration_change event.
94+
95+ Args:
96+ event: The event variable.
97+
98+ Returns:
99+ The active media's duration in seconds.
100+ """
101+ return [_player_info_key_or_zero (event , "duration" )]
102+
103+
104+ def _on_rate_change_signature (event : ReactPlayerEvent ) -> list [Var [float ]]:
105+ """Type signature for on_rate_change event.
19106
107+ Args:
108+ event: The event variable.
20109
21- class ReactPlayer (NoSSRComponent ):
110+ Returns:
111+ The current playback rate.
112+ """
113+ return [_player_info_key_or_zero (event , "playbackRate" )]
114+
115+
116+ _DEPRECATED_PROP_MAP = {
117+ "url" : "src" ,
118+ "on_duration" : "on_duration_change" ,
119+ "on_playback_rate_change" : "on_rate_change" ,
120+ "on_seek" : "on_seeked" ,
121+ "on_buffer" : "on_waiting" ,
122+ "on_buffer_end" : "on_playing" ,
123+ "on_enable_pip" : "on_enter_picture_in_picture" ,
124+ "on_disable_pip" : "on_leave_picture_in_picture" ,
125+ }
126+
127+
128+ class ReactPlayer (Component ):
22129 """Using react-player and not implement all props and callback yet.
23130 reference: https://github.com/cookpete/react-player.
24131 """
25132
26- library = "react-player@2.16.0 "
133+ library = "react-player@3.3.3 "
27134
28135 tag = "ReactPlayer"
29136
30137 is_default = True
31138
32139 # The url of a video or song to play
33- url : Var [str ]
140+ src : Var [str | list [ str ] | list [ dict [ str , str ]] ]
34141
35142 # Set to true or false to pause or play the media
36143 playing : Var [bool ]
@@ -50,38 +157,44 @@ class ReactPlayer(NoSSRComponent):
50157 # Mutes the player
51158 muted : Var [bool ]
52159
160+ # Player-specific configuration parameters.
161+ config : Var [dict [str , Any ]]
162+
53163 # Called when media is loaded and ready to play. If playing is set to true, media will play immediately.
54164 on_ready : EventHandler [no_args_event_spec ]
55165
56166 # Called when media starts playing.
57167 on_start : EventHandler [no_args_event_spec ]
58168
59- # Called when media starts or resumes playing after pausing or buffering .
169+ # Called when playing is set to true .
60170 on_play : EventHandler [no_args_event_spec ]
61171
62- # Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
63- on_progress : EventHandler [passthrough_event_spec (Progress )]
172+ # Called when media starts or resumes playing after pausing or buffering.
173+ on_playing : EventHandler [no_args_event_spec ]
174+
175+ # Called while the video is loading only. Contains played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
176+ on_progress : EventHandler [_on_progress_signature ]
177+
178+ # Called when the media's current time changes (~4Hz, use .throttle to limit calls to backend).
179+ on_time_update : EventHandler [_on_time_update_signature ]
64180
65181 # Callback containing duration of the media, in seconds.
66- on_duration : EventHandler [passthrough_event_spec ( float ) ]
182+ on_duration_change : EventHandler [_on_duration_change_signature ]
67183
68184 # Called when media is paused.
69185 on_pause : EventHandler [no_args_event_spec ]
70186
71187 # Called when media starts buffering.
72- on_buffer : EventHandler [no_args_event_spec ]
188+ on_waiting : EventHandler [no_args_event_spec ]
73189
74- # Called when media has finished buffering. Works for files, YouTube and Facebook .
75- on_buffer_end : EventHandler [no_args_event_spec ]
190+ # Called when the media is seeking .
191+ on_seeking : EventHandler [no_args_event_spec ]
76192
77193 # Called when media seeks with seconds parameter.
78- on_seek : EventHandler [passthrough_event_spec ( float ) ]
194+ on_seeked : EventHandler [_on_time_update_signature ]
79195
80196 # Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths.
81- on_playback_rate_change : EventHandler [no_args_event_spec ]
82-
83- # Called when playback quality of the player changed. Only supported by YouTube (if enabled).
84- on_playback_quality_change : EventHandler [no_args_event_spec ]
197+ on_rate_change : EventHandler [_on_rate_change_signature ]
85198
86199 # Called when media finishes playing. Does not fire when loop is set to true.
87200 on_ended : EventHandler [no_args_event_spec ]
@@ -93,10 +206,37 @@ class ReactPlayer(NoSSRComponent):
93206 on_click_preview : EventHandler [no_args_event_spec ]
94207
95208 # Called when picture-in-picture mode is enabled.
96- on_enable_pip : EventHandler [no_args_event_spec ]
209+ on_enter_picture_in_picture : EventHandler [no_args_event_spec ]
97210
98211 # Called when picture-in-picture mode is disabled.
99- on_disable_pip : EventHandler [no_args_event_spec ]
212+ on_leave_picture_in_picture : EventHandler [no_args_event_spec ]
213+
214+ @classmethod
215+ def create (cls , * children , ** props ) -> ReactPlayer :
216+ """Create a component.
217+
218+ Args:
219+ children: The children of the component.
220+ props: The props of the component.
221+
222+ Returns:
223+ The created component.
224+
225+ Raises:
226+ ValueError: If both a deprecated prop and its replacement are both passed.
227+ """
228+ for prop , new_prop in _DEPRECATED_PROP_MAP .items ():
229+ if prop in props :
230+ if new_prop in props :
231+ msg = (
232+ f"The prop { prop !r} is deprecated, but the replacement { new_prop !r} is also passed. Please remove { prop !r} ." ,
233+ )
234+ raise ValueError (msg )
235+ console .warn (
236+ f"The prop { prop !r} has been replaced by { new_prop !r} , please update your code." ,
237+ )
238+ props [new_prop ] = props .pop (prop )
239+ return super ().create (* children , ** props ) # type: ignore[return-value]
100240
101241 def _render (self , props : dict [str , Any ] | None = None ):
102242 """Render the component. Adds width and height set to None because
0 commit comments