flet-video 0.0.1__py3-none-any.whl → 0.1.0.dev17__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 +7 -1
- flet_video/video.py +552 -0
- flet_video-0.1.0.dev17.dist-info/METADATA +166 -0
- flet_video-0.1.0.dev17.dist-info/RECORD +15 -0
- {flet_video-0.0.1.dist-info → flet_video-0.1.0.dev17.dist-info}/WHEEL +2 -1
- flet_video-0.1.0.dev17.dist-info/top_level.txt +2 -0
- flutter/flet_video/CHANGELOG.md +3 -0
- flutter/flet_video/LICENSE +201 -0
- flutter/flet_video/README.md +3 -0
- flutter/flet_video/analysis_options.yaml +4 -0
- flutter/flet_video/lib/flet_video.dart +3 -0
- flutter/flet_video/lib/src/create_control.dart +18 -0
- flutter/flet_video/lib/src/utils/video.dart +121 -0
- flutter/flet_video/lib/src/video.dart +291 -0
- flutter/flet_video/pubspec.yaml +20 -0
- .DS_Store +0 -0
- flet_video/.DS_Store +0 -0
- flet_video-0.0.1.dist-info/.DS_Store +0 -0
- flet_video-0.0.1.dist-info/METADATA +0 -14
- flet_video-0.0.1.dist-info/RECORD +0 -7
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import 'dart:convert';
|
|
2
|
+
|
|
3
|
+
import 'package:collection/collection.dart';
|
|
4
|
+
import 'package:flet/flet.dart';
|
|
5
|
+
import 'package:flutter/material.dart';
|
|
6
|
+
import 'package:media_kit/media_kit.dart';
|
|
7
|
+
import 'package:media_kit_video/media_kit_video.dart';
|
|
8
|
+
|
|
9
|
+
import 'utils/video.dart';
|
|
10
|
+
|
|
11
|
+
class VideoControl extends StatefulWidget {
|
|
12
|
+
final Control? parent;
|
|
13
|
+
final Control control;
|
|
14
|
+
final FletControlBackend backend;
|
|
15
|
+
|
|
16
|
+
const VideoControl(
|
|
17
|
+
{super.key,
|
|
18
|
+
required this.parent,
|
|
19
|
+
required this.control,
|
|
20
|
+
required this.backend});
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
State<VideoControl> createState() => _VideoControlState();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class _VideoControlState extends State<VideoControl> with FletStoreMixin {
|
|
27
|
+
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
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
late final Player player = Player(
|
|
39
|
+
configuration: playerConfig,
|
|
40
|
+
);
|
|
41
|
+
late final videoControllerConfiguration = parseControllerConfiguration(
|
|
42
|
+
widget.control, "configuration", const VideoControllerConfiguration())!;
|
|
43
|
+
late final controller =
|
|
44
|
+
VideoController(player, configuration: videoControllerConfiguration);
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
void initState() {
|
|
48
|
+
super.initState();
|
|
49
|
+
player.open(Playlist(parseVideoMedia(widget.control, "playlist")),
|
|
50
|
+
play: widget.control.attrBool("autoPlay", false)!);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
void dispose() {
|
|
55
|
+
player.dispose();
|
|
56
|
+
super.dispose();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void _onError(String? message) {
|
|
60
|
+
debugPrint("Video onError: $message");
|
|
61
|
+
widget.backend
|
|
62
|
+
.triggerControlEvent(widget.control.id, "error", message ?? "");
|
|
63
|
+
}
|
|
64
|
+
|
|
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 ?? "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@override
|
|
78
|
+
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
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
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
|
+
}
|
|
185
|
+
|
|
186
|
+
if (playlistMode != null && playlistMode != prevPlaylistMode) {
|
|
187
|
+
widget.control.state["playlistMode"] = playlistMode;
|
|
188
|
+
debugPrint("Video.setPlaylistMode($playlistMode)");
|
|
189
|
+
await player.setPlaylistMode(playlistMode);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (subtitleTrack != null && subtitleTrack != prevSubtitleTrack) {
|
|
193
|
+
widget.control.state["subtitleTrack"] = subtitleTrack;
|
|
194
|
+
debugPrint("Video.setSubtitleTrack($subtitleTrack)");
|
|
195
|
+
await player.setSubtitleTrack(subtitleTrack);
|
|
196
|
+
}
|
|
197
|
+
|
|
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
|
+
}();
|
|
269
|
+
|
|
270
|
+
player.stream.error.listen((event) {
|
|
271
|
+
if (onError) {
|
|
272
|
+
_onError(event.toString());
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
player.stream.completed.listen((event) {
|
|
277
|
+
if (onCompleted) {
|
|
278
|
+
_onCompleted(event.toString());
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
player.stream.playlist.listen((event) {
|
|
283
|
+
if (onTrackChanged) {
|
|
284
|
+
_onTrackChanged(event.index.toString());
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return constrainedControl(context, video, widget.parent, widget.control);
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: flet_video
|
|
2
|
+
description: Flet Video control
|
|
3
|
+
homepage: https://flet.dev
|
|
4
|
+
repository: https://github.com/flet-dev/flet/packages/flet_video
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
environment:
|
|
7
|
+
sdk: '>=3.2.3 <4.0.0'
|
|
8
|
+
flutter: '>=1.17.0'
|
|
9
|
+
dependencies:
|
|
10
|
+
flutter:
|
|
11
|
+
sdk: flutter
|
|
12
|
+
collection: ^1.16.0
|
|
13
|
+
media_kit: ^1.1.10
|
|
14
|
+
media_kit_video: ^1.2.4
|
|
15
|
+
media_kit_libs_video: ^1.0.4
|
|
16
|
+
flet: ^0.25.1
|
|
17
|
+
dev_dependencies:
|
|
18
|
+
flutter_test:
|
|
19
|
+
sdk: flutter
|
|
20
|
+
flutter_lints: ^2.0.0
|
.DS_Store
DELETED
|
Binary file
|
flet_video/.DS_Store
DELETED
|
Binary file
|
|
Binary file
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: flet-video
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: Flet Video control
|
|
5
|
-
Author: Appveyor Systems Inc.
|
|
6
|
-
Author-email: hello@flet.dev
|
|
7
|
-
Requires-Python: >=3.8,<4.0
|
|
8
|
-
Project-URL: homepage, https://flet.dev
|
|
9
|
-
Download-URL:
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
|
|
12
|
-
# Flet Video
|
|
13
|
-
|
|
14
|
-
Video control for Flet.
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
.DS_Store,sha256=UGxWgJfmLLyazGK5A-uGE3FwdljfvRyUi6SV7VdJU-E,6148
|
|
2
|
-
flet_video/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
|
|
3
|
-
flet_video/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
4
|
-
flet_video-0.0.1.dist-info/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
|
|
5
|
-
flet_video-0.0.1.dist-info/METADATA,sha256=cDizSAGhaFaLKbWeIXOSOcg6R15EzVJRpWPOBIl54v4,301
|
|
6
|
-
flet_video-0.0.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
7
|
-
flet_video-0.0.1.dist-info/RECORD,,
|