When a recording finishes uploading, the SDK gives you a DadanVideo object. This guide shows what to store, how to play the video in your app with @dadan-io/embed, and how to get download links.
What to store
Link to section: What to storeinterface DadanVideo {
id: string;
sharedUrl: string;
embedUrl: string;
title?: string;
thumbnailUrl?: string;
duration?: number;
}Save id and sharedUrl in your database next to the record they belong to, such as the support ticket or the job application. embedUrl can always be rebuilt from sharedUrl, and download links must be requested fresh each time (see below), so there is no need to store them.
Link to the video
Link to section: Link to the videoThe simplest option is a link: use sharedUrl as the link target. It opens the video’s share page on Dadan.
Embed the player
Link to section: Embed the playerInstall the embed package:
npm install @dadan-io/embedBuild the player yourself
Link to section: Build the player yourselftoEmbedUrl turns a share URL into the player URL. Use it in an iframe that you style with your own CSS:
import { toEmbedUrl } from "@dadan-io/embed";
export function createPlayer(
sharedUrl: string,
title: string,
): HTMLIFrameElement {
const frame = document.createElement("iframe");
frame.src = toEmbedUrl(sharedUrl);
frame.title = title;
frame.loading = "lazy";
frame.allow = "autoplay; fullscreen; picture-in-picture";
frame.allowFullscreen = true;
frame.className = "video-player";
return frame;
}.video-player {
width: 100%;
aspect-ratio: 16 / 9;
border: 0;
border-radius: 12px;
}This is the best choice for pages with a strict Content Security Policy, because it needs no inline styles. Give each iframe a meaningful title, such as the video title, so screen reader users know what it contains.
Get ready-made player HTML
Link to section: Get ready-made player HTMLbuildPlayerHtml returns a responsive iframe wrapped in a container that keeps a 16:9 ratio:
import { buildPlayerHtml, toEmbedUrl } from "@dadan-io/embed";
const container = document.querySelector<HTMLDivElement>("#player")!;
container.innerHTML = buildPlayerHtml(toEmbedUrl(video.sharedUrl), 640);The HTML is built in the browser from the embed URL, and the function refuses anything that is not an http or https URL. It uses inline style attributes for the ratio, so it needs 'unsafe-inline' in your CSP style-src. If your policy does not allow that, use the iframe approach above.
Use oEmbed for the title and thumbnail
Link to section: Use oEmbed for the title and thumbnailoembed returns the player HTML together with metadata, which is handy for previews and cards:
import { oembed } from "@dadan-io/embed";
const result = await oembed(video.sharedUrl, { width: 640 });
console.log(
result.title,
result.thumbnail_url,
result.width,
result.height,
);
document.querySelector("#player")!.innerHTML = result.html;oembed asks Dadan’s oEmbed endpoint for the title, size and thumbnail, then builds the player HTML locally. HTML returned by the server is never inserted. Pass { fallback: true } to skip the request and only build the player.
Turn Dadan links into players
Link to section: Turn Dadan links into playersIf your app shows user content that may contain Dadan links, these helpers replace the links with players:
linkReplace(selector)replaces matching elements on the page whose link (or text) is a Dadan share URL, and returns how many were replaced.textReplace(text)returns HTML in which every Dadan share URL in a plain text string is replaced by a player. The rest of the text is escaped.
import { linkReplace } from "@dadan-io/embed";
const replaced = await linkReplace(".comment-body a");
console.log(`${replaced} Dadan videos embedded`);In React
Link to section: In React@dadan-io/record-sdk-react includes a component for this:
import { DadanEmbed } from "@dadan-io/record-sdk-react";
export function TicketVideo({ sharedUrl }: { sharedUrl: string }) {
return <DadanEmbed sharedUrl={sharedUrl} width={720} />;
}Get download links
Link to section: Get download linksDownload links let your users save the original file, or let your app pass it to another system. They are requested with your public app ID from an allowlisted origin, with no upload token or API key:
import {
fetchVideoDownloadWithRetry,
VideoDownloadError,
} from "@dadan-io/record-sdk";
async function downloadLinks(videoId: string) {
try {
const links = await fetchVideoDownloadWithRetry({
videoId,
publicAppId: import.meta.env.VITE_DADAN_PUBLIC_APP_ID,
});
return links.videoRenditions ?? [];
} catch (error) {
if (
error instanceof VideoDownloadError &&
error.code === "VIDEO_NOT_FOUND"
)
return [];
throw error;
}
}The response contains:
| Field | Content |
|---|---|
videoRenditions | A list of { height, label, url }, one per available quality. The first entry is the original |
audioLink | An audio-only file, when available |
captionUrl | A captions file, when available |
Important points:
- Links expire quickly. The file URLs are signed and short-lived. Never store or cache them; request new ones when the user clicks Download.
- New videos need processing time. Right after an upload, the API can answer
VIDEO_STILL_PROCESSING.fetchVideoDownloadWithRetryretries with a growing delay (up to 6 attempts by default). UsefetchVideoDownloadfor a single attempt. - Only your app’s videos. Downloads work for videos recorded through the same public app ID.
- Treat video IDs with care. Any page on your allowlisted origins that knows a video ID can request its download links. Do not expose the IDs of private recordings to people who should not download them.
For the original file only, getOriginalDownloadUrl({ videoId, publicAppId }) returns its URL or null.
If you use DadanRecorder, the same calls are methods on the recorder: await recorder.fetchDownload() returns the links for the last recording and await recorder.openDownload() opens the original file in a new tab.
Download errors
Link to section: Download errors| Code | Meaning |
|---|---|
INVALID_APP | The public app ID is not valid |
ORIGIN_DENIED | The page’s origin is not on the app’s allowlist |
PLAN_DENIED | The Dadan plan behind the app does not include this download |
VIDEO_NOT_FOUND | No video with this ID for this app |
VIDEO_STILL_PROCESSING | The video is still being processed. Retry later |
VIDEO_TRANSCODE_FAILED | Processing failed for this video |
UNKNOWN | Any other failure |
Every VideoDownloadError also carries httpStatus. Its detail field holds the raw server text for your logs; do not show it to users.
Content Security Policy
Link to section: Content Security PolicyIf your site sends a CSP, the player and the API need these sources:
frame-src https://app.dadan.io
connect-src https://app.dadan.ioframe-src allows the player iframe. connect-src covers oEmbed metadata and download links. The full policy for recording is in the Record SDK production checklist.