@capgo/native-audio 7.1.7 → 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.
@@ -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: \"complete\", listenerFunc: CompletedListener) => Promise<PluginListenerHandle>",
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 "@capacitor/core";
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: "complete", listenerFunc: CompletedListener): Promise<PluginListenerHandle>;
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
  }