Welcome to the Audion plugin development guide. This document covers everything you need to know to create extensions for Audion.
Audion's plugin system allows you to extend the player's functionality using JavaScript or WebAssembly. Plugins run in a sandboxed environment but can interact with the player through a permission-based API.
With plugins, you can:
- Control Playback: Play, pause, seek, and manage the queue.
- Read State: Access current track info, playback time, and library data.
- Inject UI: Add custom buttons, panels, and menu items to the interface.
- Store Data: Persist settings or plugin-specific data locally.
- Resolve Streams: Integrate external music sources like streaming services.
- Navigate to the
plugin-examplesdirectory. - Create a new folder for your plugin, e.g.,
my-plugin. - Create a
plugin.jsonmanifest file. - Create an
index.jsentry point file.
The manifest defines your plugin's metadata and requested permissions.
{
"name": "My Plugin",
"version": "1.0.0",
"description": "A brief description of what my plugin does.",
"author": "Your Name",
"main": "index.js",
"type": "js",
"permissions": [
"player:read",
"ui:inject"
]
}The entry point must define a global object matching your plugin's name (spaces removed) or AudionPlugin.
(function() {
const MyPlugin = {
name: 'My Plugin',
// Called when the plugin is loaded
init(api) {
this.api = api;
console.log('My Plugin initialized!');
// Subscribe to events
api.on('trackChange', (data) => {
console.log('Track changed:', data.track.title);
});
},
// Called when the plugin is enabled by the user
start() {
console.log('My Plugin started');
},
// Called when the plugin is disabled
stop() {
console.log('My Plugin stopped');
},
// Called when the plugin is completely unloaded
destroy() {
// Clean up resources
}
};
// Register the plugin
window.MyPlugin = MyPlugin;
})();The api object passed to init() provides access to Audion's core features.
init(api): Setup your plugin and save theapireference.start(): Activate logic (e.g., start timers, show UI).stop(): Deactivate logic.destroy(): Final cleanup.
Listen to player events.
api.on('trackChange', ({ track, previousTrack }) => { ... });
api.on('playbackState', ({ isPlaying }) => { ... });
api.on('timeUpdate', ({ currentTime, duration }) => { ... });
api.on('volumeChange', ({ volume }) => { ... });Requires player:control permission.
api.player.play()api.player.pause()api.player.next()api.player.prev()api.player.seek(seconds)api.player.setTrack(trackObject)api.player.addToQueue(tracks)
Requires player:read permission.
api.player.getCurrentTrack()api.player.isPlaying()api.player.getCurrentTime()api.player.getDuration()api.player.getQueue()
Requires player:control permission.
Register resolvers for external music sources (e.g., Tidal, Spotify).
// Register a resolver for 'my-service' tracks
api.stream.registerResolver('my-service', async (externalId, options) => {
// Fetch and return the actual stream URL
const url = await fetchStreamUrl(externalId);
return url;
});Requires library:write permission.
Add external tracks to the library or initiate downloads.
// Add an external track to the library
await api.library.addExternalTrack({
title: "Song Title",
artist: "Artist Name",
album: "Album Name",
cover_url: "https://example.com/cover.jpg",
source_type: "my-service", // Matches your resolver
external_id: "12345",
duration: 180 // seconds
});
// Download a track
await api.library.downloadTrack({
url: "https://example.com/audio.mp3",
filename: "Song.mp3",
metadata: {
title: "Song",
artist: "Artist"
}
});Requires ui:inject permission.
Inject custom HTML elements into specific slots in the app.
const element = document.createElement('div');
element.innerText = "Hello World";
api.ui.registerSlot('sidebar:top', element, 10); // Priority 10sidebar:top: Top of the sidebar, below the logo.sidebar:bottom: Bottom of the sidebar, above the "Add Music" button.playerbar:left: Left side of player bar (near track info).playerbar:right: Right side of player bar (near volume).playerbar:menu: Dedicated popup menu for plugins (triggered by plugin icon).
Requires storage:local permission.
Persist simple data strings.
// Save data
await api.storage.set('my-key', 'some value');
// Retrieve data
const value = await api.storage.get('my-key');You must request permissions in plugin.json to use these features.
| Permission | Description |
|---|---|
player:read |
Read current track, time, status, and queue. |
player:control |
Control playback (play, pause, seek, internal queue). |
library:read |
Access the entire music library and playlists. |
library:write |
Modify library (scan, add external tracks). |
storage:local |
Save and load plugin-specific data. |
ui:inject |
Render custom UI elements into app slots. |
system:notify |
Send native system notifications. |
network:fetch |
Make network requests (for streaming/metadata). |
Plugins can inject their own <style> tags or use inline styles. Audion provides CSS variables for theming that you should use to blend in.
.my-plugin-element {
background-color: var(--bg-surface);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: var(--spacing-md);
}Common Variables:
- Colors:
--bg-base,--bg-surface,--text-primary,--text-secondary,--accent-primary. - Spacing:
--spacing-sm,--spacing-md,--spacing-lg. - Radius:
--radius-sm,--radius-md.
Currently, plugins are installed manually by placing them in the plugins directory. A marketplace feature is planned for future releases.