Plugin Development Guide

This guide walks you through creating plugins for youtube-music-cli.

Getting Started

Prerequisites

Quick Start

  1. 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
    
  2. Install locally for testing:

    youtube-music-cli plugins install /path/to/my-plugin
    
  3. 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:

UI Template

Location: templates/plugin-ui/

A plugin that adds UI functionality:

Audio Template

Location: templates/plugin-audio/

A plugin for audio stream processing:

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:

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

  1. Create a GitHub repo for your plugin
  2. Users install with:
    youtube-music-cli plugins install https://github.com/you/my-plugin
    

Option 2: Default Plugin Repository

  1. Fork https://github.com/involvex/youtube-music-cli-plugins
  2. Add your plugin to the plugins/ directory
  3. Submit a pull request
  4. 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:

Permission denied:

Events not firing:

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?