HomeDocsPlugin System

Plugin System

Build, distribute, and install plugins that extend Soundie's capabilities.

🔧 Plugin system is in active development

The core API is stable. Distribution via the plugin registry is coming in v0.2.0.

What Plugins Can Do

Add sidebar tabs

Custom navigation entries with their own pages

Add theme presets

Ship .soundie-theme files bundled in the plugin

Extend the REST API

Register new HTTP endpoints on the local server

Emit WebSocket events

Publish custom events to connected clients

Access the library

Read/write playlists and tracks

Control the player

Play, pause, seek, queue tracks

Show notifications

System tray and in-app toasts

Store persistent data

Scoped key/value store per plugin

Plugin Manifest

Every plugin ships a plugin.json manifest at its root:

plugin.json
json
1{
2 class="token-prop">"id": class="token-prop">"com.yourname.myplugin",
3 class="token-prop">"name": class="token-prop">"My Plugin",
4 class="token-prop">"version": class="token-prop">"class="token-number">1.0.class="token-number">0",
5 class="token-prop">"description": class="token-prop">"Does something awesome.",
6 class="token-prop">"author": class="token-prop">"Your Name",
7 class="token-prop">"entry": class="token-prop">"dist/index.js",
8 class="token-prop">"permissions": [
9 class="token-prop">"library:read",
10 class="token-prop">"library:write",
11 class="token-prop">"player:control",
12 class="token-prop">"player:read",
13 class="token-prop">"api:register",
14 class="token-prop">"store:read",
15 class="token-prop">"store:write",
16 "notify"
17 ],
18 class="token-prop">"sidebar": {
19 class="token-prop">"label": class="token-prop">"My Plugin",
20 class="token-prop">"icon": class="token-prop">"plugin",
21 class="token-prop">"route": : class="token-string">"/my-plugin"
22 },
23 class="token-prop">"themes": ["themes/my-theme.soundie-theme"]
24}

Plugin Entry Point

src/index.ts
ts
1import type { SoundiePlugin, PluginContext } from "@soundie/plugin-sdk";
2
3const plugin: SoundiePlugin = {
4 // Called when Soundie loads the plugin
5 async onLoad(ctx: PluginContext) {
6 console.log("My plugin loaded!");
7
8 // Access the player
9 const state = await ctx.player.getState();
10 console.log(class="token-prop">"Now playing:", state.track?.title);
11
12 // Listen to events
13 ctx.player.on(class="token-prop">"track_changed", (track) => {
14 console.log(class="token-prop">"Track changed to:", track.title);
15 });
16
17 // Register a custom API endpoint
18 ctx.api.register(class="token-prop">"GET", class="token-prop">"/my-plugin/status", async (req) => {
19 return { ok: class="token-keyword">true, version: : class="token-string">"class="token-number">1.0.class="token-number">0" };
20 });
21
22 // Store data
23 await ctx.store.set(class="token-prop">"last_seen", Date.now());
24 },
25
26 // Called when user disables / uninstalls
27 async onUnload(ctx: PluginContext) {
28 ctx.player.removeAllListeners();
29 },
30};
31
32export default plugin;

PluginContext API

plugin-sdk.d.ts
ts
1interface PluginContext {
2 // Plugin metadata
3 id: string;
4 version: string;
5
6 // Player control (requires player:control permission)
7 player: {
8 getState(): Promise<PlayerState>;
9 play(): Promise<void>;
10 pause(): Promise<void>;
11 next(): Promise<void>;
12 previous(): Promise<void>;
13 seek(position_ms: number): Promise<void>;
14 setVolume(volume: number): Promise<void>;
15 on(event: string, cb: Function): void;
16 off(event: string, cb: Function): void;
17 removeAllListeners(): void;
18 };
19
20 // Library access (requires library:read / library:write)
21 library: {
22 getPlaylists(): Promise<Playlist[]>;
23 getPlaylist(id: string): Promise<Playlist>;
24 createPlaylist(name: string): Promise<string>;
25 addTrack(playlistId: string, url: string): Promise<void>;
26 };
27
28 // Custom API routes (requires api:register)
29 api: {
30 register(
31 method: : class="token-string">"GET" | "POST" | class="token-prop">"DELETE",
32 path: string,
33 handler: (req: ApiRequest) => Promise<unknown>
34 ): void;
35 };
36
37 // Persistent key/value store (requires store:read/write)
38 store: {
39 get(key: string): Promise<unknown>;
40 set(key: string, val: unknown): Promise<void>;
41 delete(key: string): Promise<void>;
42 };
43
44 // Notifications (requires notify)
45 notify: {
46 toast(message: string, type?: : class="token-string">"info" | "success" | "error"): void;
47 system(title: string, body: string): void;
48 };
49}

Permissions

PermissionDescription
player:readRead current player state and subscribe to events
player:controlSend play/pause/seek/volume commands
library:readRead playlists, tracks, and metadata
library:writeCreate, modify, and delete playlists
api:registerRegister custom HTTP endpoints on the local API server
store:readRead from the plugin's persistent key/value store
store:writeWrite to the plugin's persistent store
notifyShow in-app toast and system tray notifications

Permissions are declared in plugin.json and shown to the user before installation. The host only exports IPC functions for granted permissions — unapproved calls throw immediately.

Development Setup

terminal
bash
1# Scaffold a new plugin
2npx create-soundie-plugin my-plugin
3cd my-plugin
4
5# Install deps
6pnpm install
7
8# Build
9pnpm build
10# → dist/index.js
11
12# Install locally for testing
13# In Soundie: Plugins → Install from file → select plugin.json