Launch Sale: Creator and Business plans are discounted for a limited time. View pricing

Preview & Waveform Workflow

Show a waveform and a short, public preview of every track, while keeping the full-quality audio gated. AudioDN generates the waveform automatically during processing and can build a preview clip as a variant.

When AudioDN fits this job

Use AudioDN when you want preview clips and waveform data produced for you at upload time rather than running FFmpeg and waveform extraction yourself. The waveform component renders directly from the amplitude data the API returns.

Architecture

  • Upload (server): ingest the track; a default 320-sample waveform is generated during processing.
  • Preview (server): add a preview variant so there is a short, shareable clip.
  • Deliver (server): mint a play session that returns waveform levels and signed variant URLs; request preview for everyone, hq only for entitled users.
  • Render (client): draw the waveform with <audiodn-waveform> and play the preview.

Environment variables

# Server-only
ADN_API_KEY=adn_...        # API Access key (server-side)
ADN_COLLECTION_ID=...      # collection to upload into

# Safe to expose to the browser
PUBLIC_ADN_PLAYER_KEY=adn_player_...   # Client-Side Player key, scoped to the collection

1. Upload the track

// upload.mjs — standard upload lifecycle (server-side).
const API = 'https://api.audiodelivery.net/v1';

export async function uploadTrack(bytes, fileName) {
  const session = await fetch(`${API}/upload_session`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.ADN_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ collection_id: process.env.ADN_COLLECTION_ID }),
  }).then((r) => r.json());

  // Per-track create — no Bearer, the session ID authorizes it.
  const { track_id, track_upload } = await fetch(
    `${API}/upload/${session.upload_session_id}/track`,
    { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ file_name: fileName }) },
  ).then((r) => r.json());

  await fetch(track_upload.upload_url, { method: track_upload.method, body: bytes });

  // Wait until ready (a default waveform is generated during processing).
  let status;
  do {
    await new Promise((r) => setTimeout(r, 5000));
    const t = await fetch(`${API}/track/${track_id}`, {
      headers: { Authorization: `Bearer ${process.env.ADN_API_KEY}` },
    }).then((r) => r.json());
    status = t.track?.track_status_id;
  } while (!['ready', 'incomplete', 'error'].includes(status));

  return { track_id, status };
}

2. Add a preview variant

Configure a preview variant once in the dashboard so it applies to every upload, or add one per track with the Variants API:

// Add a short public preview clip to an already-processed track.
// Types: "transcode" (full-length) or "preview" (clip). See the Variants API.
await fetch(`https://api.audiodelivery.net/v1/track/${track_id}/variant`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.ADN_API_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    index: 'preview',
    variant_type_id: 'preview',
    // codec settings optional; defaults to aac / 128kbps
  }),
});
// The track re-enters "processing" and returns to "ready" when the clip is built.

3. Mint a play session (levels + signed URLs)

// server.mjs — mint a play session for the waveform + preview.
// Request only the "preview" variant for the public teaser; add "hq" for entitled users.
app.get('/api/preview/:trackId', async (req, res) => {
  const variants = (await isEntitled(req)) ? ['hq', 'preview'] : ['preview'];
  const session = await fetch('https://api.audiodelivery.net/v1/play_session/track', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.ADN_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ track_id: req.params.trackId, variants, expires_in: 600 }),
  }).then((r) => r.json());
  // first_track.levels feeds the waveform; first_track.variants[].url are signed audio URLs.
  res.json({
    levels: session.first_track.levels.levels,
    variants: session.first_track.variants,
  });
});

4. Render the waveform and preview

<!-- Render the waveform from the levels array and play the preview. -->
<audiodn-waveform id="wf" variant="vertical" height="100" line-width="2" gap="3"></audiodn-waveform>
<audio id="audio" controls></audio>

<script type="module">
  import '@audiodn/components/waveform';

  const data = await fetch('/api/preview/TRACK_ID').then((r) => r.json());

  // Feed amplitude data (0–1 values) to the waveform component.
  document.getElementById('wf').levels = data.levels;

  // Play the preview variant's signed URL.
  const preview = data.variants.find((v) => v.variant.index === 'preview');
  document.getElementById('audio').src = preview.url;
</script>

Preview vs. full

The public teaser only ever receives the preview variant URL. The full hq URL is added to the play session only after your server confirms entitlement, so the full track is never exposed to anonymous visitors.

Expected errors

  • 401 — missing/invalid Bearer key on the server-side variant or play-session calls.
  • 400 — adding a variant with a duplicate index, or an unsupported codec.
  • 404 — the track is not ready yet or the ID is wrong.
  • Empty variants in the play session response — the requested variant name does not exist on the track.

Testing

  • Upload a track and confirm track_status_id reaches ready, then that a preview variant appears.
  • Call /api/preview/<trackId> as anonymous and as entitled; assert the anonymous response omits hq.
  • Confirm the waveform renders (non-empty levels) and the preview audio plays.

Deployment

The server route needs the API Access key; deploy it on any server or serverless function. The client only uses the signed URLs and the public player key, so it can be a fully static frontend.

OpenAPI operations used

  • createUploadSessionPOST /v1/upload_session (reference, openapi.json)
  • createUploadSessionTrackPOST /v1/upload/:upload_session_id/track (reference)
  • getTrackGET /v1/track/:track_id (reference)
  • addTrackVariantPOST /v1/track/:track_id/variant (reference)
  • createPlaySessionPOST /v1/play_session/:scope (reference)