Output Assets
HLS streams, MP4 downloads, preview clips, auto-thumbnails, and subtitles from every upload.
Every video you upload to Rad TV is automatically processed into multiple formats optimized for different use cases — streaming, downloading, sharing, and embedding. After transcoding completes, you get all of these without any extra work.
Where Output Assets Live
outputAssets is a field on the Content type. It is not available on the catalog Feature type returned by channel { features }, so it cannot be read from a catalog query.
Content is returned by the content mutations, so in practice you read outputAssets from the response to the call that publishes or processes your video. Where you can, keep the value returned by your original publishVideo or submitContentForProcessing call.
There is currently no read-only query that returns outputAssets. The example below uses publishContent, which makes content publicly visible, so only call it against content that is already published. To inspect processing state without mutating anything, use channelDashboard instead (see Polling Job Status).
curl -X POST https://api.rad.live/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"query": "mutation { publishContent(id: \"did:rad.live:content/feature/123\") { id metadata { title } outputAssets { encoderType video { hls download preview } audio { download } images { thumbnails { urlFormat count } videoThumbnails { urlFormat count } } subtitles { srt vtt } } } }"
}'const response = await fetch('https://api.rad.live/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
query: `mutation {
publishContent(id: "did:rad.live:content/feature/123") {
id
metadata { title }
outputAssets {
encoderType
video { hls download preview }
audio { download }
images {
thumbnails { urlFormat count }
videoThumbnails { urlFormat count }
}
subtitles { srt vtt }
}
}
}`,
}),
});
const { data } = await response.json();
const content = data.publishContent;
console.log(content.metadata.title, content.outputAssets?.encoderType);import requests
response = requests.post(
"https://api.rad.live/graphql",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
json={
"query": """
mutation {
publishContent(id: "did:rad.live:content/feature/123") {
id
metadata { title }
outputAssets {
encoderType
video { hls download preview }
audio { download }
images {
thumbnails { urlFormat count }
videoThumbnails { urlFormat count }
}
subtitles { srt vtt }
}
}
}
"""
},
)
content = response.json()["data"]["publishContent"]
oa = content.get("outputAssets")
if oa:
print(content["metadata"]["title"], oa["encoderType"])What You Get
Video
| Field | Description | Use For |
|---|---|---|
hls | Adaptive streaming manifest (.m3u8) | In-app video players, web embeds |
download | Full MP4 file | Offline viewing, archival, redistribution |
preview | Short 10-30 second clip | Social media teasers, content previews |
Audio
| Field | Description | Use For |
|---|---|---|
download | Separate audio track | Podcast feeds, audio-only playback |
Images
| Field | Description | Use For |
|---|---|---|
thumbnails | Auto-generated static frame captures | Video players, content grids, social sharing |
videoThumbnails | Animated preview clips at key moments | Hover previews, rich thumbnails |
Each thumbnail set gives you a urlFormat (URL template) and count (how many). See Thumbnail Expansion below.
Subtitles
| Field | Description | Use For |
|---|---|---|
srt | SubRip subtitle file | Desktop players, editing tools |
vtt | WebVTT subtitle file | Web video players, accessibility |
Thumbnail URL Expansion
Thumbnails use a compact urlFormat with placeholder digits that you expand client-side. The format depends on the encoder type.
Standard Encoder (7-digit, 0-based)
urlFormat: "https://cdn.rad.live/.../thumbnail_XXXXXXX.jpg"
count: 50Replace XXXXXXX with zero-padded indices from 0000000 to 0000049:
https://cdn.rad.live/.../thumbnail_0000000.jpg
https://cdn.rad.live/.../thumbnail_0000001.jpg
...
https://cdn.rad.live/.../thumbnail_0000049.jpgRad Encoder (2-digit, 1-based)
urlFormat: "https://cdn.rad.live/.../thumbnail_XX.jpg"
count: 20Replace XX with indices from 01 to 20:
https://cdn.rad.live/.../thumbnail_01.jpg
https://cdn.rad.live/.../thumbnail_02.jpg
...
https://cdn.rad.live/.../thumbnail_20.jpgExpansion Example
function expandThumbnails(urlFormat, count, encoderType) {
const thumbnails = [];
const isRadEncoder = encoderType === 'RAD_ENCODER';
const startIndex = isRadEncoder ? 1 : 0;
const placeholder = isRadEncoder ? 'XX' : 'XXXXXXX';
const padLength = isRadEncoder ? 2 : 7;
for (let i = startIndex; i < startIndex + count; i++) {
const index = String(i).padStart(padLength, '0');
thumbnails.push(urlFormat.replace(placeholder, index));
}
return thumbnails;
}Encoder Types
The encoderType field tells you which encoder processed the content:
| Value | Description |
|---|---|
MEDIA_CONVERT | Standard transcoding |
RAD_ENCODER | AI-enhanced transcoding (Creator+ and enhance: true) |
Nullability
outputAssets is null on Content when transcoding hasn't started or is still in progress. Always check for null before accessing nested fields. Once transcoding completes, all sub-fields (video, audio, images) are non-null.
Next steps
- Publish to Rad — The full publishing pipeline
- AI Upscaling — Get enhanced output with Real-ESRGAN
- Transcoding — GraphQL reference for transcode job management