tauri-plugin-native-audio 1.0.2

Native mobile audio playback plugin for Tauri
Documentation
# tauri-plugin-native-audio

Native mobile audio playback for Tauri applications.

`tauri-plugin-native-audio` gives you one JavaScript API over native playback engines:

- Android: Media3 ExoPlayer + MediaSessionService + notification controls
- iOS: AVPlayer + MPNowPlayingInfoCenter + MPRemoteCommandCenter

It is designed for real mobile playback flows: background-friendly behavior, lock screen / notification controls, and continuous state synchronization in the frontend.

| Platform | Supported |
| -------- | --------- |
| Linux    | -         |
| Windows  | -         |
| macOS    | -         |
| Android  ||
| iOS      ||

## Install

_This plugin requires a Rust version of at least **1.77.2**._

There are three general methods of installation:

1. crates.io + npm (simplest)
2. Git dependency for Rust + npm package for JS
3. Git/path dependency for both Rust and JS in monorepo/submodule setup

Install the core plugin in `src-tauri/Cargo.toml`:

```toml
[dependencies]
tauri-plugin-native-audio = "1.0.0"
# or from Git:
tauri-plugin-native-audio = { git = "https://github.com/uvarov-frontend/tauri-plugin-native-audio", tag = "v1.0.0" }
```

Install the JavaScript guest bindings:

```sh
pnpm add tauri-plugin-native-audio-api
# or
npm add tauri-plugin-native-audio-api
# or
yarn add tauri-plugin-native-audio-api
```

## Setting up

### 1. Register the plugin in Rust

`src-tauri/src/lib.rs`

```rust
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_native_audio::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

### 2. Enable permissions in capability

Add plugin permissions to your capability file (example: `src-tauri/capabilities/default.json`):

```json
{
  "identifier": "default",
  "description": "Default capability",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "native-audio:default"
  ]
}
```

`native-audio:default` includes:

- `allow-initialize`
- `allow-register-listener`
- `allow-remove-listener`
- `allow-set-source`
- `allow-play`
- `allow-pause`
- `allow-seek-to`
- `allow-set-rate`
- `allow-get-state`
- `allow-dispose`

If you need stricter ACL, use explicit allow/deny permissions from `permissions/autogenerated/commands/*.toml`.

### 3. Platform notes

Android:

- `play()` starts a foreground service (`NativeAudioService`) for media playback notification.
- On Android 13+ notification permission is requested during `initialize()`.
- Plugin manifest declares:
  - `android.permission.INTERNET`
  - `android.permission.FOREGROUND_SERVICE`
  - `android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK`
  - `android.permission.POST_NOTIFICATIONS`
  - `android.permission.WAKE_LOCK`

iOS:

- Uses `AVAudioSession` category `.playback`.
- For true background audio, enable Background Modes -> Audio in your iOS target.
- Lock screen controls and now playing metadata are integrated through `MediaPlayer` framework APIs.

## Configuration

This plugin does not require plugin-specific `tauri.conf.json > plugins.native-audio` configuration.

Runtime behavior is configured from JS commands:

- `setSource({ src, title, artist, artworkUrl })`
- `setRate(rate)`
- `seekTo(positionSeconds)`

## Usage

After plugin registration, APIs are available in JavaScript:

```ts
import {
  initialize,
  setSource,
  play,
  pause,
  seekTo,
  setRate,
  getState,
  dispose,
  addStateListener
} from "tauri-plugin-native-audio-api";

const unlisten = await addStateListener((state) => {
  console.log("native-audio state", state);
});

await initialize();
await setSource({
  src: "https://example.com/audio.mp3",
  title: "Episode 1",
  artist: "My Podcast",
  artworkUrl: "https://example.com/cover.jpg"
});
await setRate(1.25);
await play();

// later
await seekTo(42);
await pause();
console.log(await getState());

unlisten();
await dispose();
```

## JavaScript API

Types:

```ts
export type NativeAudioStatus = "idle" | "loading" | "playing" | "ended" | "error";

export type NativeAudioState = {
  status: NativeAudioStatus;
  currentTime: number;
  duration: number;
  isPlaying: boolean;
  buffering: boolean;
  rate: number;
  error?: string;
};

export type NativeAudioSetSourcePayload = {
  src: string;
  title?: string;
  artist?: string;
  artworkUrl?: string;
};
```

Commands:

- `initialize(): Promise<NativeAudioState>`
- `setSource(payload: NativeAudioSetSourcePayload): Promise<NativeAudioState>`
- `play(): Promise<NativeAudioState>`
- `pause(): Promise<NativeAudioState>`
- `seekTo(position: number): Promise<NativeAudioState>`
- `setRate(rate: number): Promise<NativeAudioState>`
- `getState(): Promise<NativeAudioState>`
- `dispose(): Promise<void>`
- `addStateListener(handler): Promise<() => void>`

Validation and errors:

- `setSource` rejects if `src` is empty.
- `seekTo` rejects if `position` is missing/non-finite.
- `setRate` rejects if `rate <= 0` or non-finite.
- Native failures can also be reflected in `NativeAudioState.error` with status `error`.

State semantics:

- `idle`: player idle or paused
- `loading`: buffering
- `playing`: active playback
- `ended`: reached media end
- `error`: runtime/player error

## Source handling

Supported source forms:

- remote URL (`https://...`, and platform-dependent `http://...`)
- local file URL (`file://...`)
- local file path

iOS extras:

- `asset://localhost/...` and `http://asset.localhost/...` resolve to local file paths.
- Local files with missing/`.bin` extension are inspected by header and remapped via temporary alias (`mp3`, `wav`, `m4a`, `ogg`) for better AVPlayer compatibility.

## Troubleshooting

`unknown command plugin:native-audio|...`

- Ensure Rust registration exists: `.plugin(tauri_plugin_native_audio::init())`
- Ensure capability includes `native-audio:default` (or explicit allow permissions)

Android notification controls not shown:

- Call `initialize()` before playback
- Start playback using `play()`
- Grant notifications permission on Android 13+

iOS background playback not working:

- Enable Background Modes -> Audio for the iOS app target
- Verify stream URL is reachable and valid

iOS HTTP URL fails:

- ATS can block plain HTTP
- Prefer HTTPS or configure ATS exceptions in app settings

## AI Notice

This plugin was fully developed with the help of AI.
No junior developers were harmed, only a few CPU cores worked overtime 😂
Yes, this duck was also invented by AI 🤣

## License

MIT or Apache-2.0, where applicable.