Audioly Docs

Overview

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.

3 HTTP endpointsJSON responsesGET requests
HTTP addons are server connections.

Audioly reads their JSON responses. It does not download or execute code from an HTTP addon. JavaScript modules use a separate system.

Overview

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.

  1. DiscoverGET /manifest.json
  2. SearchGET /search?q=…
  3. ResolveGET /stream/{id}
  4. 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.

Build

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.

JavaScript
// 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);
Example domains are placeholders.

The sample starts a server, but audio.example does not host playable audio. Supply an absolute media URL that your player can access.

Build

Manifest

Serve a JSON object at /manifest.json. Keep the addon ID stable between deployments. New or unused properties are tolerated by the parser.

json
{
  "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"
        }
      ]
    }
  ]
}
FieldDetails
idRequired: a non-empty identifier for this source.
name / versionSource name and release identifier.
resourcesDeclare search and stream. Search is necessary when resources are declared; direct streamURL rows can support a source without a stream declaration.
settingsScalar default values are sent as query parameters. The app does not create a settings form from this schema.
allowDownloadsOmitted means enabled. Set 0 to keep this addon out of download resolution.
checkValidLosslessOmitted 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.

Build

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.

HTTP
GET /search?q=first%20light&quality=lossless
json
{
  "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"
    }
  ]
}
FieldDetails
id / titleNon-empty track identifier and title.
artist / albumRecording metadata used for matching.
durationTrack length in seconds; fractional values are accepted.
artworkURLAbsolute cover URL. albumArtworkURL is also supported.
format / audioQualityFormat and quality hints, such as flac and LOSSLESS.
streamURLOptional direct stream fallback.
atmos / audioMode / audioModesIdentify separate immersive recordings; see Dolby Atmos.

Return an empty array when a search has no matches. There is no required limit parameter.

Build

Stream endpoint

Resolve the URL-encoded identifier with GET /stream/{id}. Return one rendition for the requested quality. Use 404 for an unavailable recording.

json
{
  "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
}
FieldDetails
urlAbsolute playable media URL; required for a successful stream answer.
codec / containerState the actual codec and media container.
manifestnone, hls, or dash. Use this field for extensionless manifest URLs.
encryptedDeclare false for clear media. Protected streams are rejected.
sampleRate / bitDepthSample rate in Hz and integer sample depth.
bitrateBitrate is normalized from bps or kbps.
quality / formatDescriptive labels; they do not change the actual decoded format.
errorAn explanation when there is no playable result.
Playback

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.

LOSSLESS

Return a lossless encoding such as FLAC, ALAC, or PCM audio.

HIGH

Return the best available lossy rendition.

LOW

Return a smaller rendition suitable for metered connections.

Metadata must describe the media.

A quality label cannot turn an MP3 into a lossless recording. The decoder and actual stream information determine playback quality.

Playback

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.

HTTP
GET /stream/demo-01?quality=lossless&atmos=auto
json
{
  "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.

Immersive and lossless are different properties.

E-AC-3 JOC can provide an immersive mix without being a bit-exact lossless codec. Always declare its codec and transport accurately.

Playback

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.

json
{ "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.

Playback

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.

json
{ "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.

Ship

Install and test

  1. Publish the addon service on an HTTPS URL.
  2. Open Audioly’s Settings and Sources screen.
  3. Add the server root or its manifest.json URL and check its health.
  4. Enable the source and position it in the source order.
  5. Try a known recording, each quality tier, and a missing track.
  6. Check playback, seeking, and downloads where permitted.
Shell
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.

Ship

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.
Reference

Troubleshooting

FieldDetails
Manifest rejectedCheck the JSON response, non-empty id, and declared search capability. An HTML error page is not a manifest.
Nothing foundCheck q handling, the tracks array, and non-empty ids and titles. Compare recording metadata.
Track will not playOpen the absolute media URL, check expiry and encryption, and verify codec/container/manifest declarations.
No lossless badgeInspect the real encoding and returned metadata. Renaming the quality field does not change the stream.
No immersive mixCheck the device decoder, Dolby setting, atmos request, separate track IDs, and eac3-joc metadata.
Downloads skip addonCheck allowDownloads and whether the source is enabled.
Source skipped on BluetoothReview checkValidLossless and the active output capabilities.
429 / rate limitRespect 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 ↗

On this pageIntroduction
Report an issueBack to top ↑