-
-
Notifications
You must be signed in to change notification settings - Fork 12
Creating Lyrics Addons
ivLis edited this page Jan 22, 2026
·
1 revision
This guide explains how to create custom lyrics provider addons for ivLyrics.
Lyrics Addons provide lyrics from various sources. They are registered with LyricsAddonManager and users can enable/disable them and set priority order.
(function() {
'use strict';
const addon = {
// Required: Unique identifier
id: 'my-lyrics-provider',
// Required: Display name
name: 'My Lyrics Provider',
// Required: Author name
author: 'Your Name',
// Required: Description
description: {
en: 'Fetch lyrics from My Service',
ko: 'My Service에서 가사 가져오기'
},
// Required: Supported lyrics types
supports: {
karaoke: false, // Word-by-word sync
synced: true, // Line-by-line sync
unsynced: true // No timing
},
// Optional: ivLyrics Sync support
supportsIvSync: false,
// Optional: Settings UI
getSettingsUI: function() {
return null;
},
// Optional: Initialize addon
init: async function() {
console.log('My Lyrics Provider initialized');
},
// Required: Get lyrics
getLyrics: async function(info) {
// info: { uri, title, artist, album, duration }
// Return: LyricsResult or null
}
};
function register() {
if (window.LyricsAddonManager) {
window.LyricsAddonManager.register(addon);
} else {
setTimeout(register, 100);
}
}
register();
})();- Unique identifier string
- Use lowercase with hyphens
- Example:
'my-lyrics-provider'
- Display name shown in settings
- Example:
'My Lyrics Provider'
- Creator name
- Example:
'Your Name'
Can be string or localized object:
description: {
en: 'English description',
ko: '한국어 설명'
}Define which lyrics types the provider can return:
supports: {
karaoke: false, // Word-by-word highlighting
synced: true, // Line-level timestamps
unsynced: true // Plain lyrics
}{
type: 'unsynced',
provider: 'my-lyrics-provider',
lines: [
{ text: 'First line of lyrics' },
{ text: 'Second line of lyrics' },
{ text: '' }, // Empty line for spacing
{ text: 'Third line of lyrics' }
]
}{
type: 'synced',
provider: 'my-lyrics-provider',
lines: [
{
startTime: 0, // milliseconds
endTime: 3500, // milliseconds (optional)
text: 'First line'
},
{
startTime: 3500,
endTime: 7000,
text: 'Second line'
}
]
}{
type: 'karaoke',
provider: 'my-lyrics-provider',
lines: [
{
startTime: 0,
endTime: 3500,
text: 'First line words',
words: [
{ startTime: 0, endTime: 800, text: 'First' },
{ startTime: 800, endTime: 1500, text: 'line' },
{ startTime: 1500, endTime: 3500, text: 'words' }
]
}
]
}async getLyrics(info) {
// info object contains:
// - uri: Spotify track URI (e.g., 'spotify:track:xxx')
// - title: Track title
// - artist: Artist name
// - album: Album name
// - duration: Track duration in milliseconds
}// Success with lyrics
return {
type: 'synced',
provider: 'my-lyrics-provider',
lines: [...]
};
// No lyrics found
return null;
// Error (also return null, optionally log error)
console.error('Failed to fetch lyrics:', error);
return null;(function() {
'use strict';
const addon = {
id: 'my-lrc-provider',
name: 'My LRC Provider',
author: 'Developer',
description: 'Fetch LRC format lyrics',
supports: {
karaoke: false,
synced: true,
unsynced: true
},
getLyrics: async function(info) {
try {
// Build search URL
const url = new URL('https://api.example.com/lyrics');
url.searchParams.set('track', info.title);
url.searchParams.set('artist', info.artist);
const response = await fetch(url.toString());
if (!response.ok) {
return null;
}
const data = await response.json();
if (!data.lyrics) {
return null;
}
// Parse LRC format
if (data.syncedLyrics) {
return this.parseSyncedLyrics(data.syncedLyrics);
} else if (data.plainLyrics) {
return this.parsePlainLyrics(data.plainLyrics);
}
return null;
} catch (error) {
console.error('[MyLRCProvider] Error:', error);
return null;
}
},
parseSyncedLyrics: function(lrcText) {
const lines = [];
const regex = /\[(\d{2}):(\d{2})\.(\d{2,3})\](.*)/g;
let match;
while ((match = regex.exec(lrcText)) !== null) {
const minutes = parseInt(match[1]);
const seconds = parseInt(match[2]);
const ms = parseInt(match[3].padEnd(3, '0'));
const text = match[4].trim();
const startTime = (minutes * 60 + seconds) * 1000 + ms;
lines.push({ startTime, text });
}
// Calculate end times
for (let i = 0; i < lines.length - 1; i++) {
lines[i].endTime = lines[i + 1].startTime;
}
if (lines.length > 0) {
lines[lines.length - 1].endTime =
lines[lines.length - 1].startTime + 5000;
}
return {
type: 'synced',
provider: 'my-lrc-provider',
lines
};
},
parsePlainLyrics: function(text) {
const lines = text.split('\n').map(line => ({
text: line.trim()
}));
return {
type: 'unsynced',
provider: 'my-lrc-provider',
lines
};
}
};
function register() {
if (window.LyricsAddonManager) {
window.LyricsAddonManager.register(addon);
} else {
setTimeout(register, 100);
}
}
register();
})();Get/set addon-specific settings:
// Get setting
const value = window.LyricsAddonManager.getAddonSetting(
'my-lyrics-provider',
'settingKey',
'defaultValue'
);
// Set setting
window.LyricsAddonManager.setAddonSetting(
'my-lyrics-provider',
'settingKey',
'newValue'
);Add your addon to manifest.json:
{
"subfiles_extension": [
"Addon_Lyrics_MyProvider.js"
]
}- Create your addon file
- Add to manifest.json
- Run
spicetify apply - Open Settings > Lyrics Providers
- Enable your provider and adjust priority
- Play a song to test
- Search Accuracy: Use fuzzy matching for title/artist
- Fallback: Try multiple search strategies
- Error Handling: Return null gracefully on errors
- Performance: Cache results when possible
- Rate Limiting: Respect API limits
Users can reorder providers. The manager tries providers in order until one returns lyrics:
User Priority: [Spotify, MyProvider, LRCLIB]
1. Try Spotify → No lyrics → Continue
2. Try MyProvider → Returns lyrics → Stop
- Architecture Overview - Project structure
- Core Modules - LyricsAddonManager details
- Creating AI Addons - AI provider guide