@better-media/plugin-video-streaming 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.mts +138 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +619 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +606 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants/presets.ts","../src/interfaces/options.interface.ts","../src/runtime/buffer.ts","../src/runtime/ffmpeg.ts","../src/runtime/upload.ts","../src/runtime/next-version.ts","../src/runtime/runner.ts","../src/playback/resolve-streaming-urls.ts","../src/playback/resolve-streaming-status.ts","../src/playback/create-streaming-proxy.ts","../src/index.ts"],"names":["fs","path"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { StreamingPreset } from \"../interfaces/options.interface\";\n\nexport const DEFAULT_STREAMING_PRESETS: StreamingPreset[] = [\n { name: \"360p\", height: 360, videoBitrate: \"800k\", audioBitrate: \"96k\" },\n { name: \"480p\", height: 480, videoBitrate: \"1400k\", audioBitrate: \"128k\" },\n { name: \"720p\", height: 720, videoBitrate: \"2500k\", audioBitrate: \"128k\" },\n { name: \"1080p\", height: 1080, videoBitrate: \"5000k\", audioBitrate: \"192k\" },\n];\n\nexport const DEFAULT_VIDEO_MIME_TYPES: string[] = [\n \"video/mp4\",\n \"video/quicktime\",\n \"video/x-msvideo\",\n \"video/x-matroska\",\n \"video/webm\",\n \"video/mpeg\",\n \"video/ogg\",\n \"video/3gpp\",\n \"video/x-flv\",\n \"video/x-ms-wmv\",\n];\n","import type { PipelineContext } from \"@better-media/core\";\nimport { DEFAULT_STREAMING_PRESETS, DEFAULT_VIDEO_MIME_TYPES } from \"../constants/presets\";\n\nexport type StreamingFormat = \"hls\" | \"dash\";\n\nexport interface StreamingProgressEvent {\n /** Which streaming format is being transcoded. */\n format: StreamingFormat;\n /** For HLS: the preset currently being transcoded (one pass per preset). Absent for DASH. */\n preset?: string;\n /** Estimated completion percentage (0–100). */\n percent?: number;\n /** How far into the source video ffmpeg has processed, in seconds. */\n currentTimeSecs?: number;\n}\n\nexport interface StreamingPreset {\n /** Used in storage path, e.g. \"360p\", \"720p\". Must be stable for idempotent keys. */\n name: string;\n /** Output width (px). Aspect ratio preserved if only height is set. */\n width?: number;\n /** Output height (px). Aspect ratio preserved if only width is set. */\n height?: number;\n /** Video bitrate, e.g. \"1000k\". */\n videoBitrate?: string;\n /** Audio bitrate, e.g. \"128k\". */\n audioBitrate?: string;\n /** Video codec. Default: \"libx264\". */\n videoCodec?: string;\n /** Audio codec. Default: \"aac\". */\n audioCodec?: string;\n}\n\nexport interface VideoStreamingPluginOptions {\n /**\n * Streaming formats to generate. Default: [\"hls\"].\n * Add \"dash\" to also produce a DASH manifest.\n */\n formats?: StreamingFormat[];\n\n /** Quality presets to transcode. Default: 360p / 720p / 1080p. */\n presets?: StreamingPreset[];\n\n /**\n * Adjust each preset at runtime from context.metadata (e.g. restrict max quality\n * based on subscription tier).\n */\n resolvePreset?: (\n context: PipelineContext,\n preset: StreamingPreset,\n index: number\n ) => StreamingPreset | Promise<StreamingPreset>;\n\n /** Only process files whose mimeType is in this list. Default: common video types. */\n allowedMimeTypes?: string[];\n\n /** Prefix for streaming storage keys. Default: \"streaming\". */\n derivativePrefix?: string;\n\n /** HLS/DASH segment duration in seconds. Default: 6. */\n segmentDuration?: number;\n\n /**\n * Insert rows into media_versions for master playlists and variant playlists.\n * Individual segments are never persisted. Default: true.\n */\n persistMediaVersions?: boolean;\n\n /**\n * Skip transcoding if the master playlist already exists in storage. Default: true.\n */\n skipExistingDerivatives?: boolean;\n\n /** Timeout for the full transcoding pass (ms). Default: 600_000 (10 min). */\n timeoutMs?: number;\n\n /**\n * Called periodically during transcoding with ffmpeg progress information.\n * For HLS each preset runs as a separate pass, so events include the preset name.\n * For DASH all presets run in a single pass, so `preset` is absent.\n */\n onProgress?: (event: StreamingProgressEvent) => void;\n}\n\nexport type ResolvedVideoStreamingOptions = Required<\n Pick<\n VideoStreamingPluginOptions,\n | \"formats\"\n | \"presets\"\n | \"allowedMimeTypes\"\n | \"derivativePrefix\"\n | \"segmentDuration\"\n | \"persistMediaVersions\"\n | \"skipExistingDerivatives\"\n | \"timeoutMs\"\n >\n>;\n\nexport function resolveVideoStreamingOptions(\n opts: VideoStreamingPluginOptions\n): ResolvedVideoStreamingOptions {\n return {\n formats: opts.formats && opts.formats.length > 0 ? opts.formats : [\"hls\"],\n presets:\n opts.presets && opts.presets.length > 0 ? opts.presets : [...DEFAULT_STREAMING_PRESETS],\n allowedMimeTypes: opts.allowedMimeTypes ?? [...DEFAULT_VIDEO_MIME_TYPES],\n derivativePrefix: opts.derivativePrefix ?? \"streaming\",\n segmentDuration: opts.segmentDuration ?? 6,\n persistMediaVersions: opts.persistMediaVersions !== false,\n skipExistingDerivatives: opts.skipExistingDerivatives !== false,\n timeoutMs: opts.timeoutMs ?? 600_000,\n };\n}\n","import fs from \"node:fs/promises\";\nimport type { PipelineContext } from \"@better-media/core\";\n\nexport async function readBufferForProcessing(context: PipelineContext): Promise<Buffer | null> {\n const fileContent = context.utilities?.fileContent;\n if (fileContent?.buffer) return fileContent.buffer;\n if (fileContent?.tempPath) return fs.readFile(fileContent.tempPath);\n return context.storage.get(context.file.key);\n}\n\nexport function isReferenceUrlMode(context: PipelineContext): boolean {\n const url = context.storageLocation?.url;\n return typeof url === \"string\" && url === context.file.key;\n}\n","import { spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport type { StreamingPreset } from \"../interfaces/options.interface\";\n\nexport interface TranscodeResult {\n /** Duration of the source video in seconds. */\n duration: number;\n /** Storage-relative paths of all files written under outputDir. */\n files: string[];\n}\n\nasync function tryImportFfmpeg(): Promise<typeof import(\"fluent-ffmpeg\") | null> {\n try {\n const mod = await import(\"fluent-ffmpeg\");\n return mod.default ?? (mod as unknown as typeof import(\"fluent-ffmpeg\"));\n } catch {\n return null;\n }\n}\n\nasync function getFfmpegBinaryPath(): Promise<string | null> {\n const mod = await tryImportFfmpeg();\n if (!mod) return null;\n const configured = (mod as unknown as { getFfmpegPath?: () => string }).getFfmpegPath?.();\n return typeof configured === \"string\" && configured.length > 0 ? configured : \"ffmpeg\";\n}\n\nfunction parseTimemarkSecs(timemark: string): number {\n const parts = timemark.split(\":\").map(Number);\n return (parts[0] ?? 0) * 3600 + (parts[1] ?? 0) * 60 + (parts[2] ?? 0);\n}\n\nfunction parseDurationSecs(stderr: string): number {\n const match = stderr.match(/Duration:\\s*(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)/);\n return match ? parseTimemarkSecs(match[1]!) : 0;\n}\n\nfunction buildScaleFilter(preset: StreamingPreset): string {\n if (preset.width && preset.height) return `scale=${preset.width}:${preset.height}`;\n if (preset.width) return `scale=${preset.width}:-2`;\n if (preset.height) return `scale=-2:${preset.height}`;\n return \"scale=-2:-2\";\n}\n\nasync function collectFiles(dir: string, base: string): Promise<string[]> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const results: string[] = [];\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n const rel = path.join(base, entry.name);\n if (entry.isDirectory()) {\n results.push(...(await collectFiles(full, rel)));\n } else {\n results.push(rel);\n }\n }\n return results;\n}\n\n/**\n * Transcode source video to multi-bitrate HLS.\n *\n * Writes to outputDir:\n * {preset.name}/index.m3u8\n * {preset.name}/seg%03d.ts\n * master.m3u8\n */\nexport async function transcodeHLS(\n inputPath: string,\n outputDir: string,\n presets: StreamingPreset[],\n segmentDuration: number,\n onProgress?: (event: { preset?: string; percent?: number; currentTimeSecs?: number }) => void\n): Promise<TranscodeResult> {\n const ffmpeg = await tryImportFfmpeg();\n if (!ffmpeg) throw new FfmpegNotFoundError();\n\n // Create per-preset subdirectories\n await Promise.all(\n presets.map((p) => fs.mkdir(path.join(outputDir, p.name), { recursive: true }))\n );\n\n let duration = 0;\n\n // Run one ffmpeg pass per preset (fluent-ffmpeg doesn't support multi-output HLS natively)\n await Promise.all(\n presets.map(\n (preset) =>\n new Promise<void>((resolve, reject) => {\n const variantDir = path.join(outputDir, preset.name);\n const playlistPath = path.join(variantDir, \"index.m3u8\");\n const segmentPattern = path.join(variantDir, \"seg%03d.ts\");\n\n const cmd = ffmpeg(inputPath)\n .videoCodec(preset.videoCodec ?? \"libx264\")\n .audioCodec(preset.audioCodec ?? \"aac\")\n .videoFilters(buildScaleFilter(preset))\n .outputOptions([\n `-b:v ${preset.videoBitrate ?? \"1000k\"}`,\n `-b:a ${preset.audioBitrate ?? \"128k\"}`,\n \"-f hls\",\n `-hls_time ${segmentDuration}`,\n \"-hls_playlist_type vod\",\n \"-hls_flags independent_segments\",\n `-hls_segment_filename ${segmentPattern}`,\n ])\n .output(playlistPath);\n\n cmd.on(\"error\", (err: Error) => reject(err));\n cmd.on(\"end\", () => resolve());\n cmd.on(\"codecData\", (data: { duration?: string }) => {\n if (data.duration) {\n duration = parseTimemarkSecs(data.duration);\n }\n });\n cmd.on(\"progress\", (progress: { percent?: number; timemark?: string }) => {\n onProgress?.({\n preset: preset.name,\n percent:\n typeof progress.percent === \"number\"\n ? Math.min(100, Math.round(progress.percent))\n : undefined,\n currentTimeSecs: progress.timemark ? parseTimemarkSecs(progress.timemark) : undefined,\n });\n });\n cmd.run();\n })\n )\n );\n\n // Write master playlist manually\n const masterLines = [\"#EXTM3U\", \"#EXT-X-VERSION:3\"];\n for (const preset of presets) {\n const bandwidth = parseInt((preset.videoBitrate ?? \"1000k\").replace(/\\D/g, \"\"), 10) * 1000;\n const resolution =\n preset.width && preset.height ? `RESOLUTION=${preset.width}x${preset.height}` : \"\";\n const attrs = [`BANDWIDTH=${bandwidth}`, resolution].filter(Boolean).join(\",\");\n masterLines.push(`#EXT-X-STREAM-INF:${attrs}`, `${preset.name}/index.m3u8`);\n }\n await fs.writeFile(path.join(outputDir, \"master.m3u8\"), masterLines.join(\"\\n\") + \"\\n\", \"utf8\");\n\n const files = await collectFiles(outputDir, \"\");\n return { duration, files };\n}\n\n/**\n * Transcode source video to multi-bitrate DASH using a single ffmpeg pass.\n *\n * Writes to outputDir:\n * master.mpd ← combined manifest for all representations\n * init-stream$RepresentationID$.mp4 ← per-representation init segment\n * chunk-stream$RepresentationID$-NNN.m4s ← media segments\n *\n * All representations are declared in a single master.mpd so ABR players can\n * switch between quality levels. This differs from the old per-preset approach\n * which wrote separate stream.mpd files with no combined manifest.\n */\nexport async function transcodeDASH(\n inputPath: string,\n outputDir: string,\n presets: StreamingPreset[],\n segmentDuration: number,\n onProgress?: (event: { preset?: string; percent?: number; currentTimeSecs?: number }) => void\n): Promise<TranscodeResult> {\n const ffmpegPath = await getFfmpegBinaryPath();\n if (!ffmpegPath) throw new FfmpegNotFoundError();\n\n const masterPath = path.join(outputDir, \"master.mpd\");\n const args: string[] = [\"-y\", \"-i\", inputPath];\n\n // One video+audio pair per representation\n for (let i = 0; i < presets.length; i++) {\n args.push(\"-map\", \"0:v:0\", \"-map\", \"0:a:0\");\n }\n\n // Per-stream codec, scale filter, and bitrate\n for (let i = 0; i < presets.length; i++) {\n const p = presets[i]!;\n args.push(\n `-c:v:${i}`,\n p.videoCodec ?? \"libx264\",\n `-c:a:${i}`,\n p.audioCodec ?? \"aac\",\n `-filter:v:${i}`,\n buildScaleFilter(p),\n `-b:v:${i}`,\n p.videoBitrate ?? \"1000k\",\n `-b:a:${i}`,\n p.audioBitrate ?? \"128k\"\n );\n }\n\n args.push(\n \"-f\",\n \"dash\",\n \"-seg_duration\",\n String(segmentDuration),\n \"-use_template\",\n \"1\",\n \"-use_timeline\",\n \"1\",\n \"-init_seg_name\",\n \"init-stream$RepresentationID$.mp4\",\n \"-media_seg_name\",\n \"chunk-stream$RepresentationID$-$Number%05d$.m4s\",\n masterPath\n );\n\n const duration = await new Promise<number>((resolve, reject) => {\n const proc = spawn(ffmpegPath, args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderrBuf = \"\";\n let totalDuration = 0;\n\n proc.stderr!.on(\"data\", (chunk: Buffer) => {\n const text = chunk.toString();\n stderrBuf += text;\n\n if (onProgress) {\n // Duration line appears early; capture it once so we can compute percent.\n if (totalDuration === 0) {\n const dm = text.match(/Duration:\\s*(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)/);\n if (dm) totalDuration = parseTimemarkSecs(dm[1]!);\n }\n // Progress lines use \\r to overwrite the terminal line; split on both \\r and \\n.\n for (const segment of text.split(/[\\r\\n]/).filter(Boolean)) {\n const pm = segment.match(/time=(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)/);\n if (pm) {\n const currentTimeSecs = parseTimemarkSecs(pm[1]!);\n const percent =\n totalDuration > 0\n ? Math.min(100, Math.round((currentTimeSecs / totalDuration) * 100))\n : undefined;\n onProgress({ currentTimeSecs, percent });\n }\n }\n }\n });\n\n proc.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"ENOENT\") {\n reject(new FfmpegNotFoundError());\n } else {\n reject(err);\n }\n });\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(`ffmpeg DASH transcode failed (exit ${code})\\n${stderrBuf.slice(-2000)}`));\n } else {\n resolve(parseDurationSecs(stderrBuf));\n }\n });\n });\n\n const files = await collectFiles(outputDir, \"\");\n return { duration, files };\n}\n\nexport class FfmpegNotFoundError extends Error {\n constructor() {\n super(\n \"fluent-ffmpeg could not be imported. Install it: pnpm add fluent-ffmpeg. \" +\n \"Also ensure the ffmpeg binary is available in PATH.\"\n );\n this.name = \"FfmpegNotFoundError\";\n }\n}\n","import path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport type { StorageAdapter } from \"@better-media/core\";\n\nexport interface UploadedFile {\n storageKey: string;\n size: number;\n}\n\nconst STREAMING_CONTENT_TYPES: Record<string, string> = {\n \".m3u8\": \"application/x-mpegURL\",\n \".ts\": \"video/MP2T\",\n \".mpd\": \"application/dash+xml\",\n \".m4s\": \"video/iso.segment\",\n \".mp4\": \"video/mp4\",\n};\n\nfunction inferContentType(filename: string): string {\n const ext = path.extname(filename).toLowerCase();\n return STREAMING_CONTENT_TYPES[ext] ?? \"application/octet-stream\";\n}\n\n/**\n * Walk tempDir recursively and upload every file to storage under storagePrefix/.\n * Mirrors the local directory structure into storage keys.\n */\nexport async function uploadDirectory(\n storage: StorageAdapter,\n tempDir: string,\n storagePrefix: string,\n skipExisting: boolean\n): Promise<UploadedFile[]> {\n const uploaded: UploadedFile[] = [];\n await walkAndUpload(storage, tempDir, tempDir, storagePrefix, skipExisting, uploaded);\n return uploaded;\n}\n\nasync function walkAndUpload(\n storage: StorageAdapter,\n rootDir: string,\n currentDir: string,\n storagePrefix: string,\n skipExisting: boolean,\n acc: UploadedFile[]\n): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (entry) => {\n const fullPath = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n await walkAndUpload(storage, rootDir, fullPath, storagePrefix, skipExisting, acc);\n return;\n }\n const relative = path.relative(rootDir, fullPath);\n const storageKey = `${storagePrefix}/${relative.split(path.sep).join(\"/\")}`;\n\n if (skipExisting && (await storage.exists(storageKey))) {\n acc.push({ storageKey, size: 0 });\n return;\n }\n\n const buffer = await fs.readFile(fullPath);\n await storage.put(storageKey, buffer, { contentType: inferContentType(entry.name) });\n acc.push({ storageKey, size: buffer.length });\n })\n );\n}\n","import type { DatabaseAdapter } from \"@better-media/core\";\n\nexport async function nextMediaVersionStart(\n database: DatabaseAdapter,\n mediaId: string\n): Promise<number> {\n const rows = await database.findMany({\n model: \"media_versions\",\n where: [{ field: \"mediaId\", value: mediaId }],\n sortBy: { field: \"versionNumber\", direction: \"desc\" },\n limit: 1,\n });\n const max = rows[0]?.versionNumber;\n return typeof max === \"number\" && Number.isFinite(max) ? max + 1 : 1;\n}\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport type { PipelineContext, PluginApi, VariantResult } from \"@better-media/core\";\nimport type {\n VideoStreamingPluginOptions,\n StreamingFormat,\n StreamingPreset,\n} from \"../interfaces/options.interface\";\nimport { resolveVideoStreamingOptions } from \"../interfaces/options.interface\";\nimport { isReferenceUrlMode, readBufferForProcessing } from \"./buffer\";\nimport { transcodeHLS, transcodeDASH, FfmpegNotFoundError } from \"./ffmpeg\";\nimport { uploadDirectory } from \"./upload\";\nimport { nextMediaVersionStart } from \"./next-version\";\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n if (!Number.isFinite(ms) || ms <= 0) return promise;\n return new Promise<T>((resolve, reject) => {\n const t = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);\n promise.then(\n (v) => {\n clearTimeout(t);\n resolve(v);\n },\n (e) => {\n clearTimeout(t);\n reject(e);\n }\n );\n });\n}\n\nfunction masterKeyForFormat(prefix: string, recordId: string, format: StreamingFormat): string {\n return format === \"hls\"\n ? `${prefix}/${recordId}/hls/master.m3u8`\n : `${prefix}/${recordId}/dash/master.mpd`;\n}\n\nfunction variantPlaylistKey(\n prefix: string,\n recordId: string,\n format: StreamingFormat,\n presetName: string\n): string {\n return format === \"hls\"\n ? `${prefix}/${recordId}/hls/${presetName}/index.m3u8`\n : `${prefix}/${recordId}/dash/${presetName}/stream.mpd`;\n}\n\nexport async function runVideoStreaming(\n context: PipelineContext,\n api: PluginApi,\n opts: VideoStreamingPluginOptions\n): Promise<void> {\n const resolved = resolveVideoStreamingOptions(opts);\n\n if (isReferenceUrlMode(context)) {\n api.emitMetadata({ skipped: \"reference-url\" });\n return;\n }\n\n const mime = context.file.mimeType;\n if (!mime || !resolved.allowedMimeTypes.includes(mime)) {\n api.emitMetadata({ skipped: \"mime-not-allowed\", mimeType: mime });\n return;\n }\n\n // Check if already processed (skipExistingDerivatives checks HLS master as proxy)\n if (resolved.skipExistingDerivatives) {\n const firstMasterKey = masterKeyForFormat(\n resolved.derivativePrefix,\n context.recordId,\n resolved.formats[0]!\n );\n if (await context.storage.exists(firstMasterKey)) {\n api.emitMetadata({ skipped: \"already-processed\", key: firstMasterKey });\n return;\n }\n }\n\n let buffer: Buffer | null;\n try {\n buffer = await withTimeout(\n readBufferForProcessing(context),\n resolved.timeoutMs,\n \"readBufferForProcessing\"\n );\n } catch (err: unknown) {\n api.emitMetadata({\n error: \"read-failed\",\n message: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n if (buffer == null) {\n api.emitMetadata({ skipped: \"no-file-content\" });\n return;\n }\n\n // Write buffer to a temp input file so ffmpeg can read it\n const tempInputDir = path.join(os.tmpdir(), randomUUID());\n await fs.mkdir(tempInputDir, { recursive: true });\n const ext = context.file.extension ?? \".mp4\";\n const inputPath = path.join(tempInputDir, `input${ext}`);\n await fs.writeFile(inputPath, buffer);\n\n // Apply resolvePreset if provided\n const presets: StreamingPreset[] = opts.resolvePreset\n ? await Promise.all(\n resolved.presets.map((preset, index) =>\n Promise.resolve(opts.resolvePreset!(context, preset, index))\n )\n )\n : resolved.presets;\n\n const streamingVariants: VariantResult[] = [];\n let videoDuration = 0;\n const tempDirs: string[] = [tempInputDir];\n\n try {\n for (const format of resolved.formats) {\n const tempOutputDir = path.join(os.tmpdir(), randomUUID());\n await fs.mkdir(tempOutputDir, { recursive: true });\n tempDirs.push(tempOutputDir);\n\n const storagePrefix = `${resolved.derivativePrefix}/${context.recordId}/${format}`;\n\n let duration = 0;\n try {\n const transcode = format === \"hls\" ? transcodeHLS : transcodeDASH;\n const transcodeOnProgress = opts.onProgress\n ? (event: { preset?: string; percent?: number; currentTimeSecs?: number }) =>\n opts.onProgress!({ format, ...event })\n : undefined;\n const result = await withTimeout(\n transcode(\n inputPath,\n tempOutputDir,\n presets,\n resolved.segmentDuration,\n transcodeOnProgress\n ),\n resolved.timeoutMs,\n `transcode-${format}`\n );\n duration = result.duration;\n if (duration > 0) videoDuration = duration;\n } catch (err: unknown) {\n if (err instanceof FfmpegNotFoundError) {\n api.emitMetadata({ error: \"ffmpeg-not-found\", message: err.message });\n return;\n }\n api.emitMetadata({\n error: \"transcode-failed\",\n format,\n message: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n\n // Upload all files (segments + playlists) to storage\n try {\n await uploadDirectory(\n context.storage,\n tempOutputDir,\n storagePrefix,\n resolved.skipExistingDerivatives\n );\n } catch (err: unknown) {\n api.emitMetadata({\n error: \"upload-failed\",\n format,\n message: err instanceof Error ? err.message : String(err),\n });\n continue;\n }\n\n const masterKey = masterKeyForFormat(resolved.derivativePrefix, context.recordId, format);\n streamingVariants.push({ key: masterKey, format });\n\n // Persist media_versions rows: master + one per variant playlist\n if (resolved.persistMediaVersions) {\n let versionCounter = await nextMediaVersionStart(context.database, context.recordId);\n\n // HLS: master + one row per variant playlist.\n // DASH: master only — all representations live inside the single master.mpd.\n const variantRows =\n format === \"hls\"\n ? presets.map((p) => ({\n storageKey: variantPlaylistKey(\n resolved.derivativePrefix,\n context.recordId,\n format,\n p.name\n ),\n mimeType: \"application/x-mpegURL\" as const,\n }))\n : [];\n\n const rowsToInsert: Array<{ storageKey: string; mimeType: string }> = [\n {\n storageKey: masterKey,\n mimeType: format === \"hls\" ? \"application/x-mpegURL\" : \"application/dash+xml\",\n },\n ...variantRows,\n ];\n\n for (const row of rowsToInsert) {\n try {\n await context.database.create({\n model: \"media_versions\",\n data: {\n id: randomUUID(),\n mediaId: context.recordId,\n storageKey: row.storageKey,\n mimeType: row.mimeType,\n isOriginal: false,\n type: \"compressed\",\n versionNumber: versionCounter,\n createdAt: new Date(),\n },\n });\n versionCounter += 1;\n } catch (err: unknown) {\n api.emitMetadata({\n error: \"media-versions-persist-failed\",\n key: row.storageKey,\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n }\n } finally {\n await Promise.allSettled(tempDirs.map((d) => fs.rm(d, { recursive: true, force: true })));\n }\n\n if (streamingVariants.length === 0) return;\n\n api.emitProcessing({\n variants: { streaming: streamingVariants },\n \"video-streaming\": {\n formats: resolved.formats,\n presets: presets.map((p) => p.name),\n duration: videoDuration || undefined,\n segmentDuration: resolved.segmentDuration,\n },\n });\n}\n","import type { DatabaseAdapter } from \"@better-media/core\";\nimport type { StorageAdapter } from \"@better-media/core\";\n\nexport interface StreamingUrlResult {\n /** Master HLS playlist URL, if HLS was generated for this record. */\n hls?: string;\n /** Master DASH manifest URL, if DASH was generated for this record. */\n dash?: string;\n}\n\nexport interface ResolveStreamingUrlsOptions {\n database: DatabaseAdapter;\n storage: StorageAdapter;\n /** When set, generates a signed URL expiring in this many seconds. */\n expiresIn?: number;\n /** Storage key prefix used at transcode time. Default: \"streaming\". */\n derivativePrefix?: string;\n}\n\nconst HLS_MIME = \"application/x-mpegURL\";\nconst DASH_MIME = \"application/dash+xml\";\n\nexport async function resolveStreamingUrls(\n recordId: string,\n options: ResolveStreamingUrlsOptions\n): Promise<StreamingUrlResult> {\n const { database, storage, expiresIn, derivativePrefix = \"streaming\" } = options;\n\n if (!storage.getUrl) {\n throw new Error(\n \"resolveStreamingUrls requires storage.getUrl to be implemented. \" +\n \"The memory adapter does not support URL generation — use createStreamingProxy instead.\"\n );\n }\n\n const rows = await database.findMany<{ storageKey: string; mimeType: string }>({\n model: \"media_versions\",\n where: [\n { field: \"mediaId\", value: recordId },\n { field: \"mimeType\", value: [HLS_MIME, DASH_MIME], operator: \"in\" },\n ],\n select: [\"storageKey\", \"mimeType\"],\n });\n\n const keyPrefix = `${derivativePrefix}/${recordId}/`;\n const masters = rows.filter(\n (r) =>\n r.storageKey.startsWith(keyPrefix) &&\n (r.storageKey.endsWith(\"master.m3u8\") || r.storageKey.endsWith(\"master.mpd\"))\n );\n\n const result: StreamingUrlResult = {};\n\n for (const row of masters) {\n const url = await storage.getUrl!(\n row.storageKey,\n expiresIn !== undefined ? { expiresIn } : undefined\n );\n\n if (row.mimeType === HLS_MIME) {\n result.hls = url;\n } else if (row.mimeType === DASH_MIME) {\n result.dash = url;\n }\n }\n\n return result;\n}\n","import type { StorageAdapter } from \"@better-media/core\";\n\nexport interface StreamingStatus {\n /** Whether the HLS master playlist is ready in storage. */\n hls: boolean;\n /** Whether the DASH master manifest is ready in storage. */\n dash: boolean;\n /** True when at least one format is ready for playback. */\n ready: boolean;\n}\n\nexport interface ResolveStreamingStatusOptions {\n storage: StorageAdapter;\n /** Storage key prefix used at transcode time. Default: \"streaming\". */\n derivativePrefix?: string;\n}\n\nexport async function resolveStreamingStatus(\n recordId: string,\n options: ResolveStreamingStatusOptions\n): Promise<StreamingStatus> {\n const { storage, derivativePrefix = \"streaming\" } = options;\n\n const [hls, dash] = await Promise.all([\n storage.exists(`${derivativePrefix}/${recordId}/hls/master.m3u8`),\n storage.exists(`${derivativePrefix}/${recordId}/dash/master.mpd`),\n ]);\n\n return { hls, dash, ready: hls || dash };\n}\n","import path from \"node:path\";\nimport type { StorageAdapter } from \"@better-media/core\";\n\nexport interface StreamingProxyOptions {\n storage: StorageAdapter;\n authenticate?: (req: unknown, context: { recordId: string; storageKey: string }) => Promise<void>;\n cacheControl?: { playlist?: string; segment?: string };\n /** CORS origin to allow. Default: \"*\". */\n corsOrigin?: string;\n /** Storage key prefix used at transcode time. Default: \"streaming\". */\n derivativePrefix?: string;\n}\n\nexport interface ProxyHandleOptions {\n recordId: string;\n /** Relative path within the record, e.g. \"hls/master.m3u8\" or \"hls/720p/seg001.ts\". */\n filePath: string;\n /** Original request object — passed through to authenticate if provided. */\n req?: unknown;\n /** HTTP method. Default: \"GET\". HEAD returns headers only (no body). */\n method?: string;\n}\n\nexport interface StreamingProxy {\n handle(options: ProxyHandleOptions): Promise<Response>;\n}\n\nconst CONTENT_TYPES: Record<string, string> = {\n \".m3u8\": \"application/x-mpegURL\",\n \".mpd\": \"application/dash+xml\",\n \".ts\": \"video/MP2T\",\n \".mp4\": \"video/mp4\",\n \".m4s\": \"video/iso.segment\",\n};\n\nconst PLAYLIST_EXTS = new Set([\".m3u8\", \".mpd\"]);\n\nconst DEFAULT_CACHE_PLAYLIST = \"no-cache, no-store\";\nconst DEFAULT_CACHE_SEGMENT = \"public, max-age=31536000, immutable\";\n\nexport function createStreamingProxy(options: StreamingProxyOptions): StreamingProxy {\n const {\n storage,\n authenticate,\n cacheControl,\n corsOrigin = \"*\",\n derivativePrefix = \"streaming\",\n } = options;\n\n const playlistCache = cacheControl?.playlist ?? DEFAULT_CACHE_PLAYLIST;\n const segmentCache = cacheControl?.segment ?? DEFAULT_CACHE_SEGMENT;\n\n return {\n async handle({ recordId, filePath, req, method = \"GET\" }): Promise<Response> {\n // Step 1: path traversal guard\n if (filePath.includes(\"..\")) {\n return new Response(\"Bad Request\", { status: 400 });\n }\n\n const storageKey = `${derivativePrefix}/${recordId}/${filePath}`;\n\n // Step 2: authenticate\n if (authenticate) {\n try {\n await authenticate(req, { recordId, storageKey });\n } catch {\n return new Response(\"Forbidden\", { status: 403 });\n }\n }\n\n // Step 3: check existence\n const exists = await storage.exists(storageKey);\n if (!exists) {\n return new Response(\"Not Found\", { status: 404 });\n }\n\n const ext = path.extname(filePath);\n const contentType = CONTENT_TYPES[ext] ?? \"application/octet-stream\";\n const cacheHeader = PLAYLIST_EXTS.has(ext) ? playlistCache : segmentCache;\n\n const headers = new Headers({\n \"Content-Type\": contentType,\n \"Cache-Control\": cacheHeader,\n \"Access-Control-Allow-Origin\": corsOrigin,\n \"Access-Control-Allow-Methods\": \"GET, HEAD\",\n \"Access-Control-Expose-Headers\": \"Content-Length, Content-Type\",\n });\n\n // Step 4: HEAD — headers only, no body\n if (method.toUpperCase() === \"HEAD\") {\n return new Response(null, { status: 200, headers });\n }\n\n // Step 5: stream or buffer\n if (storage.getStream) {\n const stream = await storage.getStream(storageKey);\n if (stream == null) {\n return new Response(\"Not Found\", { status: 404 });\n }\n return new Response(stream, { status: 200, headers });\n }\n\n const buffer = await storage.get(storageKey);\n if (buffer == null) {\n return new Response(\"Not Found\", { status: 404 });\n }\n return new Response(new Uint8Array(buffer), { status: 200, headers });\n },\n };\n}\n","import type { PipelinePlugin, MediaRuntime, PipelineContext, PluginApi } from \"@better-media/core\";\nimport type { VideoStreamingPluginOptions } from \"./interfaces/options.interface\";\nimport { runVideoStreaming } from \"./runtime/runner\";\n\nexport type {\n VideoStreamingPluginOptions,\n StreamingProgressEvent,\n StreamingPreset,\n StreamingFormat,\n} from \"./interfaces/options.interface\";\nexport { DEFAULT_STREAMING_PRESETS, DEFAULT_VIDEO_MIME_TYPES } from \"./constants/presets\";\nexport { resolveStreamingUrls } from \"./playback/resolve-streaming-urls\";\nexport type {\n StreamingUrlResult,\n ResolveStreamingUrlsOptions,\n} from \"./playback/resolve-streaming-urls\";\nexport { resolveStreamingStatus } from \"./playback/resolve-streaming-status\";\nexport type {\n StreamingStatus,\n ResolveStreamingStatusOptions,\n} from \"./playback/resolve-streaming-status\";\nexport { createStreamingProxy } from \"./playback/create-streaming-proxy\";\nexport type {\n StreamingProxyOptions,\n ProxyHandleOptions,\n StreamingProxy,\n} from \"./playback/create-streaming-proxy\";\n\n/**\n * Video streaming plugin: adaptive-bitrate HLS (and optionally DASH) transcoding via ffmpeg.\n *\n * Requires `fluent-ffmpeg` peer dependency and the `ffmpeg` binary in PATH.\n */\nexport function videoStreamingPlugin(opts: VideoStreamingPluginOptions = {}): PipelinePlugin {\n return {\n name: \"video-streaming\",\n runtimeManifest: {\n id: \"better-media-video-streaming\",\n version: \"1.0.0\",\n trustLevel: \"untrusted\",\n capabilities: [\"file.read\", \"metadata.write.own\", \"processing.write.own\"],\n namespace: \"video-streaming\",\n },\n executionMode: \"background\",\n intensive: true,\n apply(runtime: MediaRuntime) {\n runtime.hooks[\"process:run\"].tap(\n \"video-streaming\",\n async (context: PipelineContext, api: PluginApi) => runVideoStreaming(context, api, opts),\n { mode: \"background\" }\n );\n },\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-media/plugin-video-streaming",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Video streaming plugin for Better Media pipeline — HLS/DASH adaptive bitrate transcoding",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@better-media/core": "0.6.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"fluent-ffmpeg": ">=2.1.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependenciesMeta": {
|
|
26
|
+
"fluent-ffmpeg": {
|
|
27
|
+
"optional": true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/fluent-ffmpeg": "^2.1.27",
|
|
32
|
+
"@types/jest": "^29.5.14",
|
|
33
|
+
"@types/node": "^22.10.0",
|
|
34
|
+
"jest": "^30.2.0",
|
|
35
|
+
"ts-jest": "^29.4.6",
|
|
36
|
+
"tsup": "^8.5.1",
|
|
37
|
+
"@better-media/adapter-db-memory": "0.6.0",
|
|
38
|
+
"@better-media/adapter-storage-memory": "0.6.0"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [],
|
|
41
|
+
"author": "Abenezer Atnafu",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/AbenezerAtnafu/better-media.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://better-media.dev/",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/AbenezerAtnafu/better-media/issues"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"lint": "eslint .",
|
|
59
|
+
"test": "jest"
|
|
60
|
+
}
|
|
61
|
+
}
|