flet-audio 1.0.4__tar.gz → 1.1.0.dev0__tar.gz
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_audio-1.0.4/src/flet_audio.egg-info → flet_audio-1.1.0.dev0}/PKG-INFO +2 -2
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/pyproject.toml +2 -2
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio/audio.py +5 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0/src/flet_audio.egg-info}/PKG-INFO +2 -2
- flet_audio-1.1.0.dev0/src/flet_audio.egg-info/requires.txt +1 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flutter/flet_audio/lib/src/audio.dart +97 -28
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flutter/flet_audio/pubspec.yaml +2 -0
- flet_audio-1.0.4/src/flet_audio.egg-info/requires.txt +0 -1
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/LICENSE +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/README.md +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/setup.cfg +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio/__init__.py +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio/types.py +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio.egg-info/SOURCES.txt +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio.egg-info/dependency_links.txt +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flet_audio.egg-info/top_level.txt +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flutter/flet_audio/lib/flet_audio.dart +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flutter/flet_audio/lib/src/extension.dart +0 -0
- {flet_audio-1.0.4 → flet_audio-1.1.0.dev0}/src/flutter/flet_audio/lib/src/utils/audio.dart +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-audio
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.1.0.dev0
|
|
4
4
|
Summary: Provides audio integration and playback in Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==1.0.
|
|
14
|
+
Requires-Dist: flet==1.1.0.dev0
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-audio
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "flet-audio"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.1.0.dev0"
|
|
4
4
|
description = "Provides audio integration and playback in Flet apps."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Flet contributors", email = "hello@flet.dev" }]
|
|
7
7
|
license = "Apache-2.0"
|
|
8
8
|
requires-python = ">=3.10"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"flet==1.0.
|
|
10
|
+
"flet==1.1.0.dev0",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[project.urls]
|
|
@@ -24,6 +24,11 @@ class Audio(ft.Service):
|
|
|
24
24
|
- A base64 string;
|
|
25
25
|
- Raw bytes.
|
|
26
26
|
|
|
27
|
+
It can be left unset and assigned later, e.g. once the user picks a file.
|
|
28
|
+
While it is unset, :meth:`~flet_audio.Audio.play`,
|
|
29
|
+
:meth:`~flet_audio.Audio.resume` and :meth:`~flet_audio.Audio.seek` raise an
|
|
30
|
+
error.
|
|
31
|
+
|
|
27
32
|
Note:
|
|
28
33
|
[Here](https://github.com/bluefireteam/audioplayers/blob/main/troubleshooting.md#supported-formats--encodings)
|
|
29
34
|
is a list of supported audio formats.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flet-audio
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.1.0.dev0
|
|
4
4
|
Summary: Provides audio integration and playback in Flet apps.
|
|
5
5
|
Author-email: Flet contributors <hello@flet.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -11,7 +11,7 @@ Project-URL: Issues, https://github.com/flet-dev/flet/issues
|
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
-
Requires-Dist: flet==1.0.
|
|
14
|
+
Requires-Dist: flet==1.1.0.dev0
|
|
15
15
|
Dynamic: license-file
|
|
16
16
|
|
|
17
17
|
# flet-audio
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flet==1.1.0.dev0
|
|
@@ -24,6 +24,12 @@ class AudioService extends FletService {
|
|
|
24
24
|
double? _balance;
|
|
25
25
|
double? _playbackRate;
|
|
26
26
|
|
|
27
|
+
/// Incremented whenever the source is set, cleared or the service is
|
|
28
|
+
/// disposed, so that an [update] that is still preparing an earlier source
|
|
29
|
+
/// can tell that it was superseded.
|
|
30
|
+
int _sourceVersion = 0;
|
|
31
|
+
Future<void> _sourcePreparation = Future<void>.value();
|
|
32
|
+
|
|
27
33
|
@override
|
|
28
34
|
void init() {
|
|
29
35
|
super.init();
|
|
@@ -61,44 +67,75 @@ class AudioService extends FletService {
|
|
|
61
67
|
update();
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
/// Applies the control's properties to [player].
|
|
71
|
+
///
|
|
72
|
+
/// A missing or empty `src` means there is no source yet, e.g. until the
|
|
73
|
+
/// user picks a file: the other properties are still applied, and playback
|
|
74
|
+
/// of a source set earlier stops.
|
|
75
|
+
///
|
|
76
|
+
/// The player is paused rather than released: the web player keeps the URL
|
|
77
|
+
/// of a released source and skips loading it again, so setting the same
|
|
78
|
+
/// `src` again would leave nothing to play.
|
|
79
|
+
///
|
|
80
|
+
/// When `src` changes again while a source is still being prepared, the
|
|
81
|
+
/// earlier preparation neither triggers `loaded` nor starts autoplay.
|
|
82
|
+
///
|
|
83
|
+
/// Volume, balance and playback rate are applied again after a new source
|
|
84
|
+
/// is loaded, and before `loaded` is triggered, since loading can reset
|
|
85
|
+
/// them: a web media element resets its playback rate when it loads.
|
|
86
|
+
///
|
|
87
|
+
/// Settings, `autoplay` included, are read from the control when they are
|
|
88
|
+
/// applied, not when [update] is called, so an update that finishes loading
|
|
89
|
+
/// a source after a later update never applies outdated values.
|
|
64
90
|
@override
|
|
65
91
|
void update() {
|
|
66
92
|
debugPrint("Audio(${control.id}).update: ${control.properties}");
|
|
67
93
|
|
|
68
|
-
final resolvedSrc = control.
|
|
94
|
+
final resolvedSrc = control.get("src") != null
|
|
95
|
+
? control.getSrc("src")
|
|
96
|
+
: const ResolvedAssetSource();
|
|
69
97
|
if (resolvedSrc.error != null) {
|
|
70
98
|
throw Exception("Audio src decode error: ${resolvedSrc.error}");
|
|
71
99
|
}
|
|
72
|
-
if (resolvedSrc.isEmpty) {
|
|
73
|
-
throw Exception("Audio must have \"src\" specified.");
|
|
74
|
-
}
|
|
75
|
-
var autoplay = control.getBool("autoplay", false)!;
|
|
76
|
-
var volume = control.getDouble("volume", 1.0)!;
|
|
77
|
-
var balance = control.getDouble("balance", 0.0)!;
|
|
78
|
-
var playbackRate = control.getDouble("playback_rate", 1)!;
|
|
79
|
-
var releaseMode = parseReleaseMode(control.getString("release_mode"));
|
|
80
|
-
|
|
81
100
|
() async {
|
|
82
101
|
bool srcChanged = false;
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
if (resolvedSrc.isEmpty) {
|
|
103
|
+
// no source
|
|
104
|
+
if (_hasSource) {
|
|
105
|
+
_src = null;
|
|
106
|
+
_srcBytes = null;
|
|
107
|
+
_sourceVersion++;
|
|
108
|
+
await player.pause();
|
|
109
|
+
}
|
|
110
|
+
} else if (resolvedSrc.uri != null && resolvedSrc.uri != _src) {
|
|
111
|
+
// URL or file
|
|
85
112
|
_src = resolvedSrc.uri;
|
|
86
113
|
_srcBytes = null;
|
|
87
114
|
srcChanged = true;
|
|
88
|
-
await _applySource();
|
|
89
115
|
} else if (resolvedSrc.bytes != null &&
|
|
90
116
|
(_srcBytes == null || !listEquals(_srcBytes, resolvedSrc.bytes))) {
|
|
91
117
|
// bytes
|
|
92
118
|
_srcBytes = resolvedSrc.bytes;
|
|
93
119
|
_src = null;
|
|
94
120
|
srcChanged = true;
|
|
95
|
-
await _applySource();
|
|
96
121
|
}
|
|
97
122
|
|
|
123
|
+
final sourceVersion = srcChanged ? ++_sourceVersion : _sourceVersion;
|
|
98
124
|
if (srcChanged) {
|
|
99
|
-
|
|
125
|
+
try {
|
|
126
|
+
await _applySource();
|
|
127
|
+
} catch (_) {
|
|
128
|
+
if (sourceVersion != _sourceVersion) return;
|
|
129
|
+
rethrow;
|
|
130
|
+
}
|
|
131
|
+
if (sourceVersion != _sourceVersion) return;
|
|
132
|
+
_volume = null;
|
|
133
|
+
_balance = null;
|
|
134
|
+
_playbackRate = null;
|
|
100
135
|
}
|
|
101
136
|
|
|
137
|
+
final releaseMode = parseReleaseMode(control.getString("release_mode"));
|
|
138
|
+
|
|
102
139
|
// releaseMode
|
|
103
140
|
if (releaseMode != null && releaseMode != _releaseMode) {
|
|
104
141
|
_releaseMode = releaseMode;
|
|
@@ -106,51 +143,82 @@ class AudioService extends FletService {
|
|
|
106
143
|
}
|
|
107
144
|
|
|
108
145
|
// volume
|
|
146
|
+
final volume = control.getDouble("volume", 1.0)!;
|
|
109
147
|
if (volume != _volume && volume >= 0 && volume <= 1) {
|
|
110
148
|
_volume = volume;
|
|
111
149
|
await player.setVolume(volume);
|
|
112
150
|
}
|
|
113
151
|
|
|
114
152
|
// playbackRate
|
|
153
|
+
final playbackRate = control.getDouble("playback_rate", 1)!;
|
|
115
154
|
if (playbackRate != _playbackRate) {
|
|
116
155
|
_playbackRate = playbackRate;
|
|
117
156
|
await player.setPlaybackRate(playbackRate);
|
|
118
157
|
}
|
|
119
158
|
|
|
120
159
|
// balance
|
|
160
|
+
final balance = control.getDouble("balance", 0.0)!;
|
|
121
161
|
if (!kIsWeb && balance != _balance && balance >= -1 && balance <= 1) {
|
|
122
162
|
_balance = balance;
|
|
123
163
|
await player.setBalance(balance);
|
|
124
164
|
}
|
|
125
165
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
166
|
+
if (srcChanged && sourceVersion == _sourceVersion) {
|
|
167
|
+
control.triggerEvent("loaded");
|
|
168
|
+
if (control.getBool("autoplay", false)!) {
|
|
169
|
+
await player.resume();
|
|
170
|
+
}
|
|
129
171
|
}
|
|
130
172
|
}();
|
|
131
173
|
}
|
|
132
174
|
|
|
175
|
+
/// Whether a source is set, in [_src] or [_srcBytes].
|
|
176
|
+
bool get _hasSource => _src != null || _srcBytes != null;
|
|
177
|
+
|
|
133
178
|
/// Pushes the currently tracked source ([_src] or [_srcBytes]) to the native
|
|
134
179
|
/// [player], preparing it for playback.
|
|
135
180
|
///
|
|
136
181
|
/// Used both when the source changes (from [update]) and to re-prepare
|
|
137
182
|
/// playback after the player has reached [PlayerState.completed] under
|
|
138
183
|
/// [ReleaseMode.release], where the native source has already been freed.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
184
|
+
/// Preparations run in order because setting a byte source may first write
|
|
185
|
+
/// a temporary file. A superseded preparation must finish before the latest
|
|
186
|
+
/// source is installed, otherwise its delayed write could replace that source.
|
|
187
|
+
Future<void> _applySource() {
|
|
188
|
+
final src = _src;
|
|
189
|
+
final srcBytes = _srcBytes;
|
|
190
|
+
final sourceVersion = _sourceVersion;
|
|
191
|
+
final preparation = _sourcePreparation.then((_) async {
|
|
192
|
+
if (sourceVersion != _sourceVersion) return;
|
|
193
|
+
if (src != null) {
|
|
194
|
+
final assetSrc = control.backend.getAssetSource(src);
|
|
195
|
+
if (assetSrc.isFile) {
|
|
196
|
+
await player.setSourceDeviceFile(assetSrc.path);
|
|
197
|
+
} else {
|
|
198
|
+
await player.setSourceUrl(assetSrc.path);
|
|
199
|
+
}
|
|
200
|
+
} else if (srcBytes != null) {
|
|
201
|
+
await player.setSourceBytes(srcBytes);
|
|
146
202
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
203
|
+
});
|
|
204
|
+
// Keep the queue usable after a failed load. The caller still receives the
|
|
205
|
+
// original error and decides whether this preparation was superseded.
|
|
206
|
+
_sourcePreparation = preparation.then<void>((_) {},
|
|
207
|
+
onError: (Object error, StackTrace stackTrace) {});
|
|
208
|
+
return preparation;
|
|
150
209
|
}
|
|
151
210
|
|
|
211
|
+
/// Handles a method call from Python.
|
|
212
|
+
///
|
|
213
|
+
/// `play`, `resume` and `seek` throw while no source is set. The native
|
|
214
|
+
/// players don't reject them: a seek never reports completion, so the call
|
|
215
|
+
/// would only fail after timing out, and the web and Linux players remember
|
|
216
|
+
/// a `resume` and start playing as soon as a source is set.
|
|
152
217
|
Future<dynamic> _invokeMethod(String name, dynamic args) async {
|
|
153
218
|
debugPrint("Audio.$name($args)");
|
|
219
|
+
if (!_hasSource && const ["play", "resume", "seek"].contains(name)) {
|
|
220
|
+
throw Exception("Audio must have \"src\" specified to $name.");
|
|
221
|
+
}
|
|
154
222
|
switch (name) {
|
|
155
223
|
case "play":
|
|
156
224
|
final position = parseDuration(args["position"]);
|
|
@@ -203,6 +271,7 @@ class AudioService extends FletService {
|
|
|
203
271
|
@override
|
|
204
272
|
void dispose() {
|
|
205
273
|
debugPrint("Audio(${control.id}).dispose()");
|
|
274
|
+
_sourceVersion++;
|
|
206
275
|
control.removeInvokeMethodListener(_invokeMethod);
|
|
207
276
|
_onDurationChangedSubscription?.cancel();
|
|
208
277
|
_onStateChangedSubscription?.cancel();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
flet==1.0.4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|