flet-video 0.2.0.dev47__py3-none-any.whl → 0.2.0.dev65__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flet_video/__init__.py +3 -2
- flet_video/types.py +168 -0
- flet_video/video.py +263 -514
- flet_video-0.2.0.dev65.dist-info/METADATA +66 -0
- flet_video-0.2.0.dev65.dist-info/RECORD +20 -0
- flet_video-0.2.0.dev65.dist-info/licenses/LICENSE +201 -0
- flutter/flet_video/CHANGELOG.md +4 -0
- flutter/flet_video/lib/flet_video.dart +1 -1
- flutter/flet_video/lib/src/extension.dart +22 -0
- flutter/flet_video/lib/src/utils/file_utils_io.dart +9 -0
- flutter/flet_video/lib/src/utils/file_utils_web.dart +4 -0
- flutter/flet_video/lib/src/utils/video.dart +100 -86
- flutter/flet_video/lib/src/video.dart +166 -239
- flutter/flet_video/pubspec.lock +76 -51
- flutter/flet_video/pubspec.yaml +9 -4
- flet_video-0.2.0.dev47.dist-info/METADATA +0 -170
- flet_video-0.2.0.dev47.dist-info/RECORD +0 -16
- flutter/flet_video/lib/src/create_control.dart +0 -18
- {flet_video-0.2.0.dev47.dist-info → flet_video-0.2.0.dev65.dist-info}/WHEEL +0 -0
- {flet_video-0.2.0.dev47.dist-info → flet_video-0.2.0.dev65.dist-info}/top_level.txt +0 -0
flet_video/video.py
CHANGED
|
@@ -1,552 +1,301 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
3
|
-
from typing import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from flet.core.tooltip import TooltipValue
|
|
14
|
-
from flet.core.types import (
|
|
15
|
-
ColorEnums,
|
|
16
|
-
ColorValue,
|
|
17
|
-
ImageFit,
|
|
18
|
-
OffsetValue,
|
|
19
|
-
OptionalControlEventCallable,
|
|
20
|
-
OptionalEventCallable,
|
|
21
|
-
PaddingValue,
|
|
22
|
-
ResponsiveNumber,
|
|
23
|
-
RotateValue,
|
|
24
|
-
ScaleValue,
|
|
25
|
-
TextAlign,
|
|
1
|
+
import asyncio
|
|
2
|
+
from dataclasses import field
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import flet as ft
|
|
6
|
+
|
|
7
|
+
from .types import (
|
|
8
|
+
PlaylistMode,
|
|
9
|
+
VideoConfiguration,
|
|
10
|
+
VideoMedia,
|
|
11
|
+
VideoSubtitleConfiguration,
|
|
12
|
+
VideoSubtitleTrack,
|
|
26
13
|
)
|
|
27
|
-
from flet.utils import deprecated
|
|
28
14
|
|
|
15
|
+
__all__ = ["Video"]
|
|
29
16
|
|
|
30
|
-
class PlaylistMode(Enum):
|
|
31
|
-
NONE = "none"
|
|
32
|
-
SINGLE = "single"
|
|
33
|
-
LOOP = "loop"
|
|
34
17
|
|
|
18
|
+
@ft.control("Video")
|
|
19
|
+
class Video(ft.ConstrainedControl):
|
|
20
|
+
"""
|
|
21
|
+
A control that displays a video from a playlist.
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
http_headers: Optional[Dict[str, str]] = dataclasses.field(default=None)
|
|
40
|
-
extras: Optional[Dict[str, str]] = dataclasses.field(default=None)
|
|
23
|
+
Raises:
|
|
24
|
+
AssertionError: If the [`volume`][(c).] is not between `0.0` and `100.0` (inclusive).
|
|
25
|
+
"""
|
|
41
26
|
|
|
27
|
+
playlist: list[VideoMedia] = field(default_factory=list)
|
|
28
|
+
"""
|
|
29
|
+
A list of `VideoMedia`s representing the video files to be played.
|
|
30
|
+
"""
|
|
42
31
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
title: str = "flet-video"
|
|
33
|
+
"""
|
|
34
|
+
Defines the name of the underlying window & process for native backend.
|
|
35
|
+
This is visible inside the windows' volume mixer.
|
|
36
|
+
"""
|
|
48
37
|
|
|
38
|
+
fit: ft.BoxFit = ft.BoxFit.CONTAIN
|
|
39
|
+
"""
|
|
40
|
+
The box fit to use for the video.
|
|
41
|
+
"""
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
language: Optional[str] = dataclasses.field(default=None)
|
|
55
|
-
text_style: Optional[TextStyle] = dataclasses.field(default=None)
|
|
56
|
-
text_scale_factor: Optional[OptionalNumber] = dataclasses.field(default=None)
|
|
57
|
-
text_align: Optional[TextAlign] = dataclasses.field(default=None)
|
|
58
|
-
padding: Optional[PaddingValue] = dataclasses.field(default=None)
|
|
59
|
-
visible: Optional[bool] = dataclasses.field(default=None)
|
|
43
|
+
fill_color: ft.ColorValue = ft.Colors.BLACK
|
|
44
|
+
"""
|
|
45
|
+
Defines the color used to fill the video background.
|
|
46
|
+
"""
|
|
60
47
|
|
|
48
|
+
wakelock: bool = True
|
|
49
|
+
"""
|
|
50
|
+
Whether to acquire wake lock while playing the video.
|
|
51
|
+
When `True`, device's display will not go to standby/sleep while the video is playing.
|
|
52
|
+
"""
|
|
61
53
|
|
|
62
|
-
|
|
54
|
+
autoplay: bool = False
|
|
55
|
+
"""
|
|
56
|
+
Whether the video should start playing automatically.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
show_controls: bool = True
|
|
60
|
+
"""
|
|
61
|
+
Whether to show the video player controls.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
muted: bool = False
|
|
65
|
+
"""
|
|
66
|
+
Defines whether the video player should be started in muted state.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
playlist_mode: Optional[PlaylistMode] = None
|
|
70
|
+
"""
|
|
71
|
+
Represents the mode of playback for the playlist.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
shuffle_playlist: bool = False
|
|
75
|
+
"""
|
|
76
|
+
Defines whether the playlist should be shuffled.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
volume: ft.Number = 100.0
|
|
80
|
+
"""
|
|
81
|
+
Defines the volume of the video player.
|
|
82
|
+
|
|
83
|
+
Note:
|
|
84
|
+
It's value ranges between `0.0` to `100.0` (inclusive), where `0.0` is muted and `100.0` is the maximum volume.
|
|
85
|
+
An exception will be raised if the value is outside this range.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
playback_rate: ft.Number = 1.0
|
|
89
|
+
"""
|
|
90
|
+
Defines the playback rate of the video player.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
alignment: ft.Alignment = field(default_factory=lambda: ft.Alignment.center())
|
|
94
|
+
"""
|
|
95
|
+
Defines the Alignment of the viewport.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
filter_quality: ft.FilterQuality = ft.FilterQuality.LOW
|
|
99
|
+
"""
|
|
100
|
+
Filter quality of the texture used to render the video output.
|
|
101
|
+
|
|
102
|
+
Note:
|
|
103
|
+
Android was reported to show blurry images when using `ft.FilterQuality.HIGH`.
|
|
104
|
+
Prefer the usage of `ft.FilterQuality.MEDIUM` on this platform.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
pause_upon_entering_background_mode: bool = True
|
|
108
|
+
"""
|
|
109
|
+
Whether to pause the video when application enters background mode.
|
|
63
110
|
"""
|
|
64
|
-
A control that displays a video from a playlist.
|
|
65
111
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
animate_offset: Optional[AnimationValue] = None,
|
|
122
|
-
on_animation_end: OptionalEventCallable = None,
|
|
123
|
-
tooltip: TooltipValue = None,
|
|
124
|
-
badge: Optional[BadgeValue] = None,
|
|
125
|
-
visible: Optional[bool] = None,
|
|
126
|
-
disabled: Optional[bool] = None,
|
|
127
|
-
data: Any = None,
|
|
128
|
-
):
|
|
129
|
-
ConstrainedControl.__init__(
|
|
130
|
-
self,
|
|
131
|
-
ref=ref,
|
|
132
|
-
key=key,
|
|
133
|
-
width=width,
|
|
134
|
-
height=height,
|
|
135
|
-
left=left,
|
|
136
|
-
top=top,
|
|
137
|
-
right=right,
|
|
138
|
-
bottom=bottom,
|
|
139
|
-
expand=expand,
|
|
140
|
-
col=col,
|
|
141
|
-
opacity=opacity,
|
|
142
|
-
rotate=rotate,
|
|
143
|
-
scale=scale,
|
|
144
|
-
offset=offset,
|
|
145
|
-
aspect_ratio=aspect_ratio,
|
|
146
|
-
animate_opacity=animate_opacity,
|
|
147
|
-
animate_size=animate_size,
|
|
148
|
-
animate_position=animate_position,
|
|
149
|
-
animate_rotation=animate_rotation,
|
|
150
|
-
animate_scale=animate_scale,
|
|
151
|
-
animate_offset=animate_offset,
|
|
152
|
-
on_animation_end=on_animation_end,
|
|
153
|
-
tooltip=tooltip,
|
|
154
|
-
badge=badge,
|
|
155
|
-
visible=visible,
|
|
156
|
-
disabled=disabled,
|
|
157
|
-
data=data,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
self.__playlist = playlist or []
|
|
161
|
-
self.subtitle_configuration = subtitle_configuration
|
|
162
|
-
self.configuration = configuration
|
|
163
|
-
self.fit = fit
|
|
164
|
-
self.pitch = pitch
|
|
165
|
-
self.fill_color = fill_color
|
|
166
|
-
self.volume = volume
|
|
167
|
-
self.playback_rate = playback_rate
|
|
168
|
-
self.alignment = alignment
|
|
169
|
-
self.wakelock = wakelock
|
|
170
|
-
self.autoplay = autoplay
|
|
171
|
-
self.show_controls = show_controls
|
|
172
|
-
self.shuffle_playlist = shuffle_playlist
|
|
173
|
-
self.muted = muted
|
|
174
|
-
self.title = title
|
|
175
|
-
self.filter_quality = filter_quality
|
|
176
|
-
self.playlist_mode = playlist_mode
|
|
177
|
-
self.pause_upon_entering_background_mode = pause_upon_entering_background_mode
|
|
178
|
-
self.resume_upon_entering_foreground_mode = resume_upon_entering_foreground_mode
|
|
179
|
-
self.on_enter_fullscreen = on_enter_fullscreen
|
|
180
|
-
self.on_exit_fullscreen = on_exit_fullscreen
|
|
181
|
-
self.on_loaded = on_loaded
|
|
182
|
-
self.on_error = on_error
|
|
183
|
-
self.on_completed = on_completed
|
|
184
|
-
self.on_track_changed = on_track_changed
|
|
185
|
-
|
|
186
|
-
def _get_control_name(self):
|
|
187
|
-
return "video"
|
|
112
|
+
resume_upon_entering_foreground_mode: bool = False
|
|
113
|
+
"""
|
|
114
|
+
Whether to resume the video when application enters foreground mode.
|
|
115
|
+
Has effect only if `pause_upon_entering_background_mode` is also set to `True`.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
pitch: ft.Number = 1.0
|
|
119
|
+
"""
|
|
120
|
+
Defines the relative pitch of the video player.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
configuration: VideoConfiguration = field(
|
|
124
|
+
default_factory=lambda: VideoConfiguration()
|
|
125
|
+
)
|
|
126
|
+
"""
|
|
127
|
+
Additional configuration for the video player.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
subtitle_configuration: VideoSubtitleConfiguration = field(
|
|
131
|
+
default_factory=lambda: VideoSubtitleConfiguration()
|
|
132
|
+
)
|
|
133
|
+
"""
|
|
134
|
+
Defines the subtitle configuration for the video player.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
subtitle_track: Optional[VideoSubtitleTrack] = None
|
|
138
|
+
"""
|
|
139
|
+
Defines the subtitle track for the video player.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
on_load: ft.OptionalControlEventHandler["Video"] = None
|
|
143
|
+
"""Fires when the video player is initialized and ready for playback."""
|
|
144
|
+
|
|
145
|
+
on_enter_fullscreen: ft.OptionalControlEventHandler["Video"] = None
|
|
146
|
+
"""Fires when the video player enters fullscreen."""
|
|
147
|
+
|
|
148
|
+
on_exit_fullscreen: ft.OptionalControlEventHandler["Video"] = None
|
|
149
|
+
"""Fires when the video player exits fullscreen"""
|
|
150
|
+
|
|
151
|
+
on_error: ft.OptionalControlEventHandler["Video"] = None
|
|
152
|
+
"""
|
|
153
|
+
Fires when an error occurs.
|
|
154
|
+
|
|
155
|
+
Event handler argument's `data` property contains information about the error.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
on_complete: ft.OptionalControlEventHandler["Video"] = None
|
|
159
|
+
"""Fires when a video player completes."""
|
|
160
|
+
|
|
161
|
+
on_track_change: ft.OptionalControlEventHandler["Video"] = None
|
|
162
|
+
"""
|
|
163
|
+
Fires when a video track changes.
|
|
164
|
+
|
|
165
|
+
Event handler argument's `data` property contains the index of the new track.
|
|
166
|
+
"""
|
|
188
167
|
|
|
189
168
|
def before_update(self):
|
|
190
169
|
super().before_update()
|
|
191
|
-
self.
|
|
192
|
-
self._set_attr_json("playlist", self.__playlist if self.__playlist else None)
|
|
193
|
-
if isinstance(self.__subtitle_configuration, VideoSubtitleConfiguration):
|
|
194
|
-
self._set_attr_json("subtitleConfiguration", self.__subtitle_configuration)
|
|
195
|
-
|
|
196
|
-
if isinstance(self.__configuration, VideoConfiguration):
|
|
197
|
-
self._set_attr_json("configuration", self.__configuration)
|
|
170
|
+
assert 0 <= self.volume <= 100, "volume must be between 0 and 100 inclusive"
|
|
198
171
|
|
|
199
172
|
def play(self):
|
|
200
|
-
|
|
173
|
+
"""Starts playing the video."""
|
|
174
|
+
asyncio.create_task(self.play_async())
|
|
175
|
+
|
|
176
|
+
def play_async(self):
|
|
177
|
+
"""Starts playing the video."""
|
|
178
|
+
return self._invoke_method_async("play")
|
|
201
179
|
|
|
202
180
|
def pause(self):
|
|
203
|
-
|
|
181
|
+
"""Pauses the video player."""
|
|
182
|
+
asyncio.create_task(self.pause_async())
|
|
183
|
+
|
|
184
|
+
def pause_async(self):
|
|
185
|
+
"""Pauses the video player."""
|
|
186
|
+
return self._invoke_method_async("pause")
|
|
204
187
|
|
|
205
188
|
def play_or_pause(self):
|
|
206
|
-
|
|
189
|
+
"""
|
|
190
|
+
Cycles between play and pause states of the video player,
|
|
191
|
+
i.e., plays if paused and pauses if playing.
|
|
192
|
+
"""
|
|
193
|
+
asyncio.create_task(self.play_or_pause_async())
|
|
194
|
+
|
|
195
|
+
def play_or_pause_async(self):
|
|
196
|
+
"""
|
|
197
|
+
Cycles between play and pause states of the video player,
|
|
198
|
+
i.e., plays if paused and pauses if playing.
|
|
199
|
+
"""
|
|
200
|
+
return self._invoke_method_async("play_or_pause")
|
|
207
201
|
|
|
208
202
|
def stop(self):
|
|
209
|
-
|
|
203
|
+
"""Stops the video player."""
|
|
204
|
+
asyncio.create_task(self.stop_async())
|
|
205
|
+
|
|
206
|
+
def stop_async(self):
|
|
207
|
+
"""Stops the video player."""
|
|
208
|
+
return self._invoke_method_async("stop")
|
|
210
209
|
|
|
211
210
|
def next(self):
|
|
212
|
-
|
|
211
|
+
"""Jumps to the next `VideoMedia` in the `playlist`."""
|
|
212
|
+
asyncio.create_task(self.next_async())
|
|
213
|
+
|
|
214
|
+
def next_async(self):
|
|
215
|
+
"""Jumps to the next `VideoMedia` in the `playlist`."""
|
|
216
|
+
return self._invoke_method_async("next")
|
|
213
217
|
|
|
214
218
|
def previous(self):
|
|
215
|
-
|
|
219
|
+
"""Jumps to the previous `VideoMedia` in the `playlist`."""
|
|
220
|
+
asyncio.create_task(self.previous_async())
|
|
221
|
+
|
|
222
|
+
def previous_async(self):
|
|
223
|
+
"""Jumps to the previous `VideoMedia` in the `playlist`."""
|
|
224
|
+
return self._invoke_method_async("previous")
|
|
216
225
|
|
|
217
|
-
def seek(self,
|
|
218
|
-
|
|
226
|
+
def seek(self, position: ft.DurationValue):
|
|
227
|
+
"""
|
|
228
|
+
Seeks the currently playing `VideoMedia` from the `playlist` at the specified `position`.
|
|
229
|
+
"""
|
|
230
|
+
asyncio.create_task(self.seek_async(position))
|
|
231
|
+
|
|
232
|
+
def seek_async(self, position: ft.DurationValue):
|
|
233
|
+
"""
|
|
234
|
+
Seeks the currently playing `VideoMedia` from the `playlist` at the specified `position`.
|
|
235
|
+
"""
|
|
236
|
+
return self._invoke_method_async("seek", {"position": position})
|
|
219
237
|
|
|
220
238
|
def jump_to(self, media_index: int):
|
|
221
|
-
|
|
239
|
+
"""
|
|
240
|
+
Jumps to the `VideoMedia` at the specified `media_index` in the `playlist`.
|
|
241
|
+
"""
|
|
242
|
+
asyncio.create_task(self.jump_to_async(media_index))
|
|
243
|
+
|
|
244
|
+
async def jump_to_async(self, media_index: int):
|
|
245
|
+
"""
|
|
246
|
+
Jumps to the `VideoMedia` at the specified `media_index` in the `playlist`.
|
|
247
|
+
"""
|
|
248
|
+
assert self.playlist[media_index], "media_index is out of range"
|
|
222
249
|
if media_index < 0:
|
|
223
250
|
# dart doesn't support negative indexes
|
|
224
|
-
media_index = len(self.
|
|
225
|
-
self.
|
|
251
|
+
media_index = len(self.playlist) + media_index
|
|
252
|
+
await self._invoke_method_async("jump_to", {"media_index": media_index})
|
|
226
253
|
|
|
227
254
|
def playlist_add(self, media: VideoMedia):
|
|
255
|
+
"""Appends/Adds the provided `media` to the `playlist`."""
|
|
256
|
+
asyncio.create_task(self.playlist_add_async(media))
|
|
257
|
+
|
|
258
|
+
async def playlist_add_async(self, media: VideoMedia):
|
|
259
|
+
"""Appends/Adds the provided `media` to the `playlist`."""
|
|
228
260
|
assert media.resource, "media has no resource"
|
|
229
|
-
self.
|
|
230
|
-
|
|
231
|
-
{
|
|
232
|
-
"resource": media.resource,
|
|
233
|
-
"http_headers": str(media.http_headers or {}),
|
|
234
|
-
"extras": str(media.extras or {}),
|
|
235
|
-
},
|
|
236
|
-
)
|
|
237
|
-
self.__playlist.append(media)
|
|
261
|
+
await self._invoke_method_async("playlist_add", {"media": media})
|
|
262
|
+
self.playlist.append(media)
|
|
238
263
|
|
|
239
264
|
def playlist_remove(self, media_index: int):
|
|
240
|
-
|
|
241
|
-
self.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
sr = self.invoke_method(
|
|
278
|
-
"get_duration",
|
|
279
|
-
wait_for_result=True,
|
|
280
|
-
wait_timeout=wait_timeout,
|
|
281
|
-
)
|
|
282
|
-
return int(sr) if sr else None
|
|
283
|
-
|
|
284
|
-
async def get_duration_async(
|
|
285
|
-
self, wait_timeout: Optional[float] = 5
|
|
286
|
-
) -> Optional[int]:
|
|
287
|
-
sr = await self.invoke_method_async(
|
|
288
|
-
"get_duration",
|
|
289
|
-
wait_for_result=True,
|
|
290
|
-
wait_timeout=wait_timeout,
|
|
291
|
-
)
|
|
292
|
-
return int(sr) if sr else None
|
|
293
|
-
|
|
294
|
-
def get_current_position(self, wait_timeout: Optional[float] = 5) -> Optional[int]:
|
|
295
|
-
sr = self.invoke_method(
|
|
296
|
-
"get_current_position",
|
|
297
|
-
wait_for_result=True,
|
|
298
|
-
wait_timeout=wait_timeout,
|
|
299
|
-
)
|
|
300
|
-
return int(sr) if sr else None
|
|
301
|
-
|
|
302
|
-
async def get_current_position_async(
|
|
303
|
-
self, wait_timeout: Optional[float] = 5
|
|
304
|
-
) -> Optional[int]:
|
|
305
|
-
sr = await self.invoke_method_async(
|
|
306
|
-
"get_current_position",
|
|
307
|
-
wait_for_result=True,
|
|
308
|
-
wait_timeout=wait_timeout,
|
|
309
|
-
)
|
|
310
|
-
return int(sr) if sr else None
|
|
311
|
-
|
|
312
|
-
# playlist
|
|
313
|
-
@property
|
|
314
|
-
def playlist(self) -> Optional[List[VideoMedia]]:
|
|
315
|
-
return self.__playlist
|
|
316
|
-
|
|
317
|
-
# fit
|
|
318
|
-
@property
|
|
319
|
-
def fit(self) -> Optional[ImageFit]:
|
|
320
|
-
return self.__fit
|
|
321
|
-
|
|
322
|
-
@fit.setter
|
|
323
|
-
def fit(self, value: Optional[ImageFit]):
|
|
324
|
-
self.__fit = value
|
|
325
|
-
self._set_attr("fit", value.value if isinstance(value, ImageFit) else value)
|
|
326
|
-
|
|
327
|
-
# subtitle_configuration
|
|
328
|
-
@property
|
|
329
|
-
def subtitle_configuration(self) -> Optional[VideoSubtitleConfiguration]:
|
|
330
|
-
return self.__subtitle_configuration
|
|
331
|
-
|
|
332
|
-
@subtitle_configuration.setter
|
|
333
|
-
def subtitle_configuration(self, value: Optional[VideoSubtitleConfiguration]):
|
|
334
|
-
self.__subtitle_configuration = value
|
|
335
|
-
|
|
336
|
-
# configuration
|
|
337
|
-
@property
|
|
338
|
-
def configuration(self) -> Optional[VideoConfiguration]:
|
|
339
|
-
return self.__configuration
|
|
340
|
-
|
|
341
|
-
@configuration.setter
|
|
342
|
-
def configuration(self, value: Optional[VideoConfiguration]):
|
|
343
|
-
self.__configuration = value
|
|
344
|
-
|
|
345
|
-
# fill_color
|
|
346
|
-
@property
|
|
347
|
-
def fill_color(self) -> Optional[ColorValue]:
|
|
348
|
-
return self.__fill_color
|
|
349
|
-
|
|
350
|
-
@fill_color.setter
|
|
351
|
-
def fill_color(self, value: Optional[ColorValue]):
|
|
352
|
-
self.__fill_color = value
|
|
353
|
-
self._set_enum_attr("fillColor", value, ColorEnums)
|
|
354
|
-
|
|
355
|
-
# wakelock
|
|
356
|
-
@property
|
|
357
|
-
def wakelock(self) -> bool:
|
|
358
|
-
return self._get_attr("wakelock", data_type="bool", def_value=True)
|
|
359
|
-
|
|
360
|
-
@wakelock.setter
|
|
361
|
-
def wakelock(self, value: Optional[bool]):
|
|
362
|
-
self._set_attr("wakelock", value)
|
|
363
|
-
|
|
364
|
-
# autoplay
|
|
365
|
-
@property
|
|
366
|
-
def autoplay(self) -> bool:
|
|
367
|
-
return self._get_attr("autoPlay", data_type="bool", def_value=False)
|
|
368
|
-
|
|
369
|
-
@autoplay.setter
|
|
370
|
-
def autoplay(self, value: Optional[bool]):
|
|
371
|
-
self._set_attr("autoPlay", value)
|
|
372
|
-
|
|
373
|
-
# muted
|
|
374
|
-
@property
|
|
375
|
-
def muted(self) -> bool:
|
|
376
|
-
return self._get_attr("muted", data_type="bool", def_value=False)
|
|
377
|
-
|
|
378
|
-
@muted.setter
|
|
379
|
-
def muted(self, value: Optional[bool]):
|
|
380
|
-
self._set_attr("muted", value)
|
|
381
|
-
|
|
382
|
-
# shuffle_playlist
|
|
383
|
-
@property
|
|
384
|
-
def shuffle_playlist(self) -> bool:
|
|
385
|
-
return self._get_attr("shufflePlaylist", data_type="bool", def_value=False)
|
|
386
|
-
|
|
387
|
-
@shuffle_playlist.setter
|
|
388
|
-
def shuffle_playlist(self, value: Optional[bool]):
|
|
389
|
-
self._set_attr("shufflePlaylist", value)
|
|
390
|
-
|
|
391
|
-
# show_controls
|
|
392
|
-
@property
|
|
393
|
-
def show_controls(self) -> bool:
|
|
394
|
-
return self._get_attr("showControls", data_type="bool", def_value=True)
|
|
395
|
-
|
|
396
|
-
@show_controls.setter
|
|
397
|
-
def show_controls(self, value: Optional[bool]):
|
|
398
|
-
self._set_attr("showControls", value)
|
|
399
|
-
|
|
400
|
-
# pitch
|
|
401
|
-
@property
|
|
402
|
-
def pitch(self) -> OptionalNumber:
|
|
403
|
-
return self._get_attr("pitch", data_type="float")
|
|
404
|
-
|
|
405
|
-
@pitch.setter
|
|
406
|
-
def pitch(self, value: OptionalNumber):
|
|
407
|
-
self._set_attr("pitch", value)
|
|
408
|
-
|
|
409
|
-
# volume
|
|
410
|
-
@property
|
|
411
|
-
def volume(self) -> OptionalNumber:
|
|
412
|
-
return self._get_attr("volume", data_type="float")
|
|
413
|
-
|
|
414
|
-
@volume.setter
|
|
415
|
-
def volume(self, value: OptionalNumber):
|
|
416
|
-
assert value is None or 0 <= value <= 100, "volume must be between 0 and 100"
|
|
417
|
-
self._set_attr("volume", value)
|
|
418
|
-
|
|
419
|
-
# playback_rate
|
|
420
|
-
@property
|
|
421
|
-
def playback_rate(self) -> OptionalNumber:
|
|
422
|
-
return self._get_attr("playbackRate", data_type="float")
|
|
423
|
-
|
|
424
|
-
@playback_rate.setter
|
|
425
|
-
def playback_rate(self, value: OptionalNumber):
|
|
426
|
-
self._set_attr("playbackRate", value)
|
|
427
|
-
|
|
428
|
-
# title
|
|
429
|
-
@property
|
|
430
|
-
def title(self) -> Optional[str]:
|
|
431
|
-
return self._get_attr("title")
|
|
432
|
-
|
|
433
|
-
@title.setter
|
|
434
|
-
def title(self, value: Optional[str]):
|
|
435
|
-
self._set_attr("title", value)
|
|
436
|
-
|
|
437
|
-
# pause_upon_entering_background_mode
|
|
438
|
-
@property
|
|
439
|
-
def pause_upon_entering_background_mode(self) -> bool:
|
|
440
|
-
return cast(
|
|
441
|
-
bool,
|
|
442
|
-
self._get_attr(
|
|
443
|
-
"pauseUponEnteringBackgroundMode", data_type="bool", def_value=True
|
|
444
|
-
),
|
|
445
|
-
)
|
|
446
|
-
|
|
447
|
-
@pause_upon_entering_background_mode.setter
|
|
448
|
-
def pause_upon_entering_background_mode(self, value: Optional[bool]):
|
|
449
|
-
self._set_attr("pauseUponEnteringBackgroundMode", value)
|
|
450
|
-
|
|
451
|
-
# resume_upon_entering_foreground_mode
|
|
452
|
-
@property
|
|
453
|
-
def resume_upon_entering_foreground_mode(self) -> bool:
|
|
454
|
-
return cast(
|
|
455
|
-
bool,
|
|
456
|
-
self._get_attr(
|
|
457
|
-
"resumeUponEnteringForegroundMode", data_type="bool", def_value=False
|
|
458
|
-
),
|
|
459
|
-
)
|
|
460
|
-
|
|
461
|
-
@resume_upon_entering_foreground_mode.setter
|
|
462
|
-
def resume_upon_entering_foreground_mode(self, value: Optional[bool]):
|
|
463
|
-
self._set_attr("resumeUponEnteringForegroundMode", value)
|
|
464
|
-
|
|
465
|
-
# alignment
|
|
466
|
-
@property
|
|
467
|
-
def alignment(self) -> Optional[Alignment]:
|
|
468
|
-
return self.__alignment
|
|
469
|
-
|
|
470
|
-
@alignment.setter
|
|
471
|
-
def alignment(self, value: Optional[Alignment]):
|
|
472
|
-
self.__alignment = value
|
|
473
|
-
|
|
474
|
-
# filter_quality
|
|
475
|
-
@property
|
|
476
|
-
def filter_quality(self) -> Optional[FilterQuality]:
|
|
477
|
-
return self.__filter_quality
|
|
478
|
-
|
|
479
|
-
@filter_quality.setter
|
|
480
|
-
def filter_quality(self, value: Optional[FilterQuality]):
|
|
481
|
-
self.__filter_quality = value
|
|
482
|
-
self._set_enum_attr("filterQuality", value, FilterQuality)
|
|
483
|
-
|
|
484
|
-
# playlist_mode
|
|
485
|
-
@property
|
|
486
|
-
def playlist_mode(self) -> Optional[PlaylistMode]:
|
|
487
|
-
return self.__playlist_mode
|
|
488
|
-
|
|
489
|
-
@playlist_mode.setter
|
|
490
|
-
def playlist_mode(self, value: Optional[PlaylistMode]):
|
|
491
|
-
self.__playlist_mode = value
|
|
492
|
-
self._set_enum_attr("playlistMode", value, PlaylistMode)
|
|
493
|
-
|
|
494
|
-
# on_enter_fullscreen
|
|
495
|
-
@property
|
|
496
|
-
def on_enter_fullscreen(self):
|
|
497
|
-
return self._get_event_handler("enter_fullscreen")
|
|
498
|
-
|
|
499
|
-
@on_enter_fullscreen.setter
|
|
500
|
-
def on_enter_fullscreen(self, handler: OptionalControlEventCallable):
|
|
501
|
-
self._add_event_handler("enter_fullscreen", handler)
|
|
502
|
-
self._set_attr("onEnterFullscreen", True if handler is not None else None)
|
|
503
|
-
|
|
504
|
-
# on_exit_fullscreen
|
|
505
|
-
@property
|
|
506
|
-
def on_exit_fullscreen(self) -> OptionalControlEventCallable:
|
|
507
|
-
return self._get_event_handler("exit_fullscreen")
|
|
508
|
-
|
|
509
|
-
@on_exit_fullscreen.setter
|
|
510
|
-
def on_exit_fullscreen(self, handler: OptionalControlEventCallable):
|
|
511
|
-
self._add_event_handler("exit_fullscreen", handler)
|
|
512
|
-
self._set_attr("onExitFullscreen", True if handler is not None else None)
|
|
513
|
-
|
|
514
|
-
# on_loaded
|
|
515
|
-
@property
|
|
516
|
-
def on_loaded(self) -> OptionalControlEventCallable:
|
|
517
|
-
return self._get_event_handler("loaded")
|
|
518
|
-
|
|
519
|
-
@on_loaded.setter
|
|
520
|
-
def on_loaded(self, handler: OptionalControlEventCallable):
|
|
521
|
-
self._set_attr("onLoaded", True if handler is not None else None)
|
|
522
|
-
self._add_event_handler("loaded", handler)
|
|
523
|
-
|
|
524
|
-
# on_error
|
|
525
|
-
@property
|
|
526
|
-
def on_error(self) -> OptionalControlEventCallable:
|
|
527
|
-
return self._get_event_handler("error")
|
|
528
|
-
|
|
529
|
-
@on_error.setter
|
|
530
|
-
def on_error(self, handler: OptionalControlEventCallable):
|
|
531
|
-
self._set_attr("onError", True if handler is not None else None)
|
|
532
|
-
self._add_event_handler("error", handler)
|
|
533
|
-
|
|
534
|
-
# on_completed
|
|
535
|
-
@property
|
|
536
|
-
def on_completed(self) -> OptionalControlEventCallable:
|
|
537
|
-
return self._get_event_handler("completed")
|
|
538
|
-
|
|
539
|
-
@on_completed.setter
|
|
540
|
-
def on_completed(self, handler: OptionalControlEventCallable):
|
|
541
|
-
self._set_attr("onCompleted", True if handler is not None else None)
|
|
542
|
-
self._add_event_handler("completed", handler)
|
|
543
|
-
|
|
544
|
-
# on_track_changed
|
|
545
|
-
@property
|
|
546
|
-
def on_track_changed(self) -> OptionalControlEventCallable:
|
|
547
|
-
return self._get_event_handler("track_changed")
|
|
548
|
-
|
|
549
|
-
@on_track_changed.setter
|
|
550
|
-
def on_track_changed(self, handler: OptionalControlEventCallable):
|
|
551
|
-
self._set_attr("onTrackChanged", True if handler is not None else None)
|
|
552
|
-
self._add_event_handler("track_changed", handler)
|
|
265
|
+
"""Removes the provided `media` from the `playlist`."""
|
|
266
|
+
asyncio.create_task(self.playlist_remove_async(media_index))
|
|
267
|
+
|
|
268
|
+
async def playlist_remove_async(self, media_index: int):
|
|
269
|
+
"""Removes the provided `media` from the `playlist`."""
|
|
270
|
+
assert self.playlist[media_index], "index out of range"
|
|
271
|
+
await self._invoke_method_async("playlist_remove", {"media_index": media_index})
|
|
272
|
+
self.playlist.pop(media_index)
|
|
273
|
+
|
|
274
|
+
async def is_playing_async(self) -> bool:
|
|
275
|
+
"""
|
|
276
|
+
Returns:
|
|
277
|
+
`True` if the video player is currently playing, `False` otherwise.
|
|
278
|
+
"""
|
|
279
|
+
return await self._invoke_method_async("is_playing")
|
|
280
|
+
|
|
281
|
+
async def is_completed_async(self) -> bool:
|
|
282
|
+
"""
|
|
283
|
+
Returns:
|
|
284
|
+
`True` if video player has reached the end of
|
|
285
|
+
the currently playing media, `False` otherwise.
|
|
286
|
+
"""
|
|
287
|
+
return await self._invoke_method_async("is_completed")
|
|
288
|
+
|
|
289
|
+
async def get_duration_async(self) -> ft.Duration:
|
|
290
|
+
"""
|
|
291
|
+
Returns:
|
|
292
|
+
The duration of the currently playing media.
|
|
293
|
+
"""
|
|
294
|
+
return await self._invoke_method_async("get_duration")
|
|
295
|
+
|
|
296
|
+
async def get_current_position_async(self) -> ft.Duration:
|
|
297
|
+
"""
|
|
298
|
+
Returns:
|
|
299
|
+
The current position of the currently playing media.
|
|
300
|
+
"""
|
|
301
|
+
return await self._invoke_method_async("get_current_position")
|