flet-video 0.2.0.dev47__py3-none-any.whl → 0.2.0.dev76__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.

Potentially problematic release.


This version of flet-video might be problematic. Click here for more details.

flet_video/video.py CHANGED
@@ -1,552 +1,301 @@
1
- import dataclasses
2
- from enum import Enum
3
- from typing import Any, Dict, List, Optional, Union, cast
4
-
5
- from flet.core.alignment import Alignment
6
- from flet.core.animation import AnimationValue
7
- from flet.core.badge import BadgeValue
8
- from flet.core.box import FilterQuality
9
- from flet.core.constrained_control import ConstrainedControl
10
- from flet.core.control import OptionalNumber
11
- from flet.core.ref import Ref
12
- from flet.core.text_style import TextStyle
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
- @dataclasses.dataclass
37
- class VideoMedia:
38
- resource: Optional[str] = dataclasses.field(default=None)
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
- @dataclasses.dataclass
44
- class VideoConfiguration:
45
- output_driver: Optional[str] = dataclasses.field(default=None)
46
- hardware_decoding_api: Optional[str] = dataclasses.field(default=None)
47
- enable_hardware_acceleration: Optional[bool] = dataclasses.field(default=None)
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
- @dataclasses.dataclass
51
- class VideoSubtitleConfiguration:
52
- src: Optional[str] = dataclasses.field(default=None)
53
- title: Optional[str] = dataclasses.field(default=None)
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
- class Video(ConstrainedControl):
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
- Online docs: https://flet.dev/docs/controls/video
69
- """
70
-
71
- def __init__(
72
- self,
73
- playlist: Optional[List[VideoMedia]] = None,
74
- title: Optional[str] = None,
75
- fit: Optional[ImageFit] = None,
76
- fill_color: Optional[ColorValue] = None,
77
- wakelock: Optional[bool] = None,
78
- autoplay: Optional[bool] = None,
79
- show_controls: Optional[bool] = None,
80
- muted: Optional[bool] = None,
81
- playlist_mode: Optional[PlaylistMode] = None,
82
- shuffle_playlist: Optional[bool] = None,
83
- volume: OptionalNumber = None,
84
- playback_rate: OptionalNumber = None,
85
- alignment: Optional[Alignment] = None,
86
- filter_quality: Optional[FilterQuality] = None,
87
- pause_upon_entering_background_mode: Optional[bool] = None,
88
- resume_upon_entering_foreground_mode: Optional[bool] = None,
89
- aspect_ratio: OptionalNumber = None,
90
- pitch: OptionalNumber = None,
91
- configuration: Optional[VideoConfiguration] = None,
92
- subtitle_configuration: Optional[VideoSubtitleConfiguration] = None,
93
- on_loaded: OptionalControlEventCallable = None,
94
- on_enter_fullscreen: OptionalControlEventCallable = None,
95
- on_exit_fullscreen: OptionalControlEventCallable = None,
96
- on_error: OptionalControlEventCallable = None,
97
- on_completed: OptionalControlEventCallable = None,
98
- on_track_changed: OptionalControlEventCallable = None,
99
- #
100
- # ConstrainedControl
101
- #
102
- ref: Optional[Ref] = None,
103
- key: Optional[str] = None,
104
- width: OptionalNumber = None,
105
- height: OptionalNumber = None,
106
- left: OptionalNumber = None,
107
- top: OptionalNumber = None,
108
- right: OptionalNumber = None,
109
- bottom: OptionalNumber = None,
110
- expand: Union[None, bool, int] = None,
111
- col: Optional[ResponsiveNumber] = None,
112
- opacity: OptionalNumber = None,
113
- rotate: RotateValue = None,
114
- scale: ScaleValue = None,
115
- offset: OffsetValue = None,
116
- animate_opacity: Optional[AnimationValue] = None,
117
- animate_size: Optional[AnimationValue] = None,
118
- animate_position: Optional[AnimationValue] = None,
119
- animate_rotation: Optional[AnimationValue] = None,
120
- animate_scale: Optional[AnimationValue] = None,
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._set_attr_json("alignment", self.__alignment)
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
- self.invoke_method("play")
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
- self.invoke_method("pause")
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
- self.invoke_method("play_or_pause")
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
- self.invoke_method("stop")
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
- self.invoke_method("next")
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
- self.invoke_method("previous")
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, position_milliseconds: int):
218
- self.invoke_method("seek", {"position": str(position_milliseconds)})
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
- assert self.__playlist[media_index], "media_index is out of range"
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.__playlist) + media_index
225
- self.invoke_method("jump_to", {"media_index": str(media_index)})
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.invoke_method(
230
- "playlist_add",
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
- assert self.__playlist[media_index], "index out of range"
241
- self.invoke_method("playlist_remove", {"media_index": str(media_index)})
242
- self.__playlist.pop(media_index)
243
-
244
- def is_playing(self, wait_timeout: Optional[float] = 5) -> bool:
245
- playing = self.invoke_method(
246
- "is_playing",
247
- wait_for_result=True,
248
- wait_timeout=wait_timeout,
249
- )
250
- return playing == "true"
251
-
252
- async def is_playing_async(self, wait_timeout: Optional[float] = 5) -> bool:
253
- playing = await self.invoke_method_async(
254
- "is_playing",
255
- wait_for_result=True,
256
- wait_timeout=wait_timeout,
257
- )
258
- return playing == "true"
259
-
260
- def is_completed(self, wait_timeout: Optional[float] = 5) -> bool:
261
- completed = self.invoke_method(
262
- "is_completed",
263
- wait_for_result=True,
264
- wait_timeout=wait_timeout,
265
- )
266
- return completed == "true"
267
-
268
- async def is_completed_async(self, wait_timeout: Optional[float] = 5) -> bool:
269
- completed = await self.invoke_method_async(
270
- "is_completed",
271
- wait_for_result=True,
272
- wait_timeout=wait_timeout,
273
- )
274
- return completed == "true"
275
-
276
- def get_duration(self, wait_timeout: Optional[float] = 5) -> Optional[int]:
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")