upload(segments, manifest, options)
Encrypts HLS segments in the browser and uploads them to S3 via presigned URLs.
Parameters:
| Parameter | Type | Description |
|---|
segments | SegmentInput[] | Array of HLS segments to encrypt and upload |
manifest | string | The .m3u8 manifest text |
options | UploadOptions | Configuration for the upload |
Returns: Promise<Result<UploadResult, UploaderError>>
UploadOptions
interface UploadOptions {
/** Unique identifier for this content (used for key derivation) */
contentId: string
/** Key server URL including the /keys path (e.g., "https://your-worker.workers.dev/keys") */
keyServerUrl: string
/** URL of the presign endpoint */
presignUrl: string
/** Returns a Bearer token for auth (called for key fetch and presign requests) */
auth?: () => Promise<string>
/** Called after each segment is uploaded */
onProgress?: (progress: { completed: number; total: number }) => void
/** AbortSignal to cancel the upload */
signal?: AbortSignal
/** Number of parallel uploads (default: 4) */
concurrency?: number
/** S3 key prefix (default: "content/<contentId>/") */
prefix?: string
}
interface SegmentInput {
/** Segment number (used for IV derivation) */
index: number
/** Raw .ts segment bytes (plaintext — will be encrypted) */
data: Uint8Array
/** Object key/path in storage (e.g., "seg-0.ts") */
key: string
}
UploadResult
interface UploadResult {
/** URL of the uploaded (rewritten) manifest */
manifestUrl: string
/** URLs of uploaded encrypted segments, in order */
segmentUrls: string[]
/** Total number of segments uploaded */
segmentCount: number
}
UploadProgress
The onProgress callback receives:
{ completed: number; total: number }
| Field | Type | Description |
|---|
completed | number | Number of segments uploaded so far |
total | number | Total number of segments |
rewriteManifest(manifest, opts)
Rewrites an HLS manifest to include EXT-X-KEY tags pointing to the key server and replaces segment URIs with uploaded URLs. This is called internally by upload() — you only need it directly for custom upload pipelines.
Exported from @blindcast/storage:
import { rewriteManifest } from "@blindcast/storage"
Parameters:
| Parameter | Type | Description |
|---|
manifest | string | Original .m3u8 manifest text |
opts | RewriteOpts | Rewrite configuration |
Returns: Result<string, StorageError> — the rewritten manifest text
interface RewriteOpts {
/** Key server base URL — /keys/:contentId is appended automatically */
keyServerUrl: string
/** Content identifier (used in the EXT-X-KEY URI) */
contentId: string
/** Map original segment filenames → uploaded URLs */
segmentUrlMap: Record<string, string>
/** If set, insert new EXT-X-KEY every N segments (key rotation) */
segmentsPerEpoch?: number
}
keyServerUrl here is the base server URL (e.g., https://your-worker.workers.dev) — NOT the /keys path. The function appends /keys/:contentId automatically. This is different from the Player and Uploader SDKs, which expect the URL to include /keys.
Error codes
| Code | Cause |
|---|
KEY_FETCH_FAILED | Could not fetch content key from key server |
ENCRYPT_FAILED | Segment encryption failed |
PRESIGN_FAILED | Presign endpoint returned an error or non-200 status |
UPLOAD_FAILED | S3 upload failed (network error or rejected) |
MANIFEST_PARSE_ERROR | The manifest text is not valid HLS |
INVALID_INPUT | Segments array is empty, contentId is invalid, or segment data is missing |
ABORTED | Upload was cancelled via AbortSignal |
See Error Codes for all error codes across BlindCast.