Kanade Scripting System (KSS) allows you to extend the app's functionality by writing JavaScript plugins to fetch music from external sources. These scripts run in a sandboxed QuickJS environment on Android.
A KSS script is a single .js file consisting of two parts:
- Manifest: A JSON block inside a JSDoc-style comment at the top of the file.
- Logic: The JavaScript functions that handle data fetching.
/**
* @kanade_script
* {
* "id": "my_provider",
* "name": "My Custom Provider",
* "version": "1.0.0",
* "author": "YourName",
* "description": "Fetches music from my private API",
* "configs": [
* { "key": "api_key", "label": "API Key", "type": "string", "default": "" },
* { "key": "quality", "label": "Audio Quality", "type": "select", "default": "128k", "options": ["128k", "320k", "flac"] }
* ]
* }
*/| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier for the provider. |
name |
string |
Display name in the UI. |
version |
string |
Semantic versioning (e.g., 1.2.3). |
author |
string |
Your name or handle. |
description |
string? |
Optional short description. |
configs |
array? |
List of user-configurable variables. |
key: The internal key used in code.label: Display name in the Settings UI.type: One of"string","number","boolean", or"select".default: Default value (as a string).options: (Forselecttype) List of possible string values.
You must implement the following functions. They can be standard functions or async functions.
Called once when the engine starts or when settings change.
config: A JavaScript object containing current user settings.- Note: Values are provided based on the
typedefined in the manifest. - Migration (v1.1): Previously,
configwas passed as a JSON string. In v1.1+, it is a native JS object. If your script usesJSON.parse(config), please remove it or add a type check:if (typeof config === "string") config = JSON.parse(config);.
Called to populate the "Discover" section on the Library screen.
- Returns:
MusicItem[]
Called when the user performs a search.
query: The search string.page: Page number (starting from 1).- Returns:
MusicItem[]
Called when a track is about to be played.
id: The internal ID of the music item.- Returns:
StreamInfoobject.
Called to fetch lyrics for a specific track.
- Returns: A raw string (
LRCorTTMLformat).
Batch fetch music details for a list of IDs.
- Returns:
MusicItem[]
{
id: "track_123", // String
title: "Song Title", // String
artist: "Artist A, B",// String (comma separated for multiple)
album: "Album Name", // String (optional)
cover: "https://...", // String (URL, optional)
duration: 180 // Number (Seconds, optional)
}{
url: "https://...", // Playable media URL
format: "mp3", // "mp3", "flac", "m4a", etc.
headers: { // Optional HTTP headers for the player
"User-Agent": "Kanade/1.0",
"Referer": "https://mysite.com"
}
}The following global objects are provided by the Kanade host:
console.log/info/debug/warn/error(...args): Standard logging.
http.get(url, options): Performs a network request.http.post(url, body, options): Performs an HTTP POST request.
Options:
headers:{ "Key": "Value" }responseType:"text"or"hex"(for binary data).contentType: (POST only) e.g.,"application/json".
crypto.md5(text): Returns MD5 hex string.crypto.aesEncrypt(text, key, mode, padding): AES encryption.crypto.aesDecrypt(hex, key, mode, padding): AES decryption.- Modes:
"CBC","ECB", etc. - Padding:
"PKCS5Padding","NoPadding", etc.
- Modes:
/**
* @kanade_script
* {
* "id": "boilerplate",
* "name": "Boilerplate Provider",
* "version": "1.1.0",
* "author": "Kanade",
* "configs": [
* { "key": "user", "label": "Username", "type": "string", "default": "Guest" }
* ]
* }
*/
let settings = {};
function init(config) {
settings = config;
}
function getHomeList() {
return [
{ id: "h1", title: "Hello " + settings.user, artist: "System" }
];
}
async function search(query, page) {
// const results = await http.get("https://api.example.com/search?q=" + query);
// return JSON.parse(results).items;
return [];
}
function getMediaUrl(id) {
return {
url: "https://example.com/stream/" + id + ".mp3",
format: "mp3"
};
}