Skip to content

Commit 80b6e71

Browse files
authored
Upgrade to react-player v3.3.3 (#5818)
* Upgrade to react-player v3.3.3 This is a breaking change as many props and event handlers have changed name and signature. Some care has been taken to maintain a compatible API for Reflex users of `rx.video`. * update pyi_hashes for audio/video components * ReactPlayer: NoSSRComponent -> Component Update comments describing props for typo/accuracy
1 parent 71acfd7 commit 80b6e71

File tree

2 files changed

+162
-22
lines changed

2 files changed

+162
-22
lines changed

pyi_hashes.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@
107107
"reflex/components/radix/themes/typography/heading.pyi": "4b059485eaafcf2c41429e02cbe483ef",
108108
"reflex/components/radix/themes/typography/link.pyi": "85d50a3c89a8b3b019289d20d85e9960",
109109
"reflex/components/radix/themes/typography/text.pyi": "5397e5691b5f0b324378f2fcb6bf0f1f",
110-
"reflex/components/react_player/audio.pyi": "466d4cc06c8139f97c43f8e62c6de4f6",
111-
"reflex/components/react_player/react_player.pyi": "90578ffc889ca84c1ea6813de5d7224e",
112-
"reflex/components/react_player/video.pyi": "b69ef0c5d780ab03b0ebb56d0f004cc0",
110+
"reflex/components/react_player/audio.pyi": "9de7d9830534c6ec1f305434c4d8abd8",
111+
"reflex/components/react_player/react_player.pyi": "7a7a29a64a0d127c12c8131abcd012d1",
112+
"reflex/components/react_player/video.pyi": "e946b4915b41326d4e4b39381349d152",
113113
"reflex/components/react_router/dom.pyi": "ea17a16e817523009e30fb049b1cf9dc",
114114
"reflex/components/recharts/__init__.pyi": "6ee7f1ca2c0912f389ba6f3251a74d99",
115115
"reflex/components/recharts/cartesian.pyi": "cfca4f880239ffaecdf9fb4c7c8caed5",

reflex/components/react_player/react_player.py

Lines changed: 159 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44

55
from 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
911
from 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

1217
class 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

Comments
 (0)