mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-02-04 07:52:55 +00:00
6.7 KiB
6.7 KiB
Lyrics Plugin Development Guide
This guide explains how to create a lyrics plugin for Spotube, similar to the existing audio source plugins.
Overview
Spotube uses Hetu Script for its plugin system. Lyrics plugins are packaged as .smplug files (ZIP archives) containing:
plugin.json- Plugin metadata and configurationplugin.out- Compiled Hetu bytecodelogo.png- Optional plugin logo
Plugin Structure
your-lyrics-plugin/
├── plugin.json # Plugin configuration
├── src/
│ └── main.ht # Hetu script source
├── logo.png # Optional logo
└── README.md
1. Create plugin.json
{
"name": "LrcLib Lyrics",
"author": "YourName",
"description": "Lyrics provider using LrcLib API",
"version": "1.0.0",
"pluginApiVersion": "2.0.0",
"entryPoint": "LrcLibLyricsPlugin",
"repository": "https://github.com/yourusername/spotube-plugin-lrclib-lyrics",
"apis": [],
"abilities": ["lyrics"]
}
Key Fields:
name: Display name of your pluginauthor: Your name or organizationversion: Plugin version (semver)pluginApiVersion: Must be "2.0.0" for current SpotubeentryPoint: Main class name in your Hetu scriptabilities: Must include "lyrics" for lyrics pluginsapis: Empty array for lyrics plugins (used for metadata/audio plugins)
2. Write Hetu Script (src/main.ht)
import 'package:spotube_plugin/spotube_plugin.dart'
class LrcLibLyricsPlugin {
// Called when plugin is loaded
LrcLibLyricsPlugin() {
print('LrcLib Lyrics Plugin initialized')
}
// Lyrics API endpoint
var lyrics = LyricsEndpoint()
}
class LyricsEndpoint {
// Search for lyrics
// Returns: List<Map<String, dynamic>>
external fun search(Map params) async {
final trackName = params['trackName']
final artistName = params['artistName']
final albumName = params['albumName']
final duration = params['duration'] // in seconds
// Make HTTP request to LrcLib API
final url = 'https://lrclib.net/api/search?track_name=$trackName&artist_name=$artistName'
final response = await http.get(url)
final data = json.decode(response.body)
// Transform to Spotube format
final results = []
for (var item in data) {
results.add({
'id': item['id'].toString(),
'name': item['name'],
'uri': 'lrclib:${item['id']}',
'rating': item['rating'] ?? 0,
'provider': 'lrclib',
'lyrics': parseLrc(item['syncedLyrics'] ?? item['plainLyrics'])
})
}
return results
}
// Get lyrics by ID
// Returns: Map<String, dynamic> or null
external fun getById(String id) async {
final url = 'https://lrclib.net/api/get/$id'
final response = await http.get(url)
if (response.statusCode != 200) {
return null
}
final data = json.decode(response.body)
return {
'id': data['id'].toString(),
'name': data['name'],
'uri': 'lrclib:${data['id']}',
'rating': data['rating'] ?? 0,
'provider': 'lrclib',
'lyrics': parseLrc(data['syncedLyrics'] ?? data['plainLyrics'])
}
}
// Check if service is available
external fun isAvailable() async {
try {
final response = await http.get('https://lrclib.net/api/health')
return response.statusCode == 200
} catch (e) {
return false
}
}
// Parse LRC format to lyrics array
fun parseLrc(String lrcContent) {
if (lrcContent == null || lrcContent.isEmpty) {
return []
}
final lines = lrcContent.split('\n')
final lyrics = []
for (var line in lines) {
// Parse [mm:ss.xx] format
final match = RegExp(r'\[(\d+):(\d+)\.(\d+)\](.*)').firstMatch(line)
if (match != null) {
final minutes = int.parse(match.group(1))
final seconds = int.parse(match.group(2))
final centiseconds = int.parse(match.group(3))
final text = match.group(4).trim()
final timeMs = (minutes * 60 + seconds) * 1000 + centiseconds * 10
lyrics.add({
'time': timeMs,
'text': text
})
}
}
return lyrics
}
}
3. Expected Data Format
Search Parameters (input)
{
'trackName': String,
'artistName': String,
'albumName': String?, // optional
'duration': int? // optional, in seconds
}
Search Results (output)
[
{
'id': String,
'name': String,
'uri': String,
'rating': int,
'provider': String,
'lyrics': [
{
'time': int, // milliseconds
'text': String
}
]
}
]
4. Compile Plugin
Install Hetu Compiler
dart pub global activate hetu_script_dev_tools
Compile Script
hetu compile src/main.ht -o plugin.out
5. Package Plugin
Create a ZIP file with .smplug extension:
zip your-plugin.smplug plugin.json plugin.out logo.png
Or using PowerShell:
Compress-Archive -Path plugin.json,plugin.out,logo.png -DestinationPath your-plugin.smplug
6. Test Plugin
- Open Spotube
- Go to Settings > Plugins
- Click "Add Plugin"
- Paste your plugin URL or select the
.smplugfile - Set as default lyrics provider
7. Distribute Plugin
GitHub Release
- Create a GitHub repository
- Create a release with your
.smplugfile - Users can install by pasting the repo URL
Direct Download
Host the .smplug file and share the direct download link
Example Plugins to Reference
- spotube-plugin-youtube-audio: Audio source plugin
- spotube-plugin-musicbrainz-listenbrainz: Metadata plugin
API Reference
Available in Hetu Scripts
http.get(url)- HTTP GET requesthttp.post(url, body)- HTTP POST requestjson.encode(obj)- JSON encodingjson.decode(str)- JSON decodinglocalStorage.get(key)- Get stored valuelocalStorage.set(key, value)- Store valueprint(message)- Debug logging
Common Issues
- Plugin API Version Mismatch: Ensure
pluginApiVersionis "2.0.0" - Entry Point Not Found: Class name must match
entryPointin plugin.json - Compilation Errors: Check Hetu syntax, use
external funfor async methods - Missing Abilities: Must include "lyrics" in abilities array
Testing Checklist
- Plugin loads without errors
- Search returns results for known songs
- Lyrics are time-synced correctly
- getById returns correct lyrics
- isAvailable returns true when service is up
- Handles network errors gracefully
- Works on all platforms (Windows, macOS, Linux, Android, iOS)
Support
For questions and issues:
- Spotube Discord: https://discord.gg/spotube
- GitHub Issues: https://github.com/KRTirtho/spotube/issues