Build an addon for BitChord
Connect your own audio catalogue to BitChord with three small JSON endpoints. Your server searches tracks, resolves a playable stream, and describes the audio accurately.
How addons work
A listener adds either your root URL or its manifest.json URL. BitChord stores the normalized root, checks the manifest, then tries enabled addons in the order chosen by the listener. When one misses or is unavailable, resolution continues.
GET /manifest.jsonGET /search?q=…GET /stream/{id}Open returned URLWhat BitChord does with your answer
- Matches title, artist, album, and duration to avoid the wrong recording.
- Negotiates lossless, high, or low quality for the current request.
- Uses declared codec and transport metadata before guessing from a URL.
- Caches manifests and searches for 10 minutes, streams for 5 minutes.
Quickstart
Expose three routes from any HTTPS server. The example below is deliberately runtime-neutral: connect handleRequest to the router you already use.
const tracks = [
{
id: "track_8f31",
title: "Midnight Signal",
artist: "Example Artist",
album: "Night Drive",
duration: 242,
artworkURL: "https://audio.example/art/8f31.jpg",
format: "flac",
audioQuality: "LOSSLESS"
}
]
const MANIFEST = {
id: "dev.example.reference-addon",
name: "Reference Addon",
version: "1.0.0",
resources: ["search", "stream"]
}
export async function handleRequest(request) {
const url = new URL(request.url)
if (url.pathname === "/manifest.json") {
return json(MANIFEST)
}
if (url.pathname === "/search") {
const query = (url.searchParams.get("q") || "").toLowerCase()
return json({
tracks: tracks.filter((track) =>
[track.title, track.artist, track.album]
.join(" ").toLowerCase().includes(query)
)
})
}
if (url.pathname.startsWith("/stream/")) {
const id = decodeURIComponent(url.pathname.slice(8))
if (!tracks.some((track) => track.id === id)) {
return new Response(null, { status: 404 })
}
return json({
url: signedMediaUrl(id),
codec: "flac",
container: "flac",
manifest: "none",
encrypted: false,
sampleRate: 96000,
bitDepth: 24
})
}
return new Response(null, { status: 404 })
}
function json(value) {
return Response.json(value, {
headers: { "Cache-Control": "no-store" }
})
}
function signedMediaUrl(id) {
return `https://audio.example/media/${encodeURIComponent(id)}.flac`
}Manifest
Serve the manifest at GET /manifest.json. BitChord needs a stableid and a searchable addon. Unknown fields are ignored, so the document can carry additional metadata for your own tooling.
{
"id": "dev.example.reference-addon",
"name": "Reference Addon",
"version": "1.0.0",
"resources": ["search", "stream"],
"settings": [
{
"key": "quality",
"type": "select",
"default": "lossless",
"options": [
{ "label": "Lossless", "value": "lossless" },
{ "label": "High", "value": "high" },
{ "label": "Low", "value": "low" }
]
}
]
}| Field | Required | Meaning |
|---|---|---|
id | Yes | Stable, unique addon identifier. An empty id is rejected. |
name | Recommended | Human-readable name shown in the source list. |
version | Recommended | Displayed with health information. |
resources | Recommended | Include search. Stream may be omitted only when rows carry streamURL. |
settings | No | Defaults are forwarded as query parameters on search and stream requests. |
Search endpoint
Accept GET /search?q=… and return an object with atracks array. Only rows with a non-empty id and title are considered. Return results in your preferred order; BitChord keeps it while evaluating recording matches.
GET /search?q=midnight&quality=lossless&atmos=auto
{
"tracks": [
{
"id": "track_8f31",
"title": "Midnight Signal",
"artist": "Example Artist",
"album": "Night Drive",
"duration": 242,
"artworkURL": "https://audio.example/art/8f31.jpg",
"format": "flac",
"audioQuality": "LOSSLESS"
}
]
}| Field | Required | Meaning |
|---|---|---|
id | Yes | Opaque track id. It is safely encoded when used in the stream path. |
title | Yes | Track title used for matching. |
artist | Recommended | Primary artist used for matching. |
album | Recommended | Album improves match confidence. |
duration | Recommended | Length in seconds; decimals are accepted. |
artworkURL | No | Absolute artwork URL. albumArtworkURL is also accepted. |
format | Recommended | A short format hint such as flac, mp3, aac, or dash. |
audioQuality | Recommended | Free-text quality tier such as LOSSLESS or HIGH. |
streamURL | No | Direct fallback URL when the stream endpoint has no answer. |
Search has no limit parameter. Keep responses focused and fast; BitChord trims what it uses.
Stream endpoint
Accept GET /stream/{id}. Return one openable rendition for the requested quality. A 404 means “not in this addon” and cleanly moves resolution onward.
GET /stream/track_8f31?quality=lossless&atmos=auto
{
"url": "https://audio.example/media/track_8f31.flac",
"format": "flac",
"quality": "Lossless · 24-bit / 96 kHz",
"codec": "flac",
"container": "flac",
"manifest": "none",
"encrypted": false,
"sampleRate": 96000,
"bitDepth": 24,
"bitrate": 2800000
}| Field | Required | Meaning |
|---|---|---|
url | Yes | Absolute, directly playable media or manifest URL. |
codec | Recommended | Most reliable codec signal: flac, alac, mp3, aac, eac3-joc, and others. |
container | Recommended | Container when known. |
manifest | Recommended | none, hls, or dash. Declare it for extensionless manifest URLs. |
encrypted | Recommended | Use false. Protected renditions are refused. |
sampleRate | No | Sample rate in Hz; a kHz value below 1000 is also normalized. |
bitDepth | No | Integer sample bit depth. |
bitrate | No | Bits per second or kbps; BitChord normalizes either convention. |
error | No | Helpful explanation when no URL can be returned. |
Quality negotiation
BitChord sends a quality parameter on both search and stream requests. If your manifest lists options, it chooses the closest value by meaning; otherwise it sends one of the native tiers below.
Prefer FLAC, ALAC, WAV, or another lossless codec.
Return your best lossy rendition, commonly near 320 kbps.
Return a compact rendition, commonly 128 kbps or below.
Do not overstate the stream
BitChord verifies quality using the codec it actually decodes. A lossless label on a lossy URL does not create a lossless badge. Accurate codec, sample-rate, bit-depth, and bitrate fields make selection and playback diagnostics reliable.
Dolby Atmos
Dolby support has two layers: selecting the immersive recording and describing its stream. Some catalogues expose Dolby as another rendition of one id; others return a separate track id. Your addon should support both shapes.
Respond to atmos=auto
BitChord adds this hint only when the device has a compatible decoder and the listener has Dolby enabled. “Auto” means prefer Dolby when available, otherwise return stereo.
// BitChord sends this only when Dolby is enabled and supported:
GET /search?q=midnight&quality=lossless&atmos=auto
GET /stream/dolby_8f31?quality=lossless&atmos=auto
// Search result for a distinct Dolby mix:
{
"id": "dolby_8f31",
"title": "Midnight Signal",
"artist": "Example Artist",
"album": "Night Drive",
"duration": 242,
"format": "dash",
"audioModes": ["DOLBY_ATMOS"],
"atmos": true
}
// Stream response:
{
"url": "https://audio.example/media/dolby_8f31",
"format": "dash",
"quality": "Dolby Atmos",
"codec": "eac3-joc",
"container": "mp4",
"manifest": "dash",
"sampleRate": 48000,
"audioMode": "DOLBY_ATMOS",
"encrypted": false
}Dolby rules that matter
- 1Mark separate rows.
Use
atmos: true,audioMode, oraudioModes. This lets BitChord prefer the Dolby row even if its catalogue quality label says LOW. - 2Use a recognized codec.
Return
eac3-jocor a clear Dolby label. BitChord normalizes common underscore and hyphen spellings. - 3Return stereo when Dolby is absent.
With
atmos=auto, an ordinary track must still play. Do not turn “no Dolby mix” into an error. - 4Declare the transport.
Dolby is often carried by DASH. Set
manifest: "dash"even when the URL has no file extension.
Install and test
Make the root and all three routes reachable over HTTPS.
In BitChord, open Settings → Sources → Add an addon. Paste the root or manifest URL.
BitChord reads the manifest and verifies the addon is searchable.
Test direct audio, manifest-based audio, every quality tier, misses, and Dolby.
Fast contract checks
GET /manifest.jsonGET /search?q=test&quality=LOSSLESSGET /stream/<encoded-id>?quality=LOSSLESSProduction checklist
Troubleshooting
“That URL answered, but not with an addon manifest”
Return an object with a non-empty id from /manifest.json, or confirm the pasted URL resolves to your addon root.
The addon is healthy but returns no results
Check the q parameter, return a tracks array, and ensure each row has both id and title.
Audio fails on an extensionless URL
Declare manifest as hls or dash. For direct audio, provide codec and container.
A track falls back to another source
Return 404 only for a true miss. Check for an empty URL, an encrypted response, malformed URL, or unsupported Dolby stream.
Dolby always returns stereo
Read atmos=auto on both search and stream. If Dolby is a separate catalogue row, mark that row with atmos or audioModes.
The wrong recording is selected
Improve artist, album, and duration metadata. Avoid live, edit, remix, or instrumental variants unless the query asks for them.
Ship the smallest honest contract.
Three routes, accurate metadata, graceful misses. BitChord handles the rest.
Review the quickstart