Skip to content

Upload files and add tracks

The local SDK 0.1.30 offers two workflows: primary video with a browser capability URL, and attached tracks with server helpers for Node.js 22 or later. Keep session IDs and the original file to recover from interruptions.

Your backend creates an upload and saves its ID:

const upload = await orilla.uploads.create({
cors_origin: "https://app.example",
new_asset_settings: {
playback_policies: ["signed"],
meta: { title: "My video" },
},
});
// Give upload.url to the browser; save upload.id in your backend.

In the browser, use only that temporary URL and the selected file:

import { uploadFile } from "@orilla-video/sdk/upload";
await uploadFile(uploadURL, file, {
signal: abortController.signal,
onProgress: (bytes, total) => console.log(bytes, total),
});

uploadURL comes from the backend, file from a file input, and abortController is an AbortController in your interface. The helper queries the offset and sends 8 MiB chunks by default. After failure, it can be called again with the same URL and original file; it does not verify whether an already uploaded prefix matches a different file chosen by mistake.

Do not give administrative credentials to the browser. Completing the transfer does not mean the asset is ready: poll uploads.retrieve(upload.id) until you receive asset_id, then check the asset status, or use webhooks.

On a ready video or audio asset, open Subtitles & chapters → Add track → File. Select a UTF-8 SRT/WebVTT file from 1 byte to 5 MiB, type, language, and name. Chapters use WebVTT; CC is supported only for subtitles. The URL option remains available.

An invalid track fails independently. Close stops local work and preserves an already accepted session; Pending upload → Resume upload requests the same file and verifies its name, size, and SHA-256. Retry confirmation recovers completion without uploading the file again. Canceling removes only the track. Readers can view tracks without managing uploads.

With orilla configured in your backend and assetId from ready content:

import { randomUUID } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
const original = await readFile("en.vtt");
const idempotencyKey = randomUUID();
await writeFile("text-request-id.txt", idempotencyKey, { mode: 0o600 });
const result = await orilla.trackUploads.uploadText(
assetId,
original,
{
filename: "en.vtt",
contentType: "text/vtt",
text_type: "subtitles",
language_code: "en",
name: "English",
},
{
idempotencyKey,
onSession: async (session) => {
await writeFile("text-upload-id.txt", session.uploadId, { mode: 0o600 });
},
},
);
console.log(result.trackId, result.status);

In production, save the UUID and session in durable storage. Reuse the UUID when recovering a reservation whose response was lost. With the persisted ID and the same original:

const uploadId = (await readFile("text-upload-id.txt", "utf8")).trim();
await orilla.trackUploads.resumeText(uploadId, original, {
filename: "en.vtt",
});

For large files, use a file-backed Blob in your backend:

import { openAsBlob } from "node:fs";
import { randomUUID } from "node:crypto";
import { writeFile } from "node:fs/promises";
const original = await openAsBlob("en.wav");
const idempotencyKey = randomUUID();
await writeFile("audio-request-id.txt", idempotencyKey, { mode: 0o600 });
const result = await orilla.trackUploads.uploadAudio(
assetId,
original,
{
filename: "en.wav",
contentType: "audio/wav",
language_code: "en",
name: "English",
},
{
idempotencyKey,
onSession: async (session) => {
await writeFile("audio-upload-id.txt", session.uploadId, { mode: 0o600 });
},
},
);
console.log(result.trackId, result.status);

resumeAudio(uploadId, original, { filename: 'en.wav' }) resumes with the persisted ID. The helper accepts Blob/Uint8Array from 1 byte to 5 GiB, calculates the full SHA-256, and transfers parts sequentially. The dashboard uses a different audio fingerprint: resuming across dashboard and SDK is not promised. Each asset allows up to ten additional audio tracks; primary audio is required and group names must be unique.

Track helpers return status: 'completed' when the upload is accepted. Query assets.retrieve(assetId).tracks to follow ready or errored. The primary content’s status is not reset.

onSession and onProgress can be asynchronous; the SDK waits for them. Progress counts accepted bytes, not worker progress, and can reach the total before confirmation fails. signal aborts local work while preserving the session. Canceling requires trackUploads.cancel(uploadId).

There are no automatic background retries. timeoutMs limits each request; fetch in helper options replaces only storage transport. Do not add Basic Authorization globally to a wrapper: S3 receives only the signed capability, without cookies or administrative credentials. Redirects are rejected. The low-level APIs createText, createAudio, retrieve, prepare, sign, complete, and cancel remain available.