@molecule/api-media-streaming-hls
v1.0.1
Published
HLS media streaming provider for molecule.dev — ffmpeg-based segmentation, transcoding, and M3U8 playlist generation
Downloads
468
Maintainers
Readme
@molecule/api-media-streaming-hls
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
HLS media streaming provider for molecule.dev.
Provides HLS (HTTP Live Streaming) support via ffmpeg for media segmentation and transcoding, with pure-TypeScript M3U8 playlist generation. Requires ffmpeg to be installed on the host system.
Quick Start
import { setProvider, createStream } from '@molecule/api-media-streaming'
import { provider } from '@molecule/api-media-streaming-hls'
setProvider(provider)
const manifest = await createStream('/path/to/video.mp4', {
segmentDuration: 6,
protocol: 'hls',
})
console.log(manifest.manifestUri) // '/hls-…/index.m3u8'Type
provider
Installation
npm install @molecule/api-media-streaming-hls @molecule/api-media-streamingAPI
Interfaces
HlsConfig
Configuration options for the HLS streaming provider.
interface HlsConfig {
/** Path to the ffmpeg binary. Defaults to `'ffmpeg'` (resolved via PATH). */
ffmpegPath?: string
/**
* Path to the ffprobe binary. RESERVED for future use — the current
* provider never invokes ffprobe (segment durations are taken from
* `segmentDuration`, not probed). Setting this has no effect today.
*/
ffprobePath?: string
/** Base directory where stream output files are written. Defaults to `os.tmpdir()`. */
outputBasePath?: string
/** Default segment duration in seconds. Defaults to `6`. */
segmentDuration?: number
/** HLS playlist version. Defaults to `3`. */
hlsVersion?: number
}M3u8PlaylistOptions
Options for generating an M3U8 media playlist.
interface M3u8PlaylistOptions {
/** HLS playlist version. Defaults to `3`. */
version?: number
/** Target segment duration in seconds. Defaults to the maximum segment duration. */
targetDuration?: number
/** Whether this is a VOD (complete) or live (in-progress) playlist. Defaults to `'vod'`. */
playlistType?: 'vod' | 'event'
/** Media sequence number for the first segment. Defaults to `0`. */
mediaSequence?: number
}Functions
assertSafePathComponent(value, label)
Asserts that a caller-supplied value is a safe single path component.
Rejects empty strings, the relative segments . and .., anything
containing a path separator or NUL byte, and anything outside the
[A-Za-z0-9._-] allow-list (which also rejects shell metacharacters).
function assertSafePathComponent(value: string, label: string): stringvalue— The caller-supplied component (e.g. a stream id or profile name).label— Human-readable name of the field, used in the error message.
Returns: The validated component, unchanged.
assertSegmentIndex(index)
Asserts that a caller-supplied segment index is a non-negative integer.
function assertSegmentIndex(index: number): numberindex— The caller-supplied segment index.
Returns: The validated index, unchanged.
createProvider(config)
Creates an HLS streaming provider.
function createProvider(config?: HlsConfig): StreamingProviderconfig— Optional provider configuration.
Returns: A StreamingProvider backed by HLS / ffmpeg.
generateMasterPlaylist(variants)
Generates an M3U8 master playlist for adaptive bitrate streaming.
function generateMasterPlaylist(variants: TranscodeVariant[]): stringvariants— The transcoded variant streams.
Returns: The master M3U8 playlist content as a string.
generateMediaPlaylist(segments, options)
Generates an M3U8 media playlist from a list of stream segments.
function generateMediaPlaylist(segments: StreamSegment[], options?: M3u8PlaylistOptions): stringsegments— Ordered list of stream segments.options— Playlist generation options.
Returns: The M3U8 playlist content as a string.
resolveWithinBase(base, parts)
Resolves parts against base and asserts the result stays within base.
Defense-in-depth on top of {@link assertSafePathComponent}: even if a
component slipped through, the resolved absolute path is rejected unless it
is base itself or a descendant of it.
function resolveWithinBase(base: string, parts?: string[]): stringbase— The intended base directory.parts— Path segments to append.
Returns: The resolved absolute path, guaranteed to be inside base.
Constants
provider
The provider implementation with default configuration.
const provider: StreamingProviderCore Interface
Implements @molecule/api-media-streaming interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-media-streaming'
import { provider } from '@molecule/api-media-streaming-hls'
export function setupMediaStreamingHls(): void {
setProvider(provider)
}Injection Notes
Requirements
Peer dependencies:
@molecule/api-media-streaming^1.0.1
Runtime Dependencies
@molecule/api-media-streamingRequires the
ffmpegbinary on the host (resolved via PATH, or setcreateProvider({ ffmpegPath })). A missing binary fails at firstcreateStream()/transcode()call withspawn ffmpeg ENOENT— verify withffmpeg -versionbefore shipping.The default output directory is
os.tmpdir()— volatile and served by nothing. PasscreateProvider({ outputBasePath })pointing at a directory your server exposes (see the core remarks), or serve bytes throughgetSegment()/generateManifest()endpoints.createStream()also caches every segmentBufferin an in-process map (never evicted) sogetSegment()is fast; memory grows by the full video size per stream.getSegment()disk fallback looks ONLY underoutputBasePath/<streamId>/— a per-callcreateStream(..., { outputPath })override writes segments where the fallback cannot find them after a restart, andtranscode()ignoresoutputPathentirely (always writes underoutputBasePath).ffprobePathinHlsConfigis currently RESERVED — no ffprobe call exists yet; segment durations come from the requestedsegmentDuration.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual upload/player screens, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. You can't judge real transcode QUALITY or a live A/V feed in the sandbox; verify the pipeline + playback WIRING you own:
- [ ] Uploading/ingesting a media asset produces a real PLAYABLE stream: the
returned
manifestUri(.m3u8for HLS /.mpdfor DASH) loads in the app's video player and actually plays — frames advance and the player fetches segments (watch the network panel), never a broken/blank player. - [ ] The stream is served from/through the APP'S OWN origin — an
outputPathunder a directory the server exposes, or endpoints that returngenerateManifest(segments)and streamgetSegment(streamId, index)bytes. The player must NOT hotlink a raw expiring provider URL, and no manifest or segment request may 404. - [ ] Processing STATE is observable and playback is gated on it: an asset moves pending → processing → ready (StreamStatus), the UI reflects that, and the player mounts only once status is 'ready' — never a dead player on a still-transcoding asset.
- [ ] If adaptive bitrate is exposed,
transcode()produced multiple renditions: the master manifest (masterManifestUri) lists more than onevariantand the player can switch quality across them. - [ ] If the app exposes a poster/thumbnail for an asset, it generates and renders before playback (no blank tile).
- [ ] SECURITY — private media is AUTHORIZED on playback: the manifest and
segment endpoints check the requester (or hand out a signed/expiring URL),
so a user CANNOT fetch another user's stream by guessing its
id/URL; and provider keys stay server-side (never shipped to the client bundle).
