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.
@@ -1,6 +1,3 @@
1
- import 'dart:convert';
2
-
3
- import 'package:collection/collection.dart';
4
1
  import 'package:flet/flet.dart';
5
2
  import 'package:flutter/material.dart';
6
3
  import 'package:media_kit/media_kit.dart';
@@ -9,15 +6,9 @@ import 'package:media_kit_video/media_kit_video.dart';
9
6
  import 'utils/video.dart';
10
7
 
11
8
  class VideoControl extends StatefulWidget {
12
- final Control? parent;
13
9
  final Control control;
14
- final FletControlBackend backend;
15
10
 
16
- const VideoControl(
17
- {super.key,
18
- required this.parent,
19
- required this.control,
20
- required this.backend});
11
+ const VideoControl({super.key, required this.control});
21
12
 
22
13
  @override
23
14
  State<VideoControl> createState() => _VideoControlState();
@@ -25,267 +16,203 @@ class VideoControl extends StatefulWidget {
25
16
 
26
17
  class _VideoControlState extends State<VideoControl> with FletStoreMixin {
27
18
  late final playerConfig = PlayerConfiguration(
28
- title: widget.control.attrString("title", "Flet Video")!,
29
- muted: widget.control.attrBool("muted", false)!,
30
- pitch: widget.control.attrDouble("pitch") != null ? true : false,
31
- ready: () {
32
- if (widget.control.attrBool("onLoaded", false)!) {
33
- widget.backend.triggerControlEvent(widget.control.id, "loaded");
34
- }
35
- },
19
+ title: widget.control.getString("title", "flet-video")!,
20
+ muted: widget.control.getBool("muted", false)!,
21
+ pitch: widget.control.getDouble("pitch") != null ? true : false,
22
+ ready: widget.control.getBool("on_loaded", false)!
23
+ ? () => widget.control.triggerEvent("loaded")
24
+ : null,
36
25
  );
37
26
 
38
- late final Player player = Player(
39
- configuration: playerConfig,
40
- );
27
+ late final Player player = Player(configuration: playerConfig);
41
28
  late final videoControllerConfiguration = parseControllerConfiguration(
42
- widget.control, "configuration", const VideoControllerConfiguration())!;
29
+ widget.control.get("configuration"),
30
+ const VideoControllerConfiguration())!;
43
31
  late final controller =
44
32
  VideoController(player, configuration: videoControllerConfiguration);
45
33
 
46
34
  @override
47
35
  void initState() {
48
36
  super.initState();
49
- player.open(Playlist(parseVideoMedia(widget.control, "playlist")),
50
- play: widget.control.attrBool("autoPlay", false)!);
37
+ widget.control.addInvokeMethodListener(_invokeMethod);
38
+ player.open(Playlist(parseVideoMedias(widget.control.get("playlist"), [])!),
39
+ play: widget.control.getBool("autoplay", false)!);
51
40
  }
52
41
 
53
42
  @override
54
43
  void dispose() {
44
+ widget.control.removeInvokeMethodListener(_invokeMethod);
55
45
  player.dispose();
56
46
  super.dispose();
57
47
  }
58
48
 
59
49
  void _onError(String? message) {
60
- debugPrint("Video onError: $message");
61
- widget.backend
62
- .triggerControlEvent(widget.control.id, "error", message ?? "");
50
+ if (widget.control.getBool("on_error", false)!) {
51
+ widget.control.triggerEvent("error", message);
52
+ }
63
53
  }
64
54
 
65
- void _onCompleted(String? message) {
66
- debugPrint("Video onCompleted: $message");
67
- widget.backend
68
- .triggerControlEvent(widget.control.id, "completed", message ?? "");
69
- }
70
-
71
- void _onTrackChanged(String? message) {
72
- debugPrint("Video onTrackChanged: $message");
73
- widget.backend
74
- .triggerControlEvent(widget.control.id, "track_changed", message ?? "");
55
+ Future<dynamic> _invokeMethod(String name, dynamic args) async {
56
+ debugPrint("Video.$name($args)");
57
+ switch (name) {
58
+ case "play":
59
+ await player.play();
60
+ break;
61
+ case "pause":
62
+ await player.pause();
63
+ break;
64
+ case "play_or_pause":
65
+ await player.playOrPause();
66
+ break;
67
+ case "stop":
68
+ await player.stop();
69
+ player.open(
70
+ Playlist(parseVideoMedias(widget.control.get("playlist"), [])!),
71
+ play: false);
72
+ break;
73
+ case "seek":
74
+ var position = parseDuration(args["position"]);
75
+ if (position != null) await player.seek(position);
76
+ break;
77
+ case "next":
78
+ await player.next();
79
+ break;
80
+ case "previous":
81
+ await player.previous();
82
+ break;
83
+ case "jump_to":
84
+ final mediaIndex = parseInt(args["media_index"]);
85
+ if (mediaIndex != null) await player.jump(mediaIndex);
86
+ break;
87
+ case "playlist_add":
88
+ var media = parseVideoMedia(args["media"]);
89
+ if (media != null) await player.add(media);
90
+ break;
91
+ case "playlist_remove":
92
+ final mediaIndex = parseInt(args["media_index"]);
93
+ if (mediaIndex != null) await player.remove(mediaIndex);
94
+ break;
95
+ case "is_playing":
96
+ return player.state.playing;
97
+ case "is_completed":
98
+ return player.state.completed;
99
+ case "get_duration":
100
+ return player.state.duration;
101
+ case "get_current_position":
102
+ return player.state.position;
103
+ default:
104
+ throw Exception("Unknown Video method: $name");
105
+ }
75
106
  }
76
107
 
77
108
  @override
78
109
  Widget build(BuildContext context) {
79
- debugPrint("Video build: ${widget.control.id}");
80
-
81
- FilterQuality filterQuality = parseFilterQuality(
82
- widget.control.attrString("filterQuality"), FilterQuality.low)!;
83
-
84
- return withPageArgs((context, pageArgs) {
85
- SubtitleTrack? subtitleTrack;
86
- Map<String, dynamic>? subtitleConfiguration = parseSubtitleConfiguration(
87
- Theme.of(context), widget.control, "subtitleConfiguration");
88
- if (subtitleConfiguration?["src"] != null) {
89
- try {
90
- var assetSrc = getAssetSrc(subtitleConfiguration?["src"],
91
- pageArgs.pageUri!, pageArgs.assetsDir);
92
- subtitleTrack = parseSubtitleTrack(
93
- assetSrc,
94
- subtitleConfiguration?["title"],
95
- subtitleConfiguration?["language"]);
96
- } catch (ex) {
97
- _onError(ex.toString());
98
- subtitleTrack = SubtitleTrack.no();
99
- }
110
+ debugPrint("Video XXX build: ${widget.control.id}");
111
+
112
+ var subtitleConfiguration = parseSubtitleConfiguration(
113
+ widget.control.get("subtitle_configuration"),
114
+ Theme.of(context),
115
+ const SubtitleViewConfiguration())!;
116
+ var subtitleTrack =
117
+ parseSubtitleTrack(widget.control.get("subtitle_track"), context);
118
+
119
+ var volume = widget.control.getDouble("volume");
120
+ var pitch = widget.control.getDouble("pitch");
121
+ var playbackRate = widget.control.getDouble("playback_rate");
122
+ var shufflePlaylist = widget.control.getBool("shuffle_playlist");
123
+ var showControls = widget.control.getBool("show_controls", true)!;
124
+ var playlistMode =
125
+ parsePlaylistMode(widget.control.getString("playlist_mode"));
126
+
127
+ // previous values
128
+ final prevVolume = widget.control.getDouble("_volume");
129
+ final prevPitch = widget.control.getDouble("_pitch");
130
+ final prevPlaybackRate = widget.control.getDouble("_playback_rate");
131
+ final prevShufflePlaylist = widget.control.getBool("_shuffle_playlist");
132
+ final PlaylistMode? prevPlaylistMode = widget.control.get("_playlist_mode");
133
+ final SubtitleTrack? prevSubtitleTrack =
134
+ widget.control.get("_subtitleTrack");
135
+
136
+ Video video = Video(
137
+ controller: controller,
138
+ wakelock: widget.control.getBool("wakelock", true)!,
139
+ controls: showControls ? AdaptiveVideoControls : null,
140
+ pauseUponEnteringBackgroundMode:
141
+ widget.control.getBool("pause_upon_entering_background_mode", true)!,
142
+ resumeUponEnteringForegroundMode: widget.control
143
+ .getBool("resume_upon_entering_foreground_mode", false)!,
144
+ alignment: widget.control.getAlignment("alignment", Alignment.center)!,
145
+ fit: widget.control.getBoxFit("fit", BoxFit.contain)!,
146
+ filterQuality:
147
+ widget.control.getFilterQuality("filter_quality", FilterQuality.low)!,
148
+ subtitleViewConfiguration: subtitleConfiguration,
149
+ fill: widget.control.getColor("fill_color", context, Colors.black)!,
150
+ onEnterFullscreen: widget.control.getBool("on_enter_fullscreen", false)!
151
+ ? () async => widget.control.triggerEvent("enter_fullscreen")
152
+ : defaultEnterNativeFullscreen,
153
+ onExitFullscreen: widget.control.getBool("on_exit_fullscreen", false)!
154
+ ? () async => widget.control.triggerEvent("exit_fullscreen")
155
+ : defaultExitNativeFullscreen,
156
+ );
157
+
158
+ () async {
159
+ if (volume != null &&
160
+ volume != prevVolume &&
161
+ volume >= 0 &&
162
+ volume <= 100) {
163
+ widget.control.updateProperties({"_volume": volume}, python: false);
164
+ await player.setVolume(volume);
100
165
  }
101
166
 
102
- SubtitleViewConfiguration? subtitleViewConfiguration =
103
- subtitleConfiguration?["subtitleViewConfiguration"];
104
-
105
- bool onError = widget.control.attrBool("onError", false)!;
106
- bool onCompleted = widget.control.attrBool("onCompleted", false)!;
107
- bool onTrackChanged = widget.control.attrBool("onTrackChanged", false)!;
108
-
109
- double? volume = widget.control.attrDouble("volume");
110
- double? pitch = widget.control.attrDouble("pitch");
111
- double? playbackRate = widget.control.attrDouble("playbackRate");
112
- bool? shufflePlaylist = widget.control.attrBool("shufflePlaylist");
113
- bool? showControls = widget.control.attrBool("showControls", true)!;
114
- PlaylistMode? playlistMode = PlaylistMode.values.firstWhereOrNull((e) =>
115
- e.name.toLowerCase() ==
116
- widget.control.attrString("playlistMode")?.toLowerCase());
117
-
118
- final double? prevVolume = widget.control.state["volume"];
119
- final double? prevPitch = widget.control.state["pitch"];
120
- final double? prevPlaybackRate = widget.control.state["playbackRate"];
121
- final bool? prevShufflePlaylist = widget.control.state["shufflePlaylist"];
122
- final PlaylistMode? prevPlaylistMode =
123
- widget.control.state["playlistMode"];
124
- final SubtitleTrack? prevSubtitleTrack =
125
- widget.control.state["subtitleTrack"];
126
-
127
- Video? video = Video(
128
- controller: controller,
129
- wakelock: widget.control.attrBool("wakelock", true)!,
130
- controls: showControls ? AdaptiveVideoControls : null,
131
- pauseUponEnteringBackgroundMode:
132
- widget.control.attrBool("pauseUponEnteringBackgroundMode", true)!,
133
- resumeUponEnteringForegroundMode:
134
- widget.control.attrBool("resumeUponEnteringForegroundMode", false)!,
135
- alignment:
136
- parseAlignment(widget.control, "alignment", Alignment.center)!,
137
- fit: parseBoxFit(widget.control.attrString("fit"), BoxFit.contain)!,
138
- filterQuality: filterQuality,
139
- subtitleViewConfiguration:
140
- subtitleViewConfiguration ?? const SubtitleViewConfiguration(),
141
- fill: parseColor(Theme.of(context),
142
- widget.control.attrString("fillColor", "")!) ??
143
- const Color(0xFF000000),
144
- onEnterFullscreen: widget.control.attrBool("onEnterFullscreen", false)!
145
- ? () async {
146
- widget.backend.triggerControlEvent(
147
- widget.control.id, "enter_fullscreen", "");
148
- }
149
- : defaultEnterNativeFullscreen,
150
- onExitFullscreen: widget.control.attrBool("onExitFullscreen", false)!
151
- ? () async {
152
- widget.backend.triggerControlEvent(
153
- widget.control.id, "exit_fullscreen", "");
154
- }
155
- : defaultExitNativeFullscreen,
156
- );
157
-
158
- () async {
159
- if (volume != null &&
160
- volume != prevVolume &&
161
- volume >= 0 &&
162
- volume <= 100) {
163
- widget.control.state["volume"] = volume;
164
- debugPrint("Video.setVolume($volume)");
165
- await player.setVolume(volume);
166
- }
167
-
168
- if (pitch != null && pitch != prevPitch) {
169
- widget.control.state["pitch"] = pitch;
170
- debugPrint("Video.setPitch($pitch)");
171
- await player.setPitch(pitch);
172
- }
173
-
174
- if (playbackRate != null && playbackRate != prevPlaybackRate) {
175
- widget.control.state["playbackRate"] = playbackRate;
176
- debugPrint("Video.setPlaybackRate($playbackRate)");
177
- await player.setRate(playbackRate);
178
- }
179
-
180
- if (shufflePlaylist != null && shufflePlaylist != prevShufflePlaylist) {
181
- widget.control.state["shufflePlaylist"] = shufflePlaylist;
182
- debugPrint("Video.setShufflePlaylist($shufflePlaylist)");
183
- await player.setShuffle(shufflePlaylist);
184
- }
167
+ if (pitch != null && pitch != prevPitch) {
168
+ widget.control.updateProperties({"_pitch": pitch}, python: false);
169
+ await player.setPitch(pitch);
170
+ }
185
171
 
186
- if (playlistMode != null && playlistMode != prevPlaylistMode) {
187
- widget.control.state["playlistMode"] = playlistMode;
188
- debugPrint("Video.setPlaylistMode($playlistMode)");
189
- await player.setPlaylistMode(playlistMode);
190
- }
172
+ if (playbackRate != null && playbackRate != prevPlaybackRate) {
173
+ widget.control
174
+ .updateProperties({"_playbackRate": playbackRate}, python: false);
175
+ await player.setRate(playbackRate);
176
+ }
191
177
 
192
- if (subtitleTrack != null && subtitleTrack != prevSubtitleTrack) {
193
- widget.control.state["subtitleTrack"] = subtitleTrack;
194
- debugPrint("Video.setSubtitleTrack($subtitleTrack)");
195
- await player.setSubtitleTrack(subtitleTrack);
196
- }
178
+ if (shufflePlaylist != null && shufflePlaylist != prevShufflePlaylist) {
179
+ widget.control.updateProperties({"_shufflePlaylist": shufflePlaylist},
180
+ python: false);
181
+ await player.setShuffle(shufflePlaylist);
182
+ }
197
183
 
198
- widget.backend.subscribeMethods(widget.control.id,
199
- (methodName, args) async {
200
- switch (methodName) {
201
- case "play":
202
- debugPrint("Video.play($hashCode)");
203
- await player.play();
204
- break;
205
- case "pause":
206
- debugPrint("Video.pause($hashCode)");
207
- await player.pause();
208
- break;
209
- case "play_or_pause":
210
- debugPrint("Video.playOrPause($hashCode)");
211
- await player.playOrPause();
212
- break;
213
- case "stop":
214
- debugPrint("Video.stop($hashCode)");
215
- await player.stop();
216
- player.open(Playlist(parseVideoMedia(widget.control, "playlist")),
217
- play: false);
218
- break;
219
- case "seek":
220
- debugPrint("Video.jump($hashCode)");
221
- await player.seek(Duration(
222
- milliseconds: int.tryParse(args["position"] ?? "") ?? 0));
223
- break;
224
- case "next":
225
- debugPrint("Video.next($hashCode)");
226
- await player.next();
227
- break;
228
- case "previous":
229
- debugPrint("Video.previous($hashCode)");
230
- await player.previous();
231
- break;
232
- case "jump_to":
233
- debugPrint("Video.jump($hashCode)");
234
- await player.jump(parseInt(args["media_index"], 0)!);
235
- break;
236
- case "playlist_add":
237
- debugPrint("Video.add($hashCode)");
238
- Map<String, dynamic> extras =
239
- json.decode(args["extras"]!.replaceAll("'", "\""));
240
- Map<String, String> httpHeaders =
241
- (json.decode(args["http_headers"]!.replaceAll("'", "\""))
242
- as Map)
243
- .map((key, value) =>
244
- MapEntry(key.toString(), value.toString()));
245
- await player.add(Media(args["resource"]!,
246
- extras: extras.isNotEmpty ? extras : null,
247
- httpHeaders: httpHeaders.isNotEmpty ? httpHeaders : null));
248
- break;
249
- case "playlist_remove":
250
- debugPrint("Video.remove($hashCode)");
251
- await player.remove(parseInt(args["media_index"], 0)!);
252
- break;
253
- case "is_playing":
254
- debugPrint("Video.isPlaying($hashCode)");
255
- return player.state.playing.toString();
256
- case "is_completed":
257
- debugPrint("Video.isCompleted($hashCode)");
258
- return player.state.completed.toString();
259
- case "get_duration":
260
- debugPrint("Video.getDuration($hashCode)");
261
- return player.state.duration.inMilliseconds.toString();
262
- case "get_current_position":
263
- debugPrint("Video.getCurrentPosition($hashCode)");
264
- return player.state.position.inMilliseconds.toString();
265
- }
266
- return null;
267
- });
268
- }();
184
+ if (playlistMode != null && playlistMode != prevPlaylistMode) {
185
+ widget.control
186
+ .updateProperties({"_playlistMode": playlistMode}, python: false);
187
+ await player.setPlaylistMode(playlistMode);
188
+ }
269
189
 
270
- player.stream.error.listen((event) {
271
- if (onError) {
272
- _onError(event.toString());
273
- }
274
- });
190
+ if (subtitleTrack != null && subtitleTrack != prevSubtitleTrack) {
191
+ widget.control
192
+ .updateProperties({"_subtitleTrack": subtitleTrack}, python: false);
193
+ await player.setSubtitleTrack(subtitleTrack);
194
+ }
195
+ }();
275
196
 
276
- player.stream.completed.listen((event) {
277
- if (onCompleted) {
278
- _onCompleted(event.toString());
279
- }
280
- });
197
+ // listen to errors
198
+ player.stream.error.listen((event) {
199
+ _onError(event);
200
+ });
281
201
 
282
- player.stream.playlist.listen((event) {
283
- if (onTrackChanged) {
284
- _onTrackChanged(event.index.toString());
285
- }
286
- });
202
+ // listen to completion
203
+ player.stream.completed.listen((event) {
204
+ if (widget.control.getBool("on_complete", false)!) {
205
+ widget.control.triggerEvent("complete", event);
206
+ }
207
+ });
287
208
 
288
- return constrainedControl(context, video, widget.parent, widget.control);
209
+ // listen to track changes
210
+ player.stream.playlist.listen((event) {
211
+ if (widget.control.getBool("on_track_change", false)!) {
212
+ widget.control.triggerEvent("track_change", event.index);
213
+ }
289
214
  });
215
+
216
+ return ConstrainedControl(control: widget.control, child: video);
290
217
  }
291
- }
218
+ }