Plugin Development Guide
This guide walks you through creating plugins for youtube-music-cli.
Getting Started
Prerequisites
- Node.js 16+
- Bun (recommended) or npm
- Basic TypeScript knowledge
Quick Start
-
Create from template:
# Clone a template cp -r templates/plugin-basic my-plugin cd my-plugin # Edit plugin.json with your plugin info # Edit index.ts with your plugin logic -
Install locally for testing:
youtube-music-cli plugins install /path/to/my-plugin -
Enable the plugin:
youtube-music-cli plugins enable my-plugin
Plugin Templates
We provide three templates to get you started:
Basic Template
Location: templates/plugin-basic/
A minimal plugin that demonstrates:
- Event listening (play, pause, track-change)
- Using the logger API
- Lifecycle hooks
UI Template
Location: templates/plugin-ui/
A plugin that adds UI functionality:
- Custom keyboard shortcuts
- View registration
- Navigation integration
Audio Template
Location: templates/plugin-audio/
A plugin for audio stream processing:
- Stream URL interception
- Audio filtering/transformation
- Error handling
Writing Your Plugin
1. Create the Manifest (plugin.json)
{
"id": "my-awesome-plugin",
"name": "My Awesome Plugin",
"version": "1.0.0",
"description": "Does awesome things",
"author": "Your Name",
"main": "index.ts",
"permissions": ["player"],
"hooks": ["track-change"]
}
Important fields:
id: Unique identifier (lowercase, hyphens allowed)permissions: Only request what you needhooks: Declare which events you’ll listen to
2. Write the Plugin Code (index.ts)
import type {Plugin, PluginManifest, PluginContext} from 'youtube-music-cli';
const manifest: PluginManifest = {
// ... same as plugin.json
};
const plugin: Plugin = {
manifest,
async init(context) {
// Called once when plugin is loaded
context.logger.info('Plugin loaded!');
// Register event handlers
context.on('track-change', event => {
// Do something when track changes
context.logger.info('Now playing:', event.track?.title);
});
},
async enable(context) {
// Called when plugin is enabled
context.logger.info('Plugin enabled');
},
async disable(context) {
// Called when plugin is disabled
// Clean up resources here
},
async destroy(context) {
// Called when plugin is unloaded
// Final cleanup
},
};
export default plugin;
export {manifest};
3. Test Your Plugin
# Install locally
youtube-music-cli plugins install /path/to/my-plugin
# List plugins to verify
youtube-music-cli plugins list
# Enable it
youtube-music-cli plugins enable my-awesome-plugin
# Run the app to test
youtube-music-cli
Best Practices
1. Request Minimal Permissions
Only request permissions you actually need. Users are more likely to trust plugins with fewer permissions.
2. Handle Errors Gracefully
Wrap your code in try-catch blocks to prevent crashing the main app:
context.on('track-change', async event => {
try {
await doSomething(event.track);
} catch (error) {
context.logger.error('Failed:', error);
}
});
3. Clean Up in disable/destroy
Always unregister shortcuts and clean up resources:
async disable(context) {
context.unregisterShortcut(['ctrl+shift+m']);
// Cancel any pending operations
}
4. Use the Logger
Use context.logger instead of console.log for proper log formatting and filtering.
5. Store Data Properly
Use context.filesystem for persistent data:
// Save data
await context.filesystem.writeFile('cache.json', JSON.stringify(data));
// Load data
const data = JSON.parse(await context.filesystem.readFile('cache.json'));
6. Use Config for Settings
Store user-configurable settings with context.config:
// Read setting with default
const enabled = context.config.get('notifications', true);
// Save setting
context.config.set('notifications', false);
Publishing Your Plugin
Option 1: GitHub Repository
- Create a GitHub repo for your plugin
- Users install with:
youtube-music-cli plugins install https://github.com/you/my-plugin
Option 2: Default Plugin Repository
- Fork https://github.com/involvex/youtube-music-cli-plugins
- Add your plugin to the
plugins/directory - Submit a pull request
- Once merged, users can install with:
youtube-music-cli plugins install my-plugin
Debugging
Enable Debug Logging
Set the DEBUG environment variable:
DEBUG=* youtube-music-cli
Check Plugin Logs
Plugin logs are prefixed with the plugin name:
[My Plugin] Plugin loaded!
[My Plugin] Now playing: Song Title
Common Issues
Plugin not loading:
- Check
plugin.jsonsyntax - Verify
mainpoints to correct file - Check for TypeScript errors
Permission denied:
- Ensure permission is declared in manifest
- Check if user granted permission
Events not firing:
- Verify hook is declared in manifest
- Check event type spelling
- Ensure plugin is enabled
Examples
Now Playing Notifications
async init(context) {
context.on('track-change', (event) => {
if (event.track) {
// Show system notification
showNotification(event.track.title, event.track.artists[0]?.name);
}
});
}
Custom Keyboard Shortcut
async init(context) {
context.registerShortcut(['ctrl+l'], () => {
const track = context.player.getCurrentTrack();
if (track) {
// Open lyrics view
context.navigation.navigate('lyrics');
}
});
}
Audio Stream Filter (Adblock)
async init(context) {
const blocklist = ['known-ad-id-1', 'known-ad-id-2'];
context.audio.onStreamRequest(async (url, track) => {
if (blocklist.includes(track.videoId)) {
context.logger.info('Blocked ad:', track.title);
return null; // Skip this track
}
return url; // Allow playback
});
}
Need Help?
- Check the Plugin API Reference
- Open an issue on GitHub
- Join our Discord community