capacitor-plugin-playlist
v8.0.0
Published
Playlist
Readme
capacitor-plugin-playlist
Capacitor plugin for Android, iOS, and Web with native audio playlist playback, background support, lock-screen / notification controls, and video handoff.
Requires Capacitor 8+ (peer dependency @capacitor/core >= 8.0.0).
Versioning
The major tracks the Capacitor major. 8.x targets Capacitor 8, as it does for the sibling capacitor-video-player. Minor and patch are this plugin's own.
This changed at 8.0.0. Before that the version was deliberately independent of Capacitor and stayed on 0.x while still requiring @capacitor/core >= 8.0.0 — which made the supported Capacitor version impossible to read off the version number. There is no 1.x–7.x: 8.0.0 follows 0.15.0 directly.
| Plugin version | Meaning | Capacitor peer |
|----------------|---------|----------------|
| 8.x | Current. Media3 Android stack; isStream no longer selects HLS (see Upgrading); error codes discriminate network vs. decode vs. unsupported. | 8+ |
| 0.15.0, 0.14.5 | Deprecated — do not use. Published to npm in error during the 8.0.0 release; 0.14.5 briefly held the latest tag despite predating the #144 fix. Go from 0.14.x straight to 8.x. | 8+ |
| 0.12.0–0.14.4 | Media3 Android stack (MediaSessionService); Capacitor JS API unchanged from 0.11.x. | 8+ |
| 0.11.x | Historical. Android: ExoMedia + PlaylistCore. | 8+ |
Older 0.8.x–0.10.x entries in CHANGELOG.md are historical release notes, not a target to “stay on 0.8 for Capacitor 8.”
Index
- Features
- Versioning
- Background
- Notes
- Installation
- Upgrading a host app
- Usage
- Events
- Video handoff
- API
- Migrating from cordova-plugin-playlist
- Changes
- Credits
- License
Features
Playlist management
setPlaylistItems— replace entire playlist (optional position retention)addItem/addAllItems— append tracks;addItemaccepts optional insert index (“play next”)moveItem— reorder without disrupting current playbackreplaceItem— swap track metadata/URL in place (e.g. stream → offline file)removeItem/removeItems/clearAllItems— remove tracksgetPlaylist— snapshot of current itemssetLoop— loop entire playlist when the last track completes
Playback controls
play/pauseskipForward/skipBackseekTo(seconds)playTrackByIndex/playTrackById— jump and playselectTrackByIndex/selectTrackById— select without playingsetPlaybackVolume(0–1)setPlaybackRate(0 pauses, 1 = normal speed)
Native platform integration
| Platform | Engine | OS controls |
|----------|--------|-------------|
| Android | androidx.media3 1.11.1 (ExoPlayer + MediaSessionService + DefaultMediaNotificationProvider) | MediaStyle notification, MediaSession, foreground mediaPlayback service |
| iOS | Custom AVBidirectionalQueuePlayer | Lock screen + Control Center via MPNowPlayingInfoCenter / MPRemoteCommandCenter |
| Web | HTMLAudioElement + optional HLS.js | Browser media controls only |
Background audio
- Android: foreground media service with
WAKE_LOCK,FOREGROUND_SERVICE, andFOREGROUND_SERVICE_MEDIA_PLAYBACK(Android 14+) - iOS:
UIBackgroundModes→audio - Position events throttled while WebView is backgrounded; one live snapshot emitted on foreground resume (0.9.1+)
Tracks and sources
- Remote URLs, local files (
file://or app-resolved paths), and streams - Set
isStream: trueon streaming URLs so pause/resume buffering behaves correctly. This does not affect how the source is parsed — HLS is picked up automatically from an.m3u8path, and anything else is sniffed - Set
mimeTypeonly when the URL has no usable extension and the type can't be inferred, e.g.mimeType: 'application/x-mpegURL'for an HLS playlist behind an extensionless URL albumArtshown in notification / lock screen (Glide on Android)- No built-in download manager — resolve offline paths in your app and pass them as
assetUrl
Status event stream
Single status listener with RmxAudioStatusMessage events (see Events).
Video handoff
When switching from background audio to native fullscreen video:
prepareForVideoHandoff()— release audio focus / sessiongetLastKnownPosition()— saved head position (seconds)resumeAfterVideoHandoff({ position, prewarm? })— re-arm audio after video
See Video handoff.
Cordova-compatible wrapper
RmxAudioPlayer (src/RmxAudioPlayer.ts) is a drop-in replacement for cordova-plugin-playlist with on('status') / off('status') and state getters (isPlaying, currentTrack, etc.).
Not exposed on RmxAudioPlayer: getPlaylist, video handoff methods — call Playlist directly for those.
Not supported
- Shuffle
- Audio ads / IMA integration (#71)
- Mixable / low-latency game audio (use cordova-plugin-nativeaudio instead)
- Simultaneous audio mixing (lock-screen controls require exclusive audio focus)
Background
Forked from cordova-plugin-playlist for Capacitor.
Notes
Android
Uses Media3 ExoPlayer inside MediaService (MediaSessionService). Notification and lock-screen controls come from Media3 DefaultMediaNotificationProvider. Media session id is org.dwbn.playlist. The notification channel (Audio playback) is created by the plugin — hosts do not configure it.
iOS
Uses a customized AVQueuePlayer (AVBidirectionalQueuePlayer) for track-change feedback and continuous audio session between songs. Minimum iOS 18 (0.9.4+). Swift Package Manager supported (0.10.0+).
Installation
npm i capacitor-plugin-playlist
npx cap syncWeb
Include HLS.js in your build for HLS streams.
Angular example
npm i hls.jsAdd to angular.json → architect → build → options → scripts:
"scripts": [
{
"input": "node_modules/hls.js/dist/hls.min.js"
}
]Android
AndroidManifest.xml
From 0.11.0, the plugin library manifest merges the media playback service and required permissions into your app automatically:
android.permission.WAKE_LOCKandroid.permission.FOREGROUND_SERVICEandroid.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACKorg.dwbn.plugins.playlist.service.MediaService(exported="false",foregroundServiceType="mediaPlayback")
After upgrading, run npx cap sync android. You do not need to copy these entries into your host AndroidManifest.xml unless you want to override plugin defaults.
Keep your application's existing Android Application class. The legacy org.dwbn.plugins.playlist.App class remains available for compatibility, but it is no longer required — remove android:name="org.dwbn.plugins.playlist.App" from <application> when upgrading from older setups.
Gradle 9+
On AGP 9+, Kotlin is built into the Android Gradle Plugin — do not add an extra kotlin-android plugin in your host app because of this plugin (see CHANGELOG 0.11.4). Match your project's existing AGP/Kotlin setup.
Glide (notification album art)
Create MyAppGlideModule.java:
package org.your.package.namespace;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}See https://guides.codepath.com/android/Displaying-Images-with-the-Glide-Library
Notification icon
Create a transparent silhouette icon (e.g. ic_notification.png) and pass it via setOptions:
await Playlist.setOptions({
verbose: !environment.production,
options: { icon: 'ic_notification' },
});In Android Studio: right-click res → New → Image Asset → Notification Icons.


iOS
Add to Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
</array>Without audio background mode, iOS stops playback when the app backgrounds.
Upgrading a host app
From 0.1x → 8.0.0
The TypeScript API is unchanged — every AudioTrack and AudioPlayerOptions field that existed on 0.14.x still exists, isStream included. Three behavior changes need a look at consuming code.
1. Extensionless HLS now needs mimeType. Through 0.14.x, isStream: true on a URL with no recognised media extension forced the HLS parser on Android. That was undocumented, disagreed with iOS and web, and broke every progressive stream behind an extensionless URL (#144). HLS is now selected by an .m3u8 path or by an explicit hint:
// Only needed when the URL has no usable extension. An `.m3u8` path is detected automatically.
{ trackId: 't1', assetUrl: 'https://cdn.example/stream/audio', mimeType: 'application/x-mpegURL' }Nothing to do if your HLS URLs end in .m3u8 — that is the common case and it keeps working. isStream keeps its documented meaning (pause/resume buffering) and still drives the live-edge jump on Android and iOS.
2. Error codes now discriminate. RMXSTATUS_ERROR previously reported RMXERR_DECODE for every failure on iOS, and RMXERR_NONE_SUPPORTED for every non-decoder failure on Android — so a dropped connection was indistinguishable from an unplayable file. Now a network failure reports RMXERR_NETWORK, which is the retry-able case:
if (status.msgType === RmxAudioStatusMessage.RMXSTATUS_ERROR) {
const { code } = status.value as OnStatusErrorCallbackData;
if (code === RmxAudioErrorType.RMXERR_NETWORK) retry(); else skipTrack();
}If you branch on code === RMXERR_DECODE today, that branch used to catch everything on iOS and will now catch almost nothing. Web previously sent no code at all and now conforms to OnStatusErrorCallbackData; the rest of its old error payload is still there alongside code/message.
3. status: "stalled" is new, and position events stop during a stall. A genuine stall no longer streams RMXSTATUS_PLAYBACK_POSITION with a frozen position — the events stop and one RMXSTATUS_STALLED fires (#143). Treat "stalled" as "still trying", not as an error, and don't assume a progress bar keeps ticking. The threshold is tunable with Playlist.setOptions({ stallTimeoutMs: 6000 }) (default 10000).
Android host app: no Gradle or manifest changes beyond what Always (Android 0.12.0+) already requires.
From 0.11.x → 0.12.0
Historical, for apps still on the ExoMedia/PlaylistCore line. Capacitor / JavaScript: no changes across this step — Playlist, RmxAudioPlayer, status events, and video handoff method names behave as before. Android host app: update Gradle and manifest as below, then npx cap sync android.
Remove from the host app (if you added these when following older docs or samples):
implementationofcom.devbrackets.android:playlistcoreandcom.devbrackets.android:exomedia— 0.12.0 uses Media3 inside the plugin; leftover host deps pull an old Media3 versionandroid:name="org.dwbn.plugins.playlist.App"on<application>— unused since 0.11.0; keep your ownApplicationclass
Stop doing on 0.12.0: subclassing PlaylistCore, calling startForeground beside MediaService, or adding com.google.android.exoplayer:exoplayer-*:2.x in the app module.
Then apply the steps under Always (Android 0.12.0+) below.
Video handoff: from 0.12.0, foreground retain starts on prepareForVideoHandoff. Existing JS that uses prewarm: true still works; prewarm is optional, not required for every flow.
Always (Android 0.12.0+)
Bump the plugin to 8.x (or at minimum 0.12.0) and run
npx cap sync android.Pin one Media3 version when the same APK also uses another Media3 library (for example a native fullscreen video player). Gradle can otherwise unify to an old transitive (pre-0.12.0 ExoMedia pulled Media3 1.5.1). In
android/variables.gradle(orext):media3Version = '1.11.1'In the root
android/build.gradle:allprojects { configurations.configureEach { resolutionStrategy { eachDependency { details -> if (details.requested.group == 'androidx.media3') { details.useVersion rootProject.ext.media3Version } } } } }Android 13+ (API 33+): declare
android.permission.POST_NOTIFICATIONSin your hostAndroidManifest.xmland request it at runtime when not granted. This plugin does not merge that permission — without it, media notifications may not appear.Minimum SDK 24 (unchanged).
Audio + video in one app
If the same Android app plays background audio with this plugin and native video with Media3, use one media3Version in Gradle (snippet above). Mixed Media3 versions in one APK are unsupported.
This plugin’s MediaSession id is org.dwbn.playlist. Any second Media3 session in the same process (typical for a fullscreen video player) must use a different id — org.dwbn.video is the usual pair for DWBN handoff setups. Media3 forbids two empty or duplicate session ids.
Usage
See also examples/audio-provider.ts for an Angular/Ionic integration.
Basic flow (Playlist API)
import { Playlist, AudioTrack, RmxAudioStatusMessage } from 'capacitor-plugin-playlist';
await Playlist.setOptions({
verbose: true,
resetStreamOnPause: true,
options: { icon: 'ic_notification' },
});
await Playlist.initialize();
const handle = await Playlist.addListener('status', ({ status }) => {
if (status.msgType === RmxAudioStatusMessage.RMXSTATUS_PLAYBACK_POSITION) {
// update UI progress
}
});
const track: AudioTrack = {
trackId: 'track-1',
assetUrl: 'https://example.com/audio.mp3',
title: 'Track title',
artist: 'Artist name',
album: 'Album name',
albumArt: 'https://example.com/cover.jpg',
isStream: false,
// mimeType: 'application/x-mpegURL', // only for sources whose URL has no usable extension
};
await Playlist.setPlaylistItems({
items: [track],
options: { startPaused: false },
});
await Playlist.play();
// later: handle.remove();RmxAudioPlayer wrapper (Cordova migration)
import { RmxAudioPlayer, AudioTrack } from 'capacitor-plugin-playlist';
const player = new RmxAudioPlayer();
await player.initialize();
player.on('status', (data) => {
console.log('status', data.msgType, data);
});
await player.setLoop(true);
await player.setPlaylistItems([track], { retainPosition: true, playFromId: track.trackId });
await player.play();Events
Subscribe via Playlist.addListener('status', …) or RmxAudioPlayer.on('status', …).
Each callback receives { action: 'status', status: OnStatusCallbackData } where:
status.trackId— current track id,"NONE"when idle,"INVALID"when playlist completedstatus.msgType—RmxAudioStatusMessageenum valuestatus.value— payload (shape depends onmsgType)
| msgType | Name | When | Payload |
|---------|------|------|---------|
| 5 | ERROR | Playback or network failure | OnStatusErrorCallbackData (code, message) — code distinguishes network from decode from unsupported |
| 10 | LOADING | Track loading started | OnStatusCallbackUpdateData |
| 11 | CANPLAY | Track ready to play | OnStatusCallbackUpdateData |
| 15 | LOADED | Track fully loaded | OnStatusCallbackUpdateData |
| 20 | STALLED | Position stopped advancing while the player still reports playing. All platforms | OnStatusCallbackUpdateData |
| 25 | BUFFERING | Buffer progress update | OnStatusCallbackUpdateData |
| 30 | PLAYING | Playback started/resumed | OnStatusCallbackUpdateData |
| 35 | PAUSE | Playback paused | OnStatusCallbackUpdateData |
| 40 | PLAYBACK_POSITION | Periodic position tick | OnStatusCallbackUpdateData (suppressed while the WebView is backgrounded, and — Android/iOS — while stalled) |
| 45 | SEEK | User or app seeked | OnStatusCallbackUpdateData |
| 50 | COMPLETED | Current track finished | OnStatusCallbackUpdateData |
| 55 | DURATION | Duration first known | OnStatusCallbackUpdateData |
| 60 | STOPPED | All playback stopped | OnStatusCallbackUpdateData |
| 90 | SKIP_FORWARD | Skipped to next track | OnStatusCallbackUpdateData |
| 95 | SKIP_BACK | Skipped to previous track | OnStatusCallbackUpdateData |
| 100 | TRACK_CHANGED | Active track changed | OnStatusTrackChangedData |
| 105 | PLAYLIST_COMPLETED | Entire playlist finished | OnStatusCallbackUpdateData |
| 110 | ITEM_ADDED | Track added | OnStatusCallbackUpdateData |
| 115 | ITEM_REMOVED | Track removed | OnStatusCallbackUpdateData |
| 120 | PLAYLIST_CLEARED | All tracks removed | OnStatusCallbackUpdateData |
For track changes, prefer handling TRACK_CHANGED over SKIP_FORWARD / SKIP_BACK.
Track status values
Most payloads carry a status string describing the track's current state. Treat an unrecognised value as non-fatal rather than as an error:
| status | Meaning |
|----------|---------|
| loading | Source is being prepared; not yet playable |
| playing | Playing, position advancing |
| paused | Paused by the user or the system |
| stalled | Still trying, but position is not advancing — see RMXSTATUS_STALLED and stallTimeoutMs. New in 8.0.0 on iOS and web |
| error | Playback failed; see the code on the RMXSTATUS_ERROR payload |
Video handoff
Native audio and native fullscreen video cannot share audio focus. These three methods coordinate a clean handoff when the user opens video while audio was playing (or when switching back to audio after video).
Works with any native Capacitor video plugin (or other player) that needs exclusive audio focus.
Methods
| Method | Purpose |
|--------|---------|
| prepareForVideoHandoff() | Pause audio, capture head position, release audio focus / session; Android retains FGS |
| getLastKnownPosition() | Read captured position (seconds) after prepare |
| resumeAfterVideoHandoff({ position, prewarm? }) | Re-arm audio after video, or silently prepare at position (optional Android prewarm) |
Call these on the Playlist plugin directly — they are not exposed on RmxAudioPlayer.
Lifecycle
sequenceDiagram
participant App
participant Playlist
participant VideoPlayer
Note over App,VideoPlayer: Entering video
App->>Playlist: prepareForVideoHandoff()
Playlist-->>App: audio paused, focus released
App->>Playlist: getLastKnownPosition() optional
App->>Playlist: resumeAfterVideoHandoff position prewarm true
Note over Playlist: Android only optional silent prepare
App->>VideoPlayer: initPlayer
VideoPlayer-->>App: video playing
Note over App,VideoPlayer: Exiting video
App->>Playlist: resumeAfterVideoHandoff position
Note over Playlist: re-arm session no prewarm
App->>Playlist: play optional
Playlist-->>App: audio resumesBasic sequence
import { Playlist } from 'capacitor-plugin-playlist';
// --- Entering native video ---
await Playlist.prepareForVideoHandoff();
const { position: audioPosition } = await Playlist.getLastKnownPosition();
await nativeVideoPlayer.init({ /* url, fullscreen, … */ });
// --- Exiting native video (use video head, not audioPosition) ---
const videoPosition = 120; // from your video player's position events
const { resumed } = await Playlist.resumeAfterVideoHandoff({ position: videoPosition });
if (!resumed) {
await Playlist.play(); // in-place Android resume already played; skip to avoid a stutter
}Optional Android sequence (prewarm)
From 0.12.0, the foreground service is retained when you call prepareForVideoHandoff, so many apps can use the basic sequence only. On older Android versions or if you still see focus/FGS edge cases, you may optionally prewarm before video:
On Android 14+ (especially Android 17), starting or re-promoting the media foreground service from the background can still fail in some flows. Prewarm while the app is still visible if you need it:
// 1. Release audio focus
await Playlist.prepareForVideoHandoff();
// 2. Prewarm FGS silently at the video start position (Android)
await Playlist.resumeAfterVideoHandoff({
position: Math.floor(videoStartSec),
prewarm: true,
});
// 3. Start native video
await nativeVideoPlayer.init({ /* … */ });
// … user watches video; track video position via player events …
// 4. After video closes — re-arm without prewarm
const { resumed } = await Playlist.resumeAfterVideoHandoff({ position: Math.floor(videoExitSec) });
if (!resumed) {
await Playlist.play();
}What prewarm: true does on Android:
- Prepares the playlist item at
positionbut stays silent — no audio focus request, no audible playback - Does not create FGS; retain already started on
prepareForVideoHandoff - Prevents video sound from dropping when audio would otherwise re-request focus
- During prewarm,
Playlist.play()is a no-op for audible playback (only keeps FGS notification updated)
iOS: prewarm is accepted but ignored. Use prepareForVideoHandoff() → video → resumeAfterVideoHandoff({ position }) → play().
Web: Both methods are stubs (pause + store position). No native session handoff.
Platform behaviour
| Step | Android | iOS | Web |
|------|---------|-----|-----|
| prepareForVideoHandoff | Pause, abandon audio focus, store position, retain FGS (do not stop) | Pause, store track time, AVAudioSession.setActive(false) | Pause HTMLAudioElement, store currentTime |
| getLastKnownPosition | Returns stored handoff position (seconds) | Same | Same |
| resumeAfterVideoHandoff (no prewarm) | In-place play-then-seek if FGS still foreground (retain starts on prepareForVideoHandoff); otherwise { resumed: false } so JS may play/seek | Reactivate audio session; reset track-id guard for PLAYING events | Store position only |
| resumeAfterVideoHandoff (prewarm) | Silent prepare at position; FGS already retained from prepare; { resumed: false } | No-op | N/A |
| After resume | If { resumed: true }, skip play() / seekTo. If false, JS may play/seek | Call play() if audible resume is wanted | Call play() on web player |
Position: audio vs video head
getLastKnownPosition()afterprepareForVideoHandoff— last audio head before video opened. Useful if video never started or for debugging.- On video exit — pass the video playback position (from your video player's position events), not the stale audio position, so audio resumes where the user left off in the video timeline.
- Track video head while video plays and persist it if the app may cold-start (e.g. after PiP dismiss on Android).
Integration checklist
- Call
prepareForVideoHandoff()immediately before your native video player starts — never after. - (Optional, Android) Call
resumeAfterVideoHandoff({ position, prewarm: true })after prepare and before video init while the WebView is foregrounded — only if you need prewarm (see 0.12.0 retain). - On video exit, call
resumeAfterVideoHandoff({ position })withoutprewarm. If{ resumed: true }, skipplay()/seekTo; iffalse, JS may play/seek. resumeAfterVideoHandoffmust complete beforeseekTo()/play()on the audio side.- Do not call
Playlist.release()between prepare and resume unless you intend to tear down the native player entirely. - Idempotent exit handling: guard against duplicate
resumeAfterVideoHandoffcalls from concurrent native exit events (merge to a single call withmax(position)).
Common pitfalls
| Symptom | Likely cause |
|---------|----------------|
| Video has no sound shortly after start | Audio re-requested focus after prepare; try optional Android prewarm: true before video, or ensure 0.12.0+ retain on prepareForVideoHandoff |
| Audio silent after long video session | FGS stopped while backgrounded; use 0.12.0+ retain and in-place resume on exit; optional prewarm before long video |
| JS stuck in PAUSED after video (iOS, index > 0) | Missing play() after resume, or PLAYING event suppressed — fixed in 0.8.11 |
| PLAYBACK_POSITION flood after background | Expected — position events suppressed while WebView backgrounded (0.9.1+) |
See CHANGELOG.md for version-specific fixes (0.8.8–0.10.3).
API
addListener('status', ...)setOptions(...)initialize()release()setPlaylistItems(...)addItem(...)moveItem(...)replaceItem(...)addAllItems(...)removeItem(...)removeItems(...)clearAllItems()getPlaylist()play()pause()skipForward()skipBack()seekTo(...)playTrackByIndex(...)playTrackById(...)selectTrackByIndex(...)selectTrackById(...)setPlaybackVolume(...)setLoop(...)setPlaybackRate(...)prepareForVideoHandoff()resumeAfterVideoHandoff(...)getLastKnownPosition()- Interfaces
- Type Aliases
- Enums
addListener('status', ...)
addListener(eventName: 'status', listenerFunc: PlaylistStatusChangeCallback) => Promise<PluginListenerHandle>Subscribe to native playback status events (track changes, position, errors, etc.).
| Param | Type | Description |
| ------------------ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| eventName | 'status' | Must be 'status'. |
| listenerFunc | PlaylistStatusChangeCallback | Callback receiving { action, status } where status.msgType is a RmxAudioStatusMessage. |
Returns: Promise<PluginListenerHandle>
setOptions(...)
setOptions(options: AudioPlayerOptions) => Promise<void>Configure plugin behaviour (verbose logging, stream pause handling, notification icon). Can be called at any time; not required before playback.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | AudioPlayerOptions |
initialize()
initialize() => Promise<void>Initialise the native player, register status callbacks, and arm lock-screen / notification controls. Call once before playback (e.g. on app start).
release()
release() => Promise<void>Tear down native resources (audio session, media service, observers). Call when the app no longer needs background audio (e.g. on logout).
setPlaylistItems(...)
setPlaylistItems(options: PlaylistOptions) => Promise<void>Replace the entire playlist. Clears all previous items.
Use options.retainPosition to keep the current track and playback position.
| Param | Type |
| ------------- | ----------------------------------------------------------- |
| options | PlaylistOptions |
addItem(...)
addItem(options: AddItemOptions) => Promise<void>Append a single track to the end of the playlist, or insert at a 0-based index.
When index is omitted the track is appended. Insertion does not interrupt playback
of the current track.
| Param | Type |
| ------------- | --------------------------------------------------------- |
| options | AddItemOptions |
moveItem(...)
moveItem(options: MoveItemOptions) => Promise<void>Move a track from one index to another without restarting the current track.
| Param | Type |
| ------------- | ----------------------------------------------------------- |
| options | MoveItemOptions |
replaceItem(...)
replaceItem(options: ReplaceItemOptions) => Promise<void>Replace a track's metadata and source URL in place (e.g. stream URL → local file). When replacing the currently playing track, playback position and play/pause state are preserved.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | ReplaceItemOptions |
addAllItems(...)
addAllItems(options: AddAllItemOptions) => Promise<void>Append multiple tracks to the end of the playlist.
Raises one RMXSTATUS_ITEM_ADDED event per track.
| Param | Type |
| ------------- | --------------------------------------------------------------- |
| options | AddAllItemOptions |
removeItem(...)
removeItem(options: RemoveItemOptions) => Promise<void>Remove a track by index (preferred) or id. If the removed track is currently playing, the next track starts automatically.
| Param | Type |
| ------------- | --------------------------------------------------------------- |
| options | RemoveItemOptions |
removeItems(...)
removeItems(options: RemoveItemsOptions) => Promise<void>Remove multiple tracks in a single batch. If the currently playing track is removed, the next available track starts automatically.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | RemoveItemsOptions |
clearAllItems()
clearAllItems() => Promise<void>Remove all tracks from the playlist. Raises RMXSTATUS_PLAYLIST_CLEARED and RMXSTATUS_STOPPED.
getPlaylist()
getPlaylist() => Promise<GetPlaylistResult>Return a snapshot of the current playlist items.
Returns: Promise<GetPlaylistResult>
play()
play() => Promise<void>Start or resume playback of the current track. No-op if the playlist is empty.
pause()
pause() => Promise<void>Pause playback of the current track.
skipForward()
skipForward() => Promise<void>Skip to the next track. At the end of the playlist, wraps to the beginning when loop is enabled.
skipBack()
skipBack() => Promise<void>Skip to the previous track. No-op when already at the first track.
seekTo(...)
seekTo(options: SeekToOptions) => Promise<void>Seek to a position (seconds) in the currently playing track. If the position exceeds track length, playback advances to the next track.
| Param | Type |
| ------------- | ------------------------------------------------------- |
| options | SeekToOptions |
playTrackByIndex(...)
playTrackByIndex(options: PlayByIndexOptions) => Promise<void>Jump to the track at the given 0-based index and start playback.
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| options | PlayByIndexOptions |
playTrackById(...)
playTrackById(options: PlayByIdOptions) => Promise<void>Jump to the track with the given id and start playback.
| Param | Type |
| ------------- | ----------------------------------------------------------- |
| options | PlayByIdOptions |
selectTrackByIndex(...)
selectTrackByIndex(options: SelectByIndexOptions) => Promise<void>Select the track at the given index without necessarily starting playback.
| Param | Type |
| ------------- | --------------------------------------------------------------------- |
| options | SelectByIndexOptions |
selectTrackById(...)
selectTrackById(options: SelectByIdOptions) => Promise<void>Select the track with the given id without necessarily starting playback.
| Param | Type |
| ------------- | --------------------------------------------------------------- |
| options | SelectByIdOptions |
setPlaybackVolume(...)
setPlaybackVolume(options: SetPlaybackVolumeOptions) => Promise<void>Set media stream volume. Float in range [0, 1]. Hardware volume controls still apply on top of this value.
| Param | Type |
| ------------- | ----------------------------------------------------------------------------- |
| options | SetPlaybackVolumeOptions |
setLoop(...)
setLoop(options: SetLoopOptions) => Promise<void>When true, the playlist loops back to the first track after the last track completes.
| Param | Type |
| ------------- | --------------------------------------------------------- |
| options | SetLoopOptions |
setPlaybackRate(...)
setPlaybackRate(options: SetPlaybackRateOptions) => Promise<void>Set playback speed. Float value; 0 pauses, 1 is normal speed.
| Param | Type |
| ------------- | ------------------------------------------------------------------------- |
| options | SetPlaybackRateOptions |
prepareForVideoHandoff()
prepareForVideoHandoff() => Promise<void>Release native audio session / focus so a video player can own playback.
Android: pauses current track, abandons audio focus, stores head position. Does not stop the foreground media service.
iOS: pauses, captures head position, deactivates AVAudioSession with notifyOthersOnDeactivation.
Web: pauses HTMLAudioElement and stores currentTime.
Call immediately before native video starts (e.g. your video plugin's init method).
resumeAfterVideoHandoff(...)
resumeAfterVideoHandoff(options: ResumeAfterVideoHandoffOptions) => Promise<ResumeAfterVideoHandoffResult>Re-arm native audio after video ends or, on Android, prewarm the media service before video starts.
Without prewarm (typical exit path):
- Android: when
playis true (default), re-acquires focus and resumes atposition. Whenresumedistrue, JS should skip redundantseekTo/play. Whenplayis false, clears handoff retain and returns{ resumed: false }so JS can seek without playing. - iOS: restores pinned track, reactivates
AVAudioSession, seeks toposition, and whenplayis true starts playback (seek-then-play). Returns{ resumed: true }when native handled the handoff. - Web: stores position only (no native session); returns
{ resumed: false }.
With prewarm: true (Android, before video): starts MediaService in foreground at position but stays silent — no audio focus, no audible playback. Always returns { resumed: false }.
| Param | Type |
| ------------- | ----------------------------------------------------------------------------------------- |
| options | ResumeAfterVideoHandoffOptions |
Returns: Promise<ResumeAfterVideoHandoffResult>
getLastKnownPosition()
getLastKnownPosition() => Promise<GetLastKnownPositionResult>Return the audio head position (seconds) captured during the most recent prepareForVideoHandoff
or passed to resumeAfterVideoHandoff.
Returns: Promise<GetLastKnownPositionResult>
Interfaces
PluginListenerHandle
| Prop | Type |
| ------------ | ----------------------------------------- |
| remove | () => Promise<void> |
PlaylistStatusChangeCallbackArg
| Prop | Type |
| ------------ | --------------------------------------------------------------------- |
| action | string |
| status | OnStatusCallbackData |
OnStatusCallbackData
Encapsulates the data received by an onStatus callback
| Prop | Type | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| trackId | string | The ID of this track. If the track is null or has completed, this value is "NONE" If the playlist is completed, this value is "INVALID" |
| msgType | RmxAudioStatusMessage | The type of status update |
| value | OnStatusCallbackUpdateData | OnStatusTrackChangedData | OnStatusErrorCallbackData | The status payload. For all updates except ERROR, the data package is described by OnStatusCallbackUpdateData. For Errors, the data is shaped as OnStatusErrorCallbackData |
OnStatusCallbackUpdateData
Contains the current track status as of the moment an onStatus update event is emitted.
| Prop | Type | Description |
| --------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| trackId | string | The ID of this track corresponding to this event. If the track is null or has completed, this value is "NONE". This will happen when skipping to the beginning or end of the playlist. If the playlist is completed, this value is "INVALID" |
| isStream | boolean | Boolean indicating whether this is a streaming track. |
| currentIndex | number | The current index of the track in the playlist. |
| status | 'error' | 'unknown' | 'ready' | 'playing' | 'loading' | 'paused' | The current status of the track, as a string. This is used to summarize the various event states that a track can be in; e.g. "playing" is true for any number of track statuses. The Javascript interface takes care of this for you; this field is here only for reference. |
| currentPosition | number | Current playback position of the reported track. |
| duration | number | The known duration of the reported track. For streams or malformed MP3's, this value will be 0. |
| playbackPercent | number | Progress of track playback, as a percent, in the range 0 - 100 |
| bufferPercent | number | Buffering progress of the track, as a percent, in the range 0 - 100 |
| bufferStart | number | The starting position of the buffering progress. For now, this is always reported as 0. |
| bufferEnd | number | The maximum position, in seconds, of the track buffer. For now, only the buffer with the maximum playback position is reported, even if there are other segments (due to seeking, for example). Practically speaking you don't need to worry about that, as in both implementations the minor gaps are automatically filled in by the underlying players. |
OnStatusTrackChangedData
Reports information about the playlist state when a track changes. Includes the new track, its index, and the state of the playlist.
| Prop | Type | Description |
| ------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| currentItem | AudioTrack | The new track that has been selected. May be null if you are at the end of the playlist, or the playlist has been emptied. |
| currentIndex | number | The 0-based index of the new track. If the playlist has ended or been cleared, this will be -1. |
| isAtEnd | boolean | Indicates whether the playlist is now currently at the last item in the list. |
| isAtBeginning | boolean | Indicates whether the playlist is now at the first item in the list |
| hasNext | boolean | Indicates if there are additional playlist items after the current item. |
| hasPrevious | boolean | Indicates if there are any items before this one in the playlist. |
AudioTrack
An audio track for playback by the playlist.
| Prop | Type | Description |
| ------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| isStream | boolean | This item is a streaming asset. Make sure this is set to true for stream URLs, otherwise you will get odd behavior when the asset is paused. This only affects pause/resume buffering behavior. It does not select how the source is parsed - use mimeType for that. |
| trackId | string | trackId is optional and if not passed in, an auto-generated UUID will be used. |
| assetUrl | string | URL of the asset; can be local, a URL, or a streaming URL. If the asset is a stream, make sure that isStream is set to true, otherwise the plugin can't properly handle the item's buffer. |
| mimeType | string | Optional container hint for sources whose URL carries no usable file extension, e.g. 'application/x-mpegURL' for an HLS playlist served from an extensionless URL. Leave this unset for ordinary progressive sources - Android sniffs the content and web reads the URL, both of which handle MP3/AAC/OGG streams without a hint. HLS is detected automatically when the URL path ends in .m3u8. No-op on iOS, where AVFoundation determines the type itself. |
| albumArt | string | The local or remote URL to an image asset to be shown for this track. If this is null, the plugin's default image is used. |
| artist | string | The track's artist |
| album | string | Album the track belongs to |
| title | string | Title of the track |
| startPosition | number | Last known playback position to resume from, in seconds, when this track becomes current via a native skip-to-next/previous (e.g. OS notification, headset button, Android Auto/CarPlay, or the in-app Next/Previous buttons). Optional; defaults to 0. |
OnStatusErrorCallbackData
Represents an error reported by the onStatus callback.
| Prop | Type | Description |
| ------------- | --------------------------------------------------------------- | ----------------------- |
| code | RmxAudioErrorType | Error code |
| message | string | The error, as a message |
AudioPlayerOptions
Options governing the overall behavior of the audio player plugin
| Prop | Type | Description |
| ------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| verbose | boolean | Should the plugin's javascript dump the status message stream to the javascript console? |
| resetStreamOnPause | boolean | If true, when pausing a live stream, play will continue from the LIVE POSITION (e.g. the stream jumps forward to the current point in time, rather than picking up where it left off when you paused). If false, the stream will continue where you paused. The drawback of doing this is that when the audio buffer fills, it will jump forward to the current point in time, cause a disjoint in playback. Default is true. |
| stallTimeoutMs | number | How long, in milliseconds, playback position may fail to advance while the player reports it is playing before the plugin declares the track stalled and emits RMXSTATUS_STALLED (issue #143). Each platform's native "playing" signal (Android ExoPlayer's playbackState/isPlaying, iOS's AVPlayerItemPlaybackStalledNotification, the browser's waiting/stalled events) can fail to fire on a mid-stream network loss, so all three platforms poll actual position as a backstop and use this shared threshold for it. Default is 10000 (10s). |
| options | NotificationOptions | Further options for notifications |
NotificationOptions
| Prop | Type |
| ---------- | ------------------- |
| icon | string |
PlaylistOptions
| Prop | Type |
| ------------- | ------------------------------------------------------------------- |
| items | AudioTrack[] |
| options | PlaylistItemOptions |
PlaylistItemOptions
Options governing how the items are managed when using setPlaylistItems to update the playlist. This is typically useful if you are retaining items that were in the previous list.
| Prop | Type | Description |
| ---------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| retainPosition | boolean | If true, the plugin will continue playback from the current playback position after setting the items to the playlist. |
| playFromPosition | number | If retainPosition is true, this value will tell the plugin the exact time to start from, rather than letting the plugin decide based on current playback. |
| playFromId | string | If retainPosition is true, this value will tell the plugin the uid of the "current" item to start from, rather than letting the plugin decide based on current playback. |
| startPaused | boolean | If playback should immediately begin when calling setPlaylistItems on the plugin. Default is false; |
AddItemOptions
| Prop | Type | Description |
| ----------- | ------------------------------------------------- | ------------------------------------------- |
| item | AudioTrack | |
| index | number | 0-based index to insert at. Omit to append. |
MoveItemOptions
| Prop | Type | Description |
| ---------- | ------------------- | ------------------------------------------ |
| from | number | Source index (0-based). |
| to | number | Destination index (0-based) after removal. |
ReplaceItemOptions
| Prop | Type | Description |
| ----------- | ------------------------------------------------- | -------------------------------------------------------------------------- |
| item | AudioTrack | Replacement track data. When trackId is omitted the existing id is kept. |
| index | number | Index of the track to replace (preferred over id). |
| id | string | Id of the track to replace. |
AddAllItemOptions
| Prop | Type |
| ----------- | ------------------------- |
| items | AudioTrack[] |
RemoveItemOptions
| Prop | Type |
| ----------- | ------------------- |
| id | string |
| index | number |
RemoveItemsOptions
| Prop | Type |
| ----------- | -------------------------------- |
| items | RemoveItemOptions[] |
GetPlaylistResult
| Prop | Type |
| ----------- | ------------------------- |
| items | AudioTrack[] |
SeekToOptions
| Prop | Type |
| -------------- | ------------------- |
| position | number |
PlayByIndexOptions
| Prop | Type |
| -------------- | ------------------- |
| index | number |
| position | number |
PlayByIdOptions
| Prop | Type |
| -------------- | ------------------- |
| id | string |
| position | number |
SelectByIndexOptions
| Prop | Type |
| -------------- | ------------------- |
| index | number</c
