Skip to content

getHlsStreamingUrl

function getHlsStreamingUrl(asset, opts?): string;

Defined in: packages/asset-client/src/transform.ts:284

Build an HLS streaming URL for a VIDEO asset (Phase 5). Returns the master.m3u8 entry point — HLS-aware players (Video.js’s @videojs/http-streaming, hls.js, native iOS Safari) follow it to fetch the variant playlist + segments at the appropriate bitrate for the connection.

⚠️ A master.m3u8 is NOT a video file. Assigning it to <video src> works only where the engine has native HLS; everywhere else it needs an MSE player. And the classic feature test is now WRONG: Chrome 147 (April 2026) added native HLS, so canPlayType("application/vnd.apple.mpegurl") answers “maybe” there and routes Chrome to the native branch, where it opened a measured 17 s hero at 426x240 for ~8 s. Branch on the ENGINE:

Parameter Type
asset Pick<AssetDTO, "sha">
opts Omit<TransformOptions, "format">

string

function prefersNativeHls(video: HTMLVideoElement): boolean {
if (video.canPlayType("application/vnd.apple.mpegurl") === "") return false;
// Apple's engine, or an engine with no MSE to fall back on (iOS < 17.1).
return "ManagedMediaSource" in globalThis || !("MediaSource" in globalThis);
}
const src = getHlsStreamingUrl(asset);
if (prefersNativeHls(video)) {
video.src = src;
} else {
// Defaults open at a fixed low rung — measure instead of guessing.
const hls = new Hls({ startLevel: -1, testBandwidth: true, abrEwmaDefaultEstimate: 1_000_000 });
hls.loadSource(src);
hls.attachMedia(video);
}

On first request the server returns 202 Accepted while a Cloud Run Job transcodes the ladder (typically 1-3 min for a 90 s source); subsequent requests get 302 to the cached master.m3u8. Keep the progressive MP4 as a fallback source for that window.

The ladder’s ceiling is the source the job probes: built at ingest it reads the RAW upload and a 4K master yields 1440p/2160p rungs; rebuilt on demand after the raw is unavailable it reads the -v.mp4, which is capped at 1920 wide. getAssetUrl(sha, "video") is always <= 1080p.