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
|
@@ -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.
|
|
29
|
-
muted: widget.control.
|
|
30
|
-
pitch: widget.control.
|
|
31
|
-
ready: ()
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
if (widget.control.getBool("on_error", false)!) {
|
|
51
|
+
widget.control.triggerEvent("error", message);
|
|
52
|
+
}
|
|
63
53
|
}
|
|
64
54
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
82
|
-
widget.control.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
});
|
|
197
|
+
// listen to errors
|
|
198
|
+
player.stream.error.listen((event) {
|
|
199
|
+
_onError(event);
|
|
200
|
+
});
|
|
281
201
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
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
|
+
}
|