@capgo/native-audio 7.1.8 → 7.3.0
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.
- package/README.md +56 -8
- package/android/build.gradle +14 -5
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +276 -133
- package/android/src/main/java/ee/forgr/audio/AudioCompletionListener.java +1 -1
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +168 -182
- package/android/src/main/java/ee/forgr/audio/Constant.java +20 -21
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +596 -506
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +599 -166
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +499 -0
- package/dist/docs.json +96 -3
- package/dist/esm/definitions.d.ts +33 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +4 -3
- package/dist/esm/web.js +23 -20
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +22 -19
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +22 -19
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/AudioAsset.swift +200 -94
- package/ios/Plugin/Plugin.swift +55 -49
- package/ios/Plugin/RemoteAudioAsset.swift +217 -62
- package/package.json +18 -20
@@ -0,0 +1,499 @@
|
|
1
|
+
package ee.forgr.audio;
|
2
|
+
|
3
|
+
import android.net.Uri;
|
4
|
+
import android.os.Handler;
|
5
|
+
import android.os.Looper;
|
6
|
+
import android.util.Log;
|
7
|
+
import androidx.media3.common.MediaItem;
|
8
|
+
import androidx.media3.common.PlaybackException;
|
9
|
+
import androidx.media3.common.PlaybackParameters;
|
10
|
+
import androidx.media3.common.Player;
|
11
|
+
import androidx.media3.common.util.UnstableApi;
|
12
|
+
import androidx.media3.datasource.DefaultHttpDataSource;
|
13
|
+
import androidx.media3.exoplayer.DefaultLivePlaybackSpeedControl;
|
14
|
+
import androidx.media3.exoplayer.DefaultLoadControl;
|
15
|
+
import androidx.media3.exoplayer.ExoPlayer;
|
16
|
+
import androidx.media3.exoplayer.hls.HlsMediaSource;
|
17
|
+
|
18
|
+
@UnstableApi
|
19
|
+
public class StreamAudioAsset extends AudioAsset {
|
20
|
+
|
21
|
+
private static final String TAG = "StreamAudioAsset";
|
22
|
+
private ExoPlayer player;
|
23
|
+
private final Uri uri;
|
24
|
+
private float volume;
|
25
|
+
private boolean isPrepared = false;
|
26
|
+
private final float initialVolume;
|
27
|
+
private static final float FADE_STEP = 0.05f;
|
28
|
+
private static final int FADE_DELAY_MS = 80; // 80ms between steps
|
29
|
+
private static final long LIVE_OFFSET_MS = 5000; // 5 seconds behind live
|
30
|
+
|
31
|
+
public StreamAudioAsset(NativeAudio owner, String assetId, Uri uri, float volume) throws Exception {
|
32
|
+
super(owner, assetId, null, 0, volume);
|
33
|
+
this.uri = uri;
|
34
|
+
this.volume = volume;
|
35
|
+
this.initialVolume = volume;
|
36
|
+
|
37
|
+
createPlayer();
|
38
|
+
}
|
39
|
+
|
40
|
+
private void createPlayer() {
|
41
|
+
// Adjust buffer settings for smoother playback
|
42
|
+
DefaultLoadControl loadControl = new DefaultLoadControl.Builder()
|
43
|
+
.setBufferDurationsMs(
|
44
|
+
60000, // Increase min buffer to 60s
|
45
|
+
180000, // Increase max buffer to 180s
|
46
|
+
5000, // Increase buffer for playback
|
47
|
+
10000 // Increase buffer to start playback
|
48
|
+
)
|
49
|
+
.setPrioritizeTimeOverSizeThresholds(true)
|
50
|
+
.setBackBuffer(60000, true) // Increase back buffer
|
51
|
+
.build();
|
52
|
+
|
53
|
+
player = new ExoPlayer.Builder(owner.getContext())
|
54
|
+
.setLoadControl(loadControl)
|
55
|
+
.setLivePlaybackSpeedControl(
|
56
|
+
new DefaultLivePlaybackSpeedControl.Builder()
|
57
|
+
.setFallbackMaxPlaybackSpeed(1.04f)
|
58
|
+
.setMaxLiveOffsetErrorMsForUnitSpeed(LIVE_OFFSET_MS)
|
59
|
+
.build()
|
60
|
+
)
|
61
|
+
.build();
|
62
|
+
|
63
|
+
player.setVolume(volume);
|
64
|
+
initializePlayer();
|
65
|
+
}
|
66
|
+
|
67
|
+
private void initializePlayer() {
|
68
|
+
Log.d(TAG, "Initializing stream player with volume: " + volume);
|
69
|
+
|
70
|
+
// Configure HLS source with better settings for live streaming
|
71
|
+
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
|
72
|
+
.setAllowCrossProtocolRedirects(true)
|
73
|
+
.setConnectTimeoutMs(15000)
|
74
|
+
.setReadTimeoutMs(15000)
|
75
|
+
.setUserAgent("ExoPlayer");
|
76
|
+
|
77
|
+
HlsMediaSource mediaSource = new HlsMediaSource.Factory(httpDataSourceFactory)
|
78
|
+
.setAllowChunklessPreparation(true)
|
79
|
+
.setTimestampAdjusterInitializationTimeoutMs(LIVE_OFFSET_MS) // 30 seconds timeout
|
80
|
+
.createMediaSource(MediaItem.fromUri(uri));
|
81
|
+
|
82
|
+
player.setMediaSource(mediaSource);
|
83
|
+
player.setVolume(volume);
|
84
|
+
player.prepare();
|
85
|
+
|
86
|
+
player.addListener(
|
87
|
+
new Player.Listener() {
|
88
|
+
@Override
|
89
|
+
public void onPlaybackStateChanged(int state) {
|
90
|
+
Log.d(TAG, "Stream state changed to: " + getStateString(state));
|
91
|
+
if (state == Player.STATE_READY && !isPrepared) {
|
92
|
+
isPrepared = true;
|
93
|
+
if (player.isCurrentMediaItemLive()) {
|
94
|
+
player.seekToDefaultPosition();
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
@Override
|
100
|
+
public void onIsLoadingChanged(boolean isLoading) {
|
101
|
+
Log.d(TAG, "Loading state changed: " + isLoading);
|
102
|
+
}
|
103
|
+
|
104
|
+
@Override
|
105
|
+
public void onIsPlayingChanged(boolean isPlaying) {
|
106
|
+
Log.d(TAG, "Playing state changed: " + isPlaying);
|
107
|
+
}
|
108
|
+
|
109
|
+
@Override
|
110
|
+
public void onPlayerError(PlaybackException error) {
|
111
|
+
Log.e(TAG, "Player error: " + error.getMessage());
|
112
|
+
isPrepared = false;
|
113
|
+
// Try to recover by recreating the player
|
114
|
+
owner
|
115
|
+
.getActivity()
|
116
|
+
.runOnUiThread(() -> {
|
117
|
+
player.release();
|
118
|
+
createPlayer();
|
119
|
+
});
|
120
|
+
}
|
121
|
+
}
|
122
|
+
);
|
123
|
+
}
|
124
|
+
|
125
|
+
private String getStateString(int state) {
|
126
|
+
switch (state) {
|
127
|
+
case Player.STATE_IDLE:
|
128
|
+
return "IDLE";
|
129
|
+
case Player.STATE_BUFFERING:
|
130
|
+
return "BUFFERING";
|
131
|
+
case Player.STATE_READY:
|
132
|
+
return "READY";
|
133
|
+
case Player.STATE_ENDED:
|
134
|
+
return "ENDED";
|
135
|
+
default:
|
136
|
+
return "UNKNOWN(" + state + ")";
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
@Override
|
141
|
+
public void play(Double time) throws Exception {
|
142
|
+
Log.d(TAG, "Play called with time: " + time + ", isPrepared: " + isPrepared);
|
143
|
+
owner
|
144
|
+
.getActivity()
|
145
|
+
.runOnUiThread(() -> {
|
146
|
+
if (!isPrepared) {
|
147
|
+
// If not prepared, wait for preparation
|
148
|
+
player.addListener(
|
149
|
+
new Player.Listener() {
|
150
|
+
@Override
|
151
|
+
public void onPlaybackStateChanged(int state) {
|
152
|
+
Log.d(TAG, "Play-wait state changed to: " + getStateString(state));
|
153
|
+
if (state == Player.STATE_READY) {
|
154
|
+
startPlayback(time);
|
155
|
+
player.removeListener(this);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
}
|
159
|
+
);
|
160
|
+
} else {
|
161
|
+
startPlayback(time);
|
162
|
+
}
|
163
|
+
});
|
164
|
+
}
|
165
|
+
|
166
|
+
private void startPlayback(Double time) {
|
167
|
+
Log.d(TAG, "Starting playback with time: " + time);
|
168
|
+
if (time != null) {
|
169
|
+
player.seekTo(Math.round(time * 1000));
|
170
|
+
} else if (player.isCurrentMediaItemLive()) {
|
171
|
+
player.seekToDefaultPosition();
|
172
|
+
}
|
173
|
+
player.setPlaybackParameters(new PlaybackParameters(1.0f));
|
174
|
+
player.setVolume(volume);
|
175
|
+
player.setPlayWhenReady(true);
|
176
|
+
}
|
177
|
+
|
178
|
+
@Override
|
179
|
+
public boolean pause() throws Exception {
|
180
|
+
final boolean[] wasPlaying = { false };
|
181
|
+
owner
|
182
|
+
.getActivity()
|
183
|
+
.runOnUiThread(() -> {
|
184
|
+
if (player.isPlaying()) {
|
185
|
+
player.setPlayWhenReady(false);
|
186
|
+
wasPlaying[0] = true;
|
187
|
+
}
|
188
|
+
});
|
189
|
+
return wasPlaying[0];
|
190
|
+
}
|
191
|
+
|
192
|
+
@Override
|
193
|
+
public void resume() throws Exception {
|
194
|
+
owner
|
195
|
+
.getActivity()
|
196
|
+
.runOnUiThread(() -> {
|
197
|
+
player.setPlayWhenReady(true);
|
198
|
+
});
|
199
|
+
}
|
200
|
+
|
201
|
+
@Override
|
202
|
+
public void stop() throws Exception {
|
203
|
+
owner
|
204
|
+
.getActivity()
|
205
|
+
.runOnUiThread(() -> {
|
206
|
+
// First stop playback
|
207
|
+
player.stop();
|
208
|
+
// Reset player state
|
209
|
+
player.clearMediaItems();
|
210
|
+
isPrepared = false;
|
211
|
+
|
212
|
+
// Create new media source
|
213
|
+
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
|
214
|
+
.setAllowCrossProtocolRedirects(true)
|
215
|
+
.setConnectTimeoutMs(15000)
|
216
|
+
.setReadTimeoutMs(15000)
|
217
|
+
.setUserAgent("ExoPlayer");
|
218
|
+
|
219
|
+
HlsMediaSource mediaSource = new HlsMediaSource.Factory(httpDataSourceFactory)
|
220
|
+
.setAllowChunklessPreparation(true)
|
221
|
+
.setTimestampAdjusterInitializationTimeoutMs(LIVE_OFFSET_MS)
|
222
|
+
.createMediaSource(MediaItem.fromUri(uri));
|
223
|
+
|
224
|
+
// Set new media source and prepare
|
225
|
+
player.setMediaSource(mediaSource);
|
226
|
+
player.prepare();
|
227
|
+
|
228
|
+
// Add listener for preparation completion
|
229
|
+
player.addListener(
|
230
|
+
new Player.Listener() {
|
231
|
+
@Override
|
232
|
+
public void onPlaybackStateChanged(int state) {
|
233
|
+
Log.d(TAG, "Stop-reinit state changed to: " + getStateString(state));
|
234
|
+
if (state == Player.STATE_READY) {
|
235
|
+
isPrepared = true;
|
236
|
+
player.removeListener(this);
|
237
|
+
} else if (state == Player.STATE_IDLE) {
|
238
|
+
// Retry preparation if it fails
|
239
|
+
player.prepare();
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
);
|
244
|
+
});
|
245
|
+
}
|
246
|
+
|
247
|
+
@Override
|
248
|
+
public void loop() throws Exception {
|
249
|
+
owner
|
250
|
+
.getActivity()
|
251
|
+
.runOnUiThread(() -> {
|
252
|
+
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
253
|
+
player.setPlayWhenReady(true);
|
254
|
+
});
|
255
|
+
}
|
256
|
+
|
257
|
+
@Override
|
258
|
+
public void unload() throws Exception {
|
259
|
+
owner
|
260
|
+
.getActivity()
|
261
|
+
.runOnUiThread(() -> {
|
262
|
+
player.stop();
|
263
|
+
player.clearMediaItems();
|
264
|
+
player.release();
|
265
|
+
isPrepared = false;
|
266
|
+
});
|
267
|
+
}
|
268
|
+
|
269
|
+
@Override
|
270
|
+
public void setVolume(float volume) throws Exception {
|
271
|
+
this.volume = volume;
|
272
|
+
owner
|
273
|
+
.getActivity()
|
274
|
+
.runOnUiThread(() -> {
|
275
|
+
Log.d(TAG, "Setting volume to: " + volume);
|
276
|
+
player.setVolume(volume);
|
277
|
+
});
|
278
|
+
}
|
279
|
+
|
280
|
+
@Override
|
281
|
+
public boolean isPlaying() throws Exception {
|
282
|
+
return player != null && player.isPlaying();
|
283
|
+
}
|
284
|
+
|
285
|
+
@Override
|
286
|
+
public double getDuration() {
|
287
|
+
if (isPrepared) {
|
288
|
+
final double[] duration = { 0 };
|
289
|
+
owner
|
290
|
+
.getActivity()
|
291
|
+
.runOnUiThread(() -> {
|
292
|
+
if (player.getPlaybackState() == Player.STATE_READY) {
|
293
|
+
long rawDuration = player.getDuration();
|
294
|
+
if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
|
295
|
+
duration[0] = rawDuration / 1000.0;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
});
|
299
|
+
return duration[0];
|
300
|
+
}
|
301
|
+
return 0;
|
302
|
+
}
|
303
|
+
|
304
|
+
@Override
|
305
|
+
public double getCurrentPosition() {
|
306
|
+
if (isPrepared) {
|
307
|
+
final double[] position = { 0 };
|
308
|
+
owner
|
309
|
+
.getActivity()
|
310
|
+
.runOnUiThread(() -> {
|
311
|
+
if (player.getPlaybackState() == Player.STATE_READY) {
|
312
|
+
position[0] = player.getCurrentPosition() / 1000.0;
|
313
|
+
}
|
314
|
+
});
|
315
|
+
return position[0];
|
316
|
+
}
|
317
|
+
return 0;
|
318
|
+
}
|
319
|
+
|
320
|
+
@Override
|
321
|
+
public void setCurrentTime(double time) throws Exception {
|
322
|
+
owner
|
323
|
+
.getActivity()
|
324
|
+
.runOnUiThread(() -> {
|
325
|
+
player.seekTo(Math.round(time * 1000));
|
326
|
+
});
|
327
|
+
}
|
328
|
+
|
329
|
+
@Override
|
330
|
+
public void playWithFade(Double time) throws Exception {
|
331
|
+
Log.d(TAG, "PlayWithFade called with time: " + time);
|
332
|
+
owner
|
333
|
+
.getActivity()
|
334
|
+
.runOnUiThread(() -> {
|
335
|
+
if (!isPrepared) {
|
336
|
+
// If not prepared, wait for preparation
|
337
|
+
player.addListener(
|
338
|
+
new Player.Listener() {
|
339
|
+
@Override
|
340
|
+
public void onPlaybackStateChanged(int state) {
|
341
|
+
if (state == Player.STATE_READY) {
|
342
|
+
startPlaybackWithFade(time);
|
343
|
+
player.removeListener(this);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
}
|
347
|
+
);
|
348
|
+
} else {
|
349
|
+
startPlaybackWithFade(time);
|
350
|
+
}
|
351
|
+
});
|
352
|
+
}
|
353
|
+
|
354
|
+
private void startPlaybackWithFade(Double time) {
|
355
|
+
if (!player.isPlayingAd()) { // Make sure we're not in an ad
|
356
|
+
if (time != null) {
|
357
|
+
player.seekTo(Math.round(time * 1000));
|
358
|
+
} else if (player.isCurrentMediaItemLive()) {
|
359
|
+
long liveEdge = player.getCurrentLiveOffset();
|
360
|
+
if (liveEdge > 0) {
|
361
|
+
player.seekTo(liveEdge - LIVE_OFFSET_MS);
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
// Wait for buffering to complete before starting playback
|
366
|
+
player.addListener(
|
367
|
+
new Player.Listener() {
|
368
|
+
@Override
|
369
|
+
public void onPlaybackStateChanged(int state) {
|
370
|
+
if (state == Player.STATE_READY) {
|
371
|
+
player.removeListener(this);
|
372
|
+
// Ensure playback rate is normal
|
373
|
+
player.setPlaybackParameters(new PlaybackParameters(1.0f));
|
374
|
+
// Start with volume 0
|
375
|
+
player.setVolume(0);
|
376
|
+
player.setPlayWhenReady(true);
|
377
|
+
// Start fade after ensuring we're actually playing
|
378
|
+
checkAndStartFade();
|
379
|
+
}
|
380
|
+
}
|
381
|
+
}
|
382
|
+
);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
private void checkAndStartFade() {
|
387
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
388
|
+
handler.postDelayed(
|
389
|
+
new Runnable() {
|
390
|
+
int attempts = 0;
|
391
|
+
|
392
|
+
@Override
|
393
|
+
public void run() {
|
394
|
+
if (player.isPlaying()) {
|
395
|
+
fadeIn();
|
396
|
+
} else if (attempts < 10) { // Try for 5 seconds (10 * 500ms)
|
397
|
+
attempts++;
|
398
|
+
handler.postDelayed(this, 500);
|
399
|
+
}
|
400
|
+
}
|
401
|
+
},
|
402
|
+
500
|
403
|
+
);
|
404
|
+
}
|
405
|
+
|
406
|
+
private void fadeIn() {
|
407
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
408
|
+
final Runnable fadeRunnable = new Runnable() {
|
409
|
+
float currentVolume = 0;
|
410
|
+
|
411
|
+
@Override
|
412
|
+
public void run() {
|
413
|
+
if (player != null && player.isPlaying() && currentVolume < volume) {
|
414
|
+
currentVolume += FADE_STEP;
|
415
|
+
if (currentVolume > volume) currentVolume = volume;
|
416
|
+
player.setVolume(currentVolume);
|
417
|
+
Log.d(TAG, "Fading in: volume = " + currentVolume);
|
418
|
+
handler.postDelayed(this, FADE_DELAY_MS);
|
419
|
+
}
|
420
|
+
}
|
421
|
+
};
|
422
|
+
handler.post(fadeRunnable);
|
423
|
+
}
|
424
|
+
|
425
|
+
@Override
|
426
|
+
public void stopWithFade() throws Exception {
|
427
|
+
owner
|
428
|
+
.getActivity()
|
429
|
+
.runOnUiThread(() -> {
|
430
|
+
if (player.isPlaying()) {
|
431
|
+
fadeOut();
|
432
|
+
}
|
433
|
+
});
|
434
|
+
}
|
435
|
+
|
436
|
+
private void fadeOut() {
|
437
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
438
|
+
final Runnable fadeRunnable = new Runnable() {
|
439
|
+
float currentVolume = player.getVolume();
|
440
|
+
|
441
|
+
@Override
|
442
|
+
public void run() {
|
443
|
+
if (currentVolume > FADE_STEP) {
|
444
|
+
currentVolume -= FADE_STEP;
|
445
|
+
player.setVolume(currentVolume);
|
446
|
+
Log.d(TAG, "Fading out: volume = " + currentVolume);
|
447
|
+
handler.postDelayed(this, FADE_DELAY_MS);
|
448
|
+
} else {
|
449
|
+
player.setVolume(0);
|
450
|
+
// Stop and reset player
|
451
|
+
player.stop();
|
452
|
+
player.clearMediaItems();
|
453
|
+
isPrepared = false;
|
454
|
+
|
455
|
+
// Create new media source
|
456
|
+
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
|
457
|
+
.setAllowCrossProtocolRedirects(true)
|
458
|
+
.setConnectTimeoutMs(15000)
|
459
|
+
.setReadTimeoutMs(15000)
|
460
|
+
.setUserAgent("ExoPlayer");
|
461
|
+
|
462
|
+
HlsMediaSource mediaSource = new HlsMediaSource.Factory(httpDataSourceFactory)
|
463
|
+
.setAllowChunklessPreparation(true)
|
464
|
+
.setTimestampAdjusterInitializationTimeoutMs(LIVE_OFFSET_MS)
|
465
|
+
.createMediaSource(MediaItem.fromUri(uri));
|
466
|
+
|
467
|
+
// Set new media source and prepare
|
468
|
+
player.setMediaSource(mediaSource);
|
469
|
+
player.prepare();
|
470
|
+
|
471
|
+
// Add listener for preparation completion
|
472
|
+
player.addListener(
|
473
|
+
new Player.Listener() {
|
474
|
+
@Override
|
475
|
+
public void onPlaybackStateChanged(int state) {
|
476
|
+
Log.d(TAG, "Fade-stop state changed to: " + getStateString(state));
|
477
|
+
if (state == Player.STATE_READY) {
|
478
|
+
isPrepared = true;
|
479
|
+
player.removeListener(this);
|
480
|
+
}
|
481
|
+
}
|
482
|
+
}
|
483
|
+
);
|
484
|
+
}
|
485
|
+
}
|
486
|
+
};
|
487
|
+
handler.post(fadeRunnable);
|
488
|
+
}
|
489
|
+
|
490
|
+
@Override
|
491
|
+
public void setRate(float rate) throws Exception {
|
492
|
+
owner
|
493
|
+
.getActivity()
|
494
|
+
.runOnUiThread(() -> {
|
495
|
+
Log.d(TAG, "Setting playback rate to: " + rate);
|
496
|
+
player.setPlaybackParameters(new PlaybackParameters(rate));
|
497
|
+
});
|
498
|
+
}
|
499
|
+
}
|
package/dist/docs.json
CHANGED
@@ -507,7 +507,7 @@
|
|
507
507
|
},
|
508
508
|
{
|
509
509
|
"name": "addListener",
|
510
|
-
"signature": "(eventName:
|
510
|
+
"signature": "(eventName: 'complete', listenerFunc: CompletedListener) => Promise<PluginListenerHandle>",
|
511
511
|
"parameters": [
|
512
512
|
{
|
513
513
|
"name": "eventName",
|
@@ -533,6 +533,53 @@
|
|
533
533
|
"CompletedListener"
|
534
534
|
],
|
535
535
|
"slug": "addlistenercomplete-"
|
536
|
+
},
|
537
|
+
{
|
538
|
+
"name": "addListener",
|
539
|
+
"signature": "(eventName: 'currentTime', listenerFunc: CurrentTimeListener) => Promise<PluginListenerHandle>",
|
540
|
+
"parameters": [
|
541
|
+
{
|
542
|
+
"name": "eventName",
|
543
|
+
"docs": "",
|
544
|
+
"type": "'currentTime'"
|
545
|
+
},
|
546
|
+
{
|
547
|
+
"name": "listenerFunc",
|
548
|
+
"docs": "",
|
549
|
+
"type": "CurrentTimeListener"
|
550
|
+
}
|
551
|
+
],
|
552
|
+
"returns": "Promise<PluginListenerHandle>",
|
553
|
+
"tags": [
|
554
|
+
{
|
555
|
+
"name": "since",
|
556
|
+
"text": "6.5.0\nreturn {@link CurrentTimeEvent}"
|
557
|
+
}
|
558
|
+
],
|
559
|
+
"docs": "Listen for current time updates\nEmits every 100ms while audio is playing",
|
560
|
+
"complexTypes": [
|
561
|
+
"PluginListenerHandle",
|
562
|
+
"CurrentTimeListener"
|
563
|
+
],
|
564
|
+
"slug": "addlistenercurrenttime-"
|
565
|
+
},
|
566
|
+
{
|
567
|
+
"name": "clearCache",
|
568
|
+
"signature": "() => Promise<void>",
|
569
|
+
"parameters": [],
|
570
|
+
"returns": "Promise<void>",
|
571
|
+
"tags": [
|
572
|
+
{
|
573
|
+
"name": "since",
|
574
|
+
"text": "6.5.0"
|
575
|
+
},
|
576
|
+
{
|
577
|
+
"name": "returns"
|
578
|
+
}
|
579
|
+
],
|
580
|
+
"docs": "Clear the audio cache for remote audio files",
|
581
|
+
"complexTypes": [],
|
582
|
+
"slug": "clearcache"
|
536
583
|
}
|
537
584
|
],
|
538
585
|
"properties": []
|
@@ -578,7 +625,7 @@
|
|
578
625
|
{
|
579
626
|
"name": "assetPath",
|
580
627
|
"tags": [],
|
581
|
-
"docs": "Path to the audio file, relative path of the file, absolute url (file://) or remote url (https://)",
|
628
|
+
"docs": "Path to the audio file, relative path of the file, absolute url (file://) or remote url (https://)\nSupported formats:\n- MP3, WAV (all platforms)\n- M3U8/HLS streams (iOS and Android)",
|
582
629
|
"complexTypes": [],
|
583
630
|
"type": "string"
|
584
631
|
},
|
@@ -606,7 +653,7 @@
|
|
606
653
|
{
|
607
654
|
"name": "isUrl",
|
608
655
|
"tags": [],
|
609
|
-
"docs": "Is the audio file a URL, pass true if assetPath is a `file://` url",
|
656
|
+
"docs": "Is the audio file a URL, pass true if assetPath is a `file://` url\nor a streaming URL (m3u8)",
|
610
657
|
"complexTypes": [],
|
611
658
|
"type": "boolean | undefined"
|
612
659
|
}
|
@@ -664,6 +711,39 @@
|
|
664
711
|
"type": "string"
|
665
712
|
}
|
666
713
|
]
|
714
|
+
},
|
715
|
+
{
|
716
|
+
"name": "CurrentTimeEvent",
|
717
|
+
"slug": "currenttimeevent",
|
718
|
+
"docs": "",
|
719
|
+
"tags": [],
|
720
|
+
"methods": [],
|
721
|
+
"properties": [
|
722
|
+
{
|
723
|
+
"name": "currentTime",
|
724
|
+
"tags": [
|
725
|
+
{
|
726
|
+
"text": "6.5.0",
|
727
|
+
"name": "since"
|
728
|
+
}
|
729
|
+
],
|
730
|
+
"docs": "Current time of the audio in seconds",
|
731
|
+
"complexTypes": [],
|
732
|
+
"type": "number"
|
733
|
+
},
|
734
|
+
{
|
735
|
+
"name": "assetId",
|
736
|
+
"tags": [
|
737
|
+
{
|
738
|
+
"text": "6.5.0",
|
739
|
+
"name": "since"
|
740
|
+
}
|
741
|
+
],
|
742
|
+
"docs": "Asset Id of the audio",
|
743
|
+
"complexTypes": [],
|
744
|
+
"type": "string"
|
745
|
+
}
|
746
|
+
]
|
667
747
|
}
|
668
748
|
],
|
669
749
|
"enums": [],
|
@@ -680,6 +760,19 @@
|
|
680
760
|
]
|
681
761
|
}
|
682
762
|
]
|
763
|
+
},
|
764
|
+
{
|
765
|
+
"name": "CurrentTimeListener",
|
766
|
+
"slug": "currenttimelistener",
|
767
|
+
"docs": "",
|
768
|
+
"types": [
|
769
|
+
{
|
770
|
+
"text": "(state: CurrentTimeEvent): void",
|
771
|
+
"complexTypes": [
|
772
|
+
"CurrentTimeEvent"
|
773
|
+
]
|
774
|
+
}
|
775
|
+
]
|
683
776
|
}
|
684
777
|
],
|
685
778
|
"pluginConfigs": []
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { PluginListenerHandle } from
|
1
|
+
import type { PluginListenerHandle } from '@capacitor/core';
|
2
2
|
export interface CompletedEvent {
|
3
3
|
/**
|
4
4
|
* Emit when a play completes
|
@@ -65,6 +65,9 @@ export interface ConfigureOptions {
|
|
65
65
|
export interface PreloadOptions {
|
66
66
|
/**
|
67
67
|
* Path to the audio file, relative path of the file, absolute url (file://) or remote url (https://)
|
68
|
+
* Supported formats:
|
69
|
+
* - MP3, WAV (all platforms)
|
70
|
+
* - M3U8/HLS streams (iOS and Android)
|
68
71
|
*/
|
69
72
|
assetPath: string;
|
70
73
|
/**
|
@@ -81,9 +84,23 @@ export interface PreloadOptions {
|
|
81
84
|
audioChannelNum?: number;
|
82
85
|
/**
|
83
86
|
* Is the audio file a URL, pass true if assetPath is a `file://` url
|
87
|
+
* or a streaming URL (m3u8)
|
84
88
|
*/
|
85
89
|
isUrl?: boolean;
|
86
90
|
}
|
91
|
+
export interface CurrentTimeEvent {
|
92
|
+
/**
|
93
|
+
* Current time of the audio in seconds
|
94
|
+
* @since 6.5.0
|
95
|
+
*/
|
96
|
+
currentTime: number;
|
97
|
+
/**
|
98
|
+
* Asset Id of the audio
|
99
|
+
* @since 6.5.0
|
100
|
+
*/
|
101
|
+
assetId: string;
|
102
|
+
}
|
103
|
+
export type CurrentTimeListener = (state: CurrentTimeEvent) => void;
|
87
104
|
export interface NativeAudio {
|
88
105
|
/**
|
89
106
|
* Configure the audio player
|
@@ -221,5 +238,19 @@ export interface NativeAudio {
|
|
221
238
|
* @since 5.0.0
|
222
239
|
* return {@link CompletedEvent}
|
223
240
|
*/
|
224
|
-
addListener(eventName:
|
241
|
+
addListener(eventName: 'complete', listenerFunc: CompletedListener): Promise<PluginListenerHandle>;
|
242
|
+
/**
|
243
|
+
* Listen for current time updates
|
244
|
+
* Emits every 100ms while audio is playing
|
245
|
+
*
|
246
|
+
* @since 6.5.0
|
247
|
+
* return {@link CurrentTimeEvent}
|
248
|
+
*/
|
249
|
+
addListener(eventName: 'currentTime', listenerFunc: CurrentTimeListener): Promise<PluginListenerHandle>;
|
250
|
+
/**
|
251
|
+
* Clear the audio cache for remote audio files
|
252
|
+
* @since 6.5.0
|
253
|
+
* @returns {Promise<void>}
|
254
|
+
*/
|
255
|
+
clearCache(): Promise<void>;
|
225
256
|
}
|