Return a lossless encoding such as FLAC, ALAC, or PCM audio.
Build an addon for Audioly
Bring an audio catalogue into Audioly through a small HTTP service. Publish metadata, return search matches, and resolve a track to a playable URL.
Audioly reads their JSON responses. It does not download or execute code from an HTTP addon. JavaScript modules use a separate system.
How addons work
The source manager accepts a base URL or a manifest URL, normalizes it, and reads the manifest. Sources are queried according to the listener’s configured order.
- Discover
GET /manifest.json - Search
GET /search?q=… - Resolve
GET /stream/{id} - PlayOpen the returned media URL
Matching uses recording metadata such as the title, artist, album, and duration. A missing recording can fall through to the next source. Manifests and searches are cached for ten minutes; stream responses for five minutes.
Quickstart
This example uses Node’s built-in HTTP server. Replace the demonstration catalogue and media URL with content your server can serve, run node server.mjs, and put the service behind HTTPS before installing it on a device.
// Save as server.mjs. Node.js 18+; no dependencies.
import { createServer } from 'node:http';
const track = { id: 'demo-01', title: 'First Light',
artist: 'Demo Artist', duration: 214, format: 'flac' };
const send = (res, status, body) => {
res.writeHead(status, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(body));
};
createServer((req, res) => {
const url = new URL(req.url, 'http://localhost');
if (req.method !== 'GET') return send(res, 405, { error: 'GET only' });
if (url.pathname === '/manifest.json') return send(res, 200, {
id: 'me.audioly.example', name: 'My Audio Catalogue',
version: '1.0.0', resources: ['search', 'stream']
});
if (url.pathname === '/search') {
const q = (url.searchParams.get('q') || '').toLowerCase();
return send(res, 200, { tracks:
`${track.title} ${track.artist}`.toLowerCase().includes(q) ? [track] : [] });
}
if (url.pathname === '/stream/demo-01') return send(res, 200, {
url: 'https://audio.example/media/demo-01.flac',
codec: 'flac', container: 'flac', manifest: 'none', encrypted: false
});
return send(res, 404, { error: 'Not found' });
}).listen(8080);The sample starts a server, but audio.example does not host playable audio. Supply an absolute media URL that your player can access.
Manifest
Serve a JSON object at /manifest.json. Keep the addon ID stable between deployments. New or unused properties are tolerated by the parser.
{
"id": "me.audioly.example",
"name": "My Audio Catalogue",
"version": "1.0.0",
"resources": [
"search",
"stream"
],
"allowDownloads": 1,
"checkValidLossless": 0,
"settings": [
{
"key": "quality",
"default": "lossless",
"options": [
{
"value": "lossless"
},
{
"value": "high"
},
{
"value": "low"
}
]
}
]
}| Field | Details |
|---|---|
id | Required: a non-empty identifier for this source. |
name / version | Source name and release identifier. |
resources | Declare search and stream. Search is necessary when resources are declared; direct streamURL rows can support a source without a stream declaration. |
settings | Scalar default values are sent as query parameters. The app does not create a settings form from this schema. |
allowDownloads | Omitted means enabled. Set 0 to keep this addon out of download resolution. |
checkValidLossless | Omitted means disabled. Set 1 to require a lossless-capable output before this source is used. |
An empty resources list is tolerated. Explicitly declaring your supported routes is preferable. A requested quality is negotiated separately from other setting defaults.
Search endpoint
Accept a GET request with q, and return a tracks array. Include enough metadata to distinguish the intended recording from remixes, live versions, and covers.
GET /search?q=first%20light&quality=lossless{
"tracks": [
{
"id": "demo-01",
"title": "First Light",
"artist": "Demo Artist",
"album": "Open Skies",
"duration": 214,
"artworkURL": "https://audio.example/art/demo-01.jpg",
"format": "flac",
"audioQuality": "LOSSLESS"
}
]
}| Field | Details |
|---|---|
id / title | Non-empty track identifier and title. |
artist / album | Recording metadata used for matching. |
duration | Track length in seconds; fractional values are accepted. |
artworkURL | Absolute cover URL. albumArtworkURL is also supported. |
format / audioQuality | Format and quality hints, such as flac and LOSSLESS. |
streamURL | Optional direct stream fallback. |
atmos / audioMode / audioModes | Identify separate immersive recordings; see Dolby Atmos. |
Return an empty array when a search has no matches. There is no required limit parameter.
Stream endpoint
Resolve the URL-encoded identifier with GET /stream/{id}. Return one rendition for the requested quality. Use 404 for an unavailable recording.
{
"url": "https://audio.example/media/demo-01.flac",
"format": "flac",
"quality": "24-bit / 96 kHz",
"codec": "flac",
"container": "flac",
"manifest": "none",
"encrypted": false,
"sampleRate": 96000,
"bitDepth": 24,
"bitrate": 2400000
}| Field | Details |
|---|---|
url | Absolute playable media URL; required for a successful stream answer. |
codec / container | State the actual codec and media container. |
manifest | none, hls, or dash. Use this field for extensionless manifest URLs. |
encrypted | Declare false for clear media. Protected streams are rejected. |
sampleRate / bitDepth | Sample rate in Hz and integer sample depth. |
bitrate | Bitrate is normalized from bps or kbps. |
quality / format | Descriptive labels; they do not change the actual decoded format. |
error | An explanation when there is no playable result. |
Quality negotiation
Search and stream requests carry a quality value. When the manifest defines quality choices, Audioly selects a value matching the requested tier. Otherwise it sends its native tier.
Return the best available lossy rendition.
Return a smaller rendition suitable for metered connections.
A quality label cannot turn an MP3 into a lossless recording. The decoder and actual stream information determine playback quality.
Dolby Atmos
A compatible decoder and the listener’s Dolby setting allow Audioly to send atmos=auto. Prefer an immersive mix when available and keep a stereo fallback when it is not.
GET /stream/demo-01?quality=lossless&atmos=auto{
"url": "https://audio.example/media/demo-01.mpd",
"format": "dash",
"quality": "Dolby Atmos",
"codec": "eac3-joc",
"container": "mp4",
"manifest": "dash",
"encrypted": false,
"sampleRate": 48000,
"bitrate": 2400000,
"audioMode": "DOLBY_ATMOS"
}For a separate mix in the search results, use atmos: true, audioMode: "DOLBY_ATMOS", or an audioModes array. Provide its own track ID when the catalogue requires it.
E-AC-3 JOC can provide an immersive mix without being a bit-exact lossless codec. Always declare its codec and transport accurately.
Download permission
allowDownloads controls whether Audioly can use this addon when saving audio. The default is enabled; set it to 0 or false for a source intended for playback only.
{ "id": "me.audioly.playback", "resources": ["search", "stream"], "allowDownloads": 0 }Playback remains available. A download request can continue with another eligible source. Make sure expiring media URLs remain valid long enough to finish an allowed download.
Lossless output gate
checkValidLossless: 1 limits an addon to an output path that can carry lossless audio. The Android implementation checks paths such as wired, USB, HDMI, and supported high-quality Bluetooth configurations.
{ "id": "me.audioly.lossless", "resources": ["search", "stream"], "checkValidLossless": 1 }Leave this unset or use 0 for a source that should remain available on ordinary outputs. This switch is an eligibility rule; it does not improve the media encoding.
Install and test
- Publish the addon service on an HTTPS URL.
- Open Audioly’s Settings and Sources screen.
- Add the server root or its manifest.json URL and check its health.
- Enable the source and position it in the source order.
- Try a known recording, each quality tier, and a missing track.
- Check playback, seeking, and downloads where permitted.
curl -fsS https://YOUR-HOST/manifest.json
curl -fsS "https://YOUR-HOST/search?q=first%20light&quality=lossless"
curl -fsS "https://YOUR-HOST/stream/demo-01?quality=lossless"These HTTP examples test the server contract. Validate the returned media URL separately on a real device.
Production checklist
- Use HTTPS and a stable source identifier.
- Return JSON with the correct Content-Type.
- Encode track identifiers correctly and publish absolute asset URLs.
- Serve only media your catalogue is authorized to provide.
- Support 404 misses, clear error responses, and sensible rate limits.
- Use Retry-After when returning 429.
- Declare HLS or DASH explicitly, including on URLs without extensions.
- Keep URLs valid beyond the five-minute stream cache window.
- Verify metadata against the real codec, rate, and bit depth.
- Test stereo fallback, source ordering, and download restrictions.
Troubleshooting
| Field | Details |
|---|---|
Manifest rejected | Check the JSON response, non-empty id, and declared search capability. An HTML error page is not a manifest. |
Nothing found | Check q handling, the tracks array, and non-empty ids and titles. Compare recording metadata. |
Track will not play | Open the absolute media URL, check expiry and encryption, and verify codec/container/manifest declarations. |
No lossless badge | Inspect the real encoding and returned metadata. Renaming the quality field does not change the stream. |
No immersive mix | Check the device decoder, Dolby setting, atmos request, separate track IDs, and eac3-joc metadata. |
Downloads skip addon | Check allowDownloads and whether the source is enabled. |
Source skipped on Bluetooth | Review checkValidLossless and the active output capabilities. |
429 / rate limit | Respect the retry interval. Audioly temporarily quiets requests to a source after a rate-limit response. |
For an app issue, include reproducible steps and sanitized responses in the Audioly issue tracker. Keep tokens and signed URLs out of public reports.
Audioly addon protocol · View the implementation ↗