@karsten_zhou/vite-plugin-hls 1.1.0 → 1.1.2
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/README.md +2 -2
- package/dist/index.cjs +24 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,8 +98,8 @@ import type {
|
|
|
98
98
|
2. `load` transcodes the source with FFmpeg into an HLS output folder inside
|
|
99
99
|
Vite's cache directory (only once per build — results are memoized).
|
|
100
100
|
3. The output is emitted as static assets into `outputDir`, and the module's
|
|
101
|
-
default export is set to the resolved public URL of the playlist
|
|
102
|
-
|
|
101
|
+
default export is set to the resolved public URL of the playlist (with
|
|
102
|
+
Vite's `base` baked in), so it works under any `base` — client or SSR.
|
|
103
103
|
|
|
104
104
|
### SSR note
|
|
105
105
|
|
package/dist/index.cjs
CHANGED
|
@@ -169,6 +169,22 @@ function hlsMuxerOptions(outputDirectory, options) {
|
|
|
169
169
|
}
|
|
170
170
|
return hls;
|
|
171
171
|
}
|
|
172
|
+
async function ensureInitSegment(outputDirectory) {
|
|
173
|
+
const target = (0, import_node_path2.join)(outputDirectory, "init.mp4");
|
|
174
|
+
try {
|
|
175
|
+
await (0, import_promises.access)(target);
|
|
176
|
+
return;
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
const leaked = (0, import_node_path2.join)(process.cwd(), "init.mp4");
|
|
180
|
+
try {
|
|
181
|
+
await (0, import_promises.access)(leaked);
|
|
182
|
+
} catch {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
await (0, import_promises.copyFile)(leaked, target);
|
|
186
|
+
await (0, import_promises.rm)(leaked, { force: true });
|
|
187
|
+
}
|
|
172
188
|
async function remuxToHls(source, playlist, hls) {
|
|
173
189
|
var _stack = [];
|
|
174
190
|
try {
|
|
@@ -307,6 +323,9 @@ async function encodeVariant(source, outputDirectory, options, variant = {}) {
|
|
|
307
323
|
} else {
|
|
308
324
|
await transcodeToHls(source, playlist, hls, options, variant);
|
|
309
325
|
}
|
|
326
|
+
if (options.segmentType === "fmp4") {
|
|
327
|
+
await ensureInitSegment(outputDirectory);
|
|
328
|
+
}
|
|
310
329
|
}
|
|
311
330
|
async function createEncodeDirectory() {
|
|
312
331
|
return (0, import_promises.mkdtemp)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), "vite-plugin-hls-"));
|
|
@@ -346,9 +365,10 @@ async function collectFiles(directory) {
|
|
|
346
365
|
}
|
|
347
366
|
|
|
348
367
|
// src/cache.ts
|
|
368
|
+
var CACHE_VERSION = "2";
|
|
349
369
|
async function getCacheKey(source, options) {
|
|
350
370
|
const sourceData = await (0, import_promises3.readFile)(source);
|
|
351
|
-
return (0, import_node_crypto.createHash)("sha256").update(sourceData).update(JSON.stringify(options)).digest("hex");
|
|
371
|
+
return (0, import_node_crypto.createHash)("sha256").update(CACHE_VERSION).update(sourceData).update(JSON.stringify(options)).digest("hex");
|
|
352
372
|
}
|
|
353
373
|
async function readCache(cacheRoot, key) {
|
|
354
374
|
const directory = (0, import_node_path4.join)(cacheRoot, "hls", key);
|
|
@@ -487,9 +507,9 @@ function emitHls(context, result, options) {
|
|
|
487
507
|
});
|
|
488
508
|
}
|
|
489
509
|
}
|
|
490
|
-
function publicManifestExpression(result, options) {
|
|
510
|
+
function publicManifestExpression(result, options, base) {
|
|
491
511
|
const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;
|
|
492
|
-
return
|
|
512
|
+
return JSON.stringify(`${base}${path}`);
|
|
493
513
|
}
|
|
494
514
|
|
|
495
515
|
// src/plugin.ts
|
|
@@ -531,7 +551,7 @@ function hlsVideos(input) {
|
|
|
531
551
|
if (!loadOptions?.ssr) {
|
|
532
552
|
emitHls(this, result, options);
|
|
533
553
|
}
|
|
534
|
-
const manifest = publicManifestExpression(result, options);
|
|
554
|
+
const manifest = publicManifestExpression(result, options, config.base);
|
|
535
555
|
return `
|
|
536
556
|
export default ${manifest};
|
|
537
557
|
`;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/resolve.ts","../src/hls.ts","../src/encoder.ts","../src/bitrate.ts","../src/cache.ts","../src/fs.ts","../src/playlist.ts","../src/assets.ts","../src/plugin.ts"],"sourcesContent":["import { hlsVideos } from \"./plugin\";\n\nexport { hlsVideos };\nexport default hlsVideos;\n\nexport type { HlsPluginOptions, HlsVariant } from \"./types\";\n","import type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n segmentDuration: input.segmentDuration ?? 4,\n\n segmentType: input.segmentType ?? \"fmp4\",\n\n outputDir: input.outputDir ?? \"assets/hls\",\n\n preset: input.preset ?? \"medium\",\n\n crf: input.crf ?? 23,\n };\n\n if (input.mode === \"adaptive\") {\n if (!input.variants || input.variants.length === 0) {\n throw new Error(\n \"[vite-plugin-hls] Adaptive mode requires at least one variant.\",\n );\n }\n\n return {\n ...common,\n mode: \"adaptive\",\n variants: input.variants,\n };\n }\n\n return {\n ...common,\n mode: \"single\",\n };\n}\n","import { extname } from \"node:path\";\n\nimport type { PluginContext } from \"rolldown\";\n\nconst VIDEO_EXTENSIONS = new Set([\n \".mp4\",\n \".mov\",\n \".m4v\",\n \".webm\",\n \".mkv\",\n \".avi\",\n]);\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function isVideoSource(source: string): boolean {\n const pathname = source.split(\"?\")[0] ?? source;\n\n return VIDEO_EXTENSIONS.has(extname(pathname).toLowerCase());\n}\n\nexport function virtualIdForSource(source: string): string {\n return `${VIRTUAL_PREFIX}${encodeURIComponent(source)}`;\n}\n\nexport function sourceFromVirtualId(id: string): string {\n return decodeURIComponent(id.slice(VIRTUAL_PREFIX.length));\n}\n\nexport async function resolveVideoSource(\n source: string,\n importer: string,\n context: PluginContext,\n): Promise<string | undefined> {\n if (!isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await context.resolve(source, importer, {\n skipSelf: true,\n });\n\n if (!resolved || resolved.external) {\n return undefined;\n }\n\n /*\n * Queries such as ?url are irrelevant here.\n * FFmpeg needs the physical file.\n */\n return resolved.id.split(\"?\", 1)[0];\n}\n","import { basename, extname, join } from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport {\n encodeVariant,\n createEncodeDirectory,\n removeEncodeDirectory,\n} from \"./encoder\";\nimport { readCache, getCacheKey, writeCache } from \"./cache\";\nimport { collectFiles } from \"./fs\";\nimport { slugify, createMasterPlaylist } from \"./playlist\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport async function generateHls(\n source: string,\n cacheRoot: string,\n options: ResolvedHlsOptions,\n): Promise<CachedHls> {\n const key = await getCacheKey(source, options);\n\n const cached = await readCache(cacheRoot, key);\n\n if (cached) {\n console.log(`[vite-plugin-hls] cache hit ${source}`);\n\n return cached;\n }\n\n console.log(`[vite-plugin-hls] encoding ${source}`);\n\n const temporaryDirectory = await createEncodeDirectory();\n\n try {\n const videoName = slugify(basename(source, extname(source)));\n\n const directoryName = `${videoName}-${key.slice(0, 12)}`;\n\n let manifest: string;\n\n if (options.mode === \"single\") {\n await encodeVariant(source, temporaryDirectory, options);\n\n manifest = \"index.m3u8\";\n } else {\n for (const variant of options.variants) {\n await encodeVariant(\n source,\n join(temporaryDirectory, `${variant.height}p`),\n options,\n {\n height: variant.height,\n bitrate: variant.bitrate,\n },\n );\n }\n\n const master = createMasterPlaylist(options.variants);\n\n await writeFile(join(temporaryDirectory, \"master.m3u8\"), master, \"utf8\");\n\n manifest = \"master.m3u8\";\n }\n\n const result: CachedHls = {\n directoryName,\n manifest,\n files: await collectFiles(temporaryDirectory),\n };\n\n await writeCache(cacheRoot, key, result);\n\n console.log(`[vite-plugin-hls] generated ${directoryName}/${manifest}`);\n\n return result;\n } finally {\n await removeEncodeDirectory(temporaryDirectory);\n }\n}\n","import { mkdir, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from \"node-av/api\";\nimport { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from \"node-av/constants\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nconst PLAYLIST = \"index.m3u8\";\n\ntype HlsOptions = {\n hls_time: string;\n hls_playlist_type: \"vod\";\n hls_segment_filename: string;\n hls_segment_type?: \"mpegts\" | \"fmp4\";\n hls_fmp4_init_filename?: string;\n};\n\nfunction hlsMuxerOptions(\n outputDirectory: string,\n options: ResolvedHlsOptions,\n): HlsOptions {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const hls: HlsOptions = {\n hls_time: String(options.segmentDuration),\n hls_playlist_type: \"vod\",\n hls_segment_filename: join(outputDirectory, `segment-%05d.${extension}`),\n };\n\n if (options.segmentType === \"fmp4\") {\n hls.hls_segment_type = \"fmp4\";\n hls.hls_fmp4_init_filename = \"init.mp4\";\n }\n\n return hls;\n}\n\n/*\n * SINGLE MODE (remux): copy the streams, only repackage into HLS.\n */\nasync function remuxToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n): Promise<void> {\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const routed = new Map<number, number>();\n\n const video = input.video();\n if (video) {\n routed.set(video.index, output.addStream(video));\n }\n\n const audio = input.audio();\n if (audio) {\n routed.set(audio.index, output.addStream(audio));\n }\n\n for await (const packet of input.packets()) {\n if (packet === null) {\n break;\n }\n\n const index = routed.get(packet.streamIndex);\n\n if (index !== undefined) {\n await output.writePacket(packet, index);\n }\n\n packet.free();\n }\n\n await output.close();\n}\n\n/*\n * ADAPTIVE MODE (transcode): re-encode each rendition's video (scaled,\n * libx264) and audio (aac) into its HLS output.\n */\nasync function transcodeToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions,\n): Promise<void> {\n if (options.mode !== \"adaptive\") {\n throw new Error(\n \"[vite-plugin-hls] internal: transcode requires adaptive mode\",\n );\n }\n\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const tasks: Promise<void>[] = [];\n\n const video = input.video();\n if (video) {\n const decoder = await Decoder.create(video);\n\n const scale = variant.height\n ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p`\n : \"format=yuv420p\";\n\n const filter = FilterAPI.create(scale);\n\n const encoderOptions: Record<string, string | number> = {\n preset: options.preset,\n crf: String(options.crf),\n };\n\n const encoderBase: Record<string, unknown> = {\n options: encoderOptions,\n };\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n encoderOptions.maxrate = bitrate;\n encoderOptions.bufsize = Math.round(bitrate * 1.5);\n encoderBase.bitrate = bitrate;\n }\n\n const encoder = await Encoder.create(\n FF_ENCODER_LIBX264,\n encoderBase as Parameters<typeof Encoder.create>[1],\n );\n\n const index = output.addStream(encoder, { inputStream: video });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(video.index));\n const scaled = filter.frames(frames);\n\n for await (const packet of encoder.packets(scaled)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n filter.close();\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n const audio = input.audio();\n if (audio) {\n const decoder = await Decoder.create(audio);\n\n const encoder = await Encoder.create(FF_ENCODER_AAC, {\n decoder,\n bitrate: 128_000,\n });\n\n const index = output.addStream(encoder, { inputStream: audio });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(audio.index));\n\n for await (const packet of encoder.packets(frames)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n await Promise.all(tasks);\n await output.close();\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const hls = hlsMuxerOptions(outputDirectory, options);\n const playlist = join(outputDirectory, PLAYLIST);\n\n /*\n * FFmpeg's HLS muxer does not create the output directory.\n * Ensure it exists so segments/playlist can be written.\n */\n await mkdir(outputDirectory, { recursive: true });\n\n if (options.mode === \"single\") {\n await remuxToHls(source, playlist, hls);\n } else {\n await transcodeToHls(source, playlist, hls, options, variant);\n }\n}\n\nexport async function createEncodeDirectory(): Promise<string> {\n return mkdtemp(join(tmpdir(), \"vite-plugin-hls-\"));\n}\n\nexport async function removeEncodeDirectory(directory: string): Promise<void> {\n await rm(directory, {\n recursive: true,\n force: true,\n });\n}\n","/**\n * Converts a human-readable bitrate such as \"1200k\", \"2.5M\" or \"1G\"\n * into the equivalent number of bits per second.\n */\nexport function bitrateToNumber(bitrate: string): number {\n const match = /^([\\d.]+)\\s*([kKmMgG]?)$/.exec(bitrate.trim());\n\n if (!match) {\n throw new Error(`[vite-plugin-hls] Invalid bitrate: ${bitrate}`);\n }\n\n const amount = Number(match[1]);\n\n const suffix = (match[2] ?? \"\").toLowerCase();\n\n const multiplier =\n suffix === \"g\"\n ? 1_000_000_000\n : suffix === \"m\"\n ? 1_000_000\n : suffix === \"k\"\n ? 1_000\n : 1;\n\n return Math.round(amount * multiplier);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { collectFiles } from \"./fs\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\ninterface CacheManifest {\n directoryName: string;\n manifest: string;\n}\n\nexport async function getCacheKey(\n source: string,\n options: ResolvedHlsOptions,\n): Promise<string> {\n const sourceData = await readFile(source);\n\n return createHash(\"sha256\")\n .update(sourceData)\n .update(JSON.stringify(options))\n .digest(\"hex\");\n}\n\nexport async function readCache(\n cacheRoot: string,\n key: string,\n): Promise<CachedHls | undefined> {\n const directory = join(cacheRoot, \"hls\", key);\n\n try {\n await stat(directory);\n } catch {\n return undefined;\n }\n\n const metadata = JSON.parse(\n await readFile(join(directory, \"manifest.json\"), \"utf8\"),\n ) as CacheManifest;\n\n const files = await collectFiles(directory);\n\n // manifest.json is internal bookkeeping, never an emitted asset.\n files.delete(\"manifest.json\");\n\n return {\n directoryName: metadata.directoryName,\n\n manifest: metadata.manifest,\n\n files,\n };\n}\n\nexport async function writeCache(\n cacheRoot: string,\n key: string,\n result: CachedHls,\n): Promise<void> {\n const temporaryDirectory = join(cacheRoot, \"hls\", `${key}.tmp`);\n\n const finalDirectory = join(cacheRoot, \"hls\", key);\n\n await rm(temporaryDirectory, {\n recursive: true,\n force: true,\n });\n\n await mkdir(temporaryDirectory, {\n recursive: true,\n });\n\n for (const [filename, data] of result.files) {\n const destination = join(temporaryDirectory, filename);\n\n await mkdir(resolve(destination, \"..\"), {\n recursive: true,\n });\n\n await writeFile(destination, data);\n }\n\n await writeFile(\n join(temporaryDirectory, \"manifest.json\"),\n JSON.stringify({\n directoryName: result.directoryName,\n manifest: result.manifest,\n } satisfies CacheManifest),\n \"utf8\",\n );\n\n await rm(finalDirectory, {\n recursive: true,\n force: true,\n });\n\n await rename(temporaryDirectory, finalDirectory);\n}\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Recursively collects every file under `directory` into a map of\n * directory-relative POSIX paths to their buffer contents.\n */\nexport async function collectFiles(\n directory: string,\n): Promise<Map<string, Buffer>> {\n const result = new Map<string, Buffer>();\n\n async function visit(current: string): Promise<void> {\n for (const entry of await readdir(current, { withFileTypes: true })) {\n const filename = join(current, entry.name);\n\n if (entry.isDirectory()) {\n await visit(filename);\n continue;\n }\n\n result.set(\n relative(directory, filename).replaceAll(\"\\\\\", \"/\"),\n await readFile(filename),\n );\n }\n }\n\n await visit(directory);\n\n return result;\n}\n","import { extname } from \"node:path\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { HlsVariant } from \"./types\";\n\n/** Turns a filename into a filesystem-safe slug used for the output folder. */\nexport function slugify(filename: string): string {\n return (\n filename\n .replace(extname(filename), \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase() || \"video\"\n );\n}\n\n/** Builds the adaptive master playlist pointing at each rendition. */\nexport function createMasterPlaylist(variants: readonly HlsVariant[]): string {\n const lines = [\"#EXTM3U\", \"#EXT-X-VERSION:7\"];\n\n for (const variant of variants) {\n // BANDWIDTH is an estimate derived from the configured bitrate.\n const bandwidth = Math.round(bitrateToNumber(variant.bitrate) * 1.15);\n\n lines.push(\n `#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth}`,\n `${variant.height}p/index.m3u8`,\n );\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import type { PluginContext } from \"rolldown\";\n\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport function emitHls(\n context: PluginContext,\n result: CachedHls,\n options: ResolvedHlsOptions,\n): void {\n const prefix = `${options.outputDir}/${result.directoryName}`;\n\n const manifestSource = result.files.get(result.manifest);\n\n if (!manifestSource) {\n throw new Error(`[vite-plugin-hls] Missing manifest ${result.manifest}`);\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${result.manifest}`,\n source: manifestSource,\n });\n\n for (const [filename, data] of result.files) {\n if (filename === result.manifest) {\n continue;\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${filename}`,\n source: data,\n });\n }\n}\n\nexport function publicManifestExpression(\n result: CachedHls,\n options: ResolvedHlsOptions,\n): string {\n const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;\n\n /*\n * BASE_URL is replaced by Vite in both the client\n * and SSR builds.\n *\n * Examples:\n *\n * \"/\" -> /assets/hls/...\n * \"/notes/\" -> /notes/assets/hls/...\n * \"./\" -> ./assets/hls/...\n */\n return `import.meta.env.BASE_URL + ${JSON.stringify(path)}`;\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\n\nimport { resolveOptions } from \"./options\";\nimport {\n isVideoSource,\n resolveVideoSource,\n sourceFromVirtualId,\n virtualIdForSource,\n} from \"./resolve\";\nimport { generateHls } from \"./hls\";\nimport { emitHls, publicManifestExpression } from \"./assets\";\nimport type { HlsPluginOptions } from \"./types\";\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function hlsVideos(input?: HlsPluginOptions): Plugin {\n const options = resolveOptions(input);\n\n let config: ResolvedConfig;\n\n const resolvedSources = new Map<string, string>();\n\n /*\n * This only caches inside the current Vite build.\n *\n * The persistent cache in cache.ts handles the\n * client/server build boundary.\n */\n const generated = new Map<string, Awaited<ReturnType<typeof generateHls>>>();\n\n return {\n name: \"vite-plugin-hls\",\n\n apply: \"build\",\n\n enforce: \"pre\",\n\n configResolved(resolved) {\n config = resolved;\n },\n\n async resolveId(source, importer) {\n if (!importer || !isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await resolveVideoSource(source, importer, this);\n\n if (!resolved) {\n return undefined;\n }\n\n const id = virtualIdForSource(resolved);\n\n resolvedSources.set(id, resolved);\n\n return id;\n },\n\n async load(id, loadOptions) {\n if (!id.startsWith(VIRTUAL_PREFIX)) {\n return undefined;\n }\n\n const source = resolvedSources.get(id) ?? sourceFromVirtualId(id);\n\n let result = generated.get(source);\n\n if (!result) {\n result = await generateHls(source, config.cacheDir, options);\n\n generated.set(source, result);\n }\n\n /*\n * Important:\n *\n * The SSR build must NOT use Vite's emitted-file\n * URL because that resolves to a file:// URL.\n *\n * We only emit the actual files during the client\n * build. SSR receives the public URL directly.\n */\n if (!loadOptions?.ssr) {\n emitHls(this, result, options);\n }\n\n const manifest = publicManifestExpression(result, options);\n\n return `\n export default ${manifest};\n `;\n },\n };\n}\n"],"mappings":"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","names":["import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_node_path","VIRTUAL_PREFIX"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/options.ts","../src/resolve.ts","../src/hls.ts","../src/encoder.ts","../src/bitrate.ts","../src/cache.ts","../src/fs.ts","../src/playlist.ts","../src/assets.ts","../src/plugin.ts"],"sourcesContent":["import { hlsVideos } from \"./plugin\";\n\nexport { hlsVideos };\nexport default hlsVideos;\n\nexport type { HlsPluginOptions, HlsVariant } from \"./types\";\n","import type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n segmentDuration: input.segmentDuration ?? 4,\n\n segmentType: input.segmentType ?? \"fmp4\",\n\n outputDir: input.outputDir ?? \"assets/hls\",\n\n preset: input.preset ?? \"medium\",\n\n crf: input.crf ?? 23,\n };\n\n if (input.mode === \"adaptive\") {\n if (!input.variants || input.variants.length === 0) {\n throw new Error(\n \"[vite-plugin-hls] Adaptive mode requires at least one variant.\",\n );\n }\n\n return {\n ...common,\n mode: \"adaptive\",\n variants: input.variants,\n };\n }\n\n return {\n ...common,\n mode: \"single\",\n };\n}\n","import { extname } from \"node:path\";\n\nimport type { PluginContext } from \"rolldown\";\n\nconst VIDEO_EXTENSIONS = new Set([\n \".mp4\",\n \".mov\",\n \".m4v\",\n \".webm\",\n \".mkv\",\n \".avi\",\n]);\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function isVideoSource(source: string): boolean {\n const pathname = source.split(\"?\")[0] ?? source;\n\n return VIDEO_EXTENSIONS.has(extname(pathname).toLowerCase());\n}\n\nexport function virtualIdForSource(source: string): string {\n return `${VIRTUAL_PREFIX}${encodeURIComponent(source)}`;\n}\n\nexport function sourceFromVirtualId(id: string): string {\n return decodeURIComponent(id.slice(VIRTUAL_PREFIX.length));\n}\n\nexport async function resolveVideoSource(\n source: string,\n importer: string,\n context: PluginContext,\n): Promise<string | undefined> {\n if (!isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await context.resolve(source, importer, {\n skipSelf: true,\n });\n\n if (!resolved || resolved.external) {\n return undefined;\n }\n\n /*\n * Queries such as ?url are irrelevant here.\n * FFmpeg needs the physical file.\n */\n return resolved.id.split(\"?\", 1)[0];\n}\n","import { basename, extname, join } from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport {\n encodeVariant,\n createEncodeDirectory,\n removeEncodeDirectory,\n} from \"./encoder\";\nimport { readCache, getCacheKey, writeCache } from \"./cache\";\nimport { collectFiles } from \"./fs\";\nimport { slugify, createMasterPlaylist } from \"./playlist\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport async function generateHls(\n source: string,\n cacheRoot: string,\n options: ResolvedHlsOptions,\n): Promise<CachedHls> {\n const key = await getCacheKey(source, options);\n\n const cached = await readCache(cacheRoot, key);\n\n if (cached) {\n console.log(`[vite-plugin-hls] cache hit ${source}`);\n\n return cached;\n }\n\n console.log(`[vite-plugin-hls] encoding ${source}`);\n\n const temporaryDirectory = await createEncodeDirectory();\n\n try {\n const videoName = slugify(basename(source, extname(source)));\n\n const directoryName = `${videoName}-${key.slice(0, 12)}`;\n\n let manifest: string;\n\n if (options.mode === \"single\") {\n await encodeVariant(source, temporaryDirectory, options);\n\n manifest = \"index.m3u8\";\n } else {\n for (const variant of options.variants) {\n await encodeVariant(\n source,\n join(temporaryDirectory, `${variant.height}p`),\n options,\n {\n height: variant.height,\n bitrate: variant.bitrate,\n },\n );\n }\n\n const master = createMasterPlaylist(options.variants);\n\n await writeFile(join(temporaryDirectory, \"master.m3u8\"), master, \"utf8\");\n\n manifest = \"master.m3u8\";\n }\n\n const result: CachedHls = {\n directoryName,\n manifest,\n files: await collectFiles(temporaryDirectory),\n };\n\n await writeCache(cacheRoot, key, result);\n\n console.log(`[vite-plugin-hls] generated ${directoryName}/${manifest}`);\n\n return result;\n } finally {\n await removeEncodeDirectory(temporaryDirectory);\n }\n}\n","import { access, copyFile, mkdir, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from \"node-av/api\";\nimport { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from \"node-av/constants\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nconst PLAYLIST = \"index.m3u8\";\n\ntype HlsOptions = {\n hls_time: string;\n hls_playlist_type: \"vod\";\n hls_segment_filename: string;\n hls_segment_type?: \"mpegts\" | \"fmp4\";\n hls_fmp4_init_filename?: string;\n};\n\nfunction hlsMuxerOptions(\n outputDirectory: string,\n options: ResolvedHlsOptions,\n): HlsOptions {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const hls: HlsOptions = {\n hls_time: String(options.segmentDuration),\n hls_playlist_type: \"vod\",\n hls_segment_filename: join(outputDirectory, `segment-%05d.${extension}`),\n };\n\n if (options.segmentType === \"fmp4\") {\n hls.hls_segment_type = \"fmp4\";\n\n /*\n * ffmpeg base-joins hls_fmp4_init_filename onto the HLS output dir, so an\n * absolute path is doubled (e.g. on POSIX). Keep it relative and rely on\n * ensureInitSegment() to place it correctly.\n */\n hls.hls_fmp4_init_filename = \"init.mp4\";\n }\n\n return hls;\n}\n\n/*\n * ffmpeg writes the relative init filename into the HLS output directory on\n * POSIX, but on some platforms (e.g. Windows) it can instead land in the\n * process CWD. Make sure it ends up next to the playlist.\n */\nasync function ensureInitSegment(outputDirectory: string): Promise<void> {\n const target = join(outputDirectory, \"init.mp4\");\n\n try {\n await access(target);\n return;\n } catch {\n // fall through\n }\n\n const leaked = join(process.cwd(), \"init.mp4\");\n\n try {\n await access(leaked);\n } catch {\n return;\n }\n\n await copyFile(leaked, target);\n await rm(leaked, { force: true });\n}\n\n/*\n * SINGLE MODE (remux): copy the streams, only repackage into HLS.\n */\nasync function remuxToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n): Promise<void> {\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const routed = new Map<number, number>();\n\n const video = input.video();\n if (video) {\n routed.set(video.index, output.addStream(video));\n }\n\n const audio = input.audio();\n if (audio) {\n routed.set(audio.index, output.addStream(audio));\n }\n\n for await (const packet of input.packets()) {\n if (packet === null) {\n break;\n }\n\n const index = routed.get(packet.streamIndex);\n\n if (index !== undefined) {\n await output.writePacket(packet, index);\n }\n\n packet.free();\n }\n\n await output.close();\n}\n\n/*\n * ADAPTIVE MODE (transcode): re-encode each rendition's video (scaled,\n * libx264) and audio (aac) into its HLS output.\n */\nasync function transcodeToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions,\n): Promise<void> {\n if (options.mode !== \"adaptive\") {\n throw new Error(\n \"[vite-plugin-hls] internal: transcode requires adaptive mode\",\n );\n }\n\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const tasks: Promise<void>[] = [];\n\n const video = input.video();\n if (video) {\n const decoder = await Decoder.create(video);\n\n const scale = variant.height\n ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p`\n : \"format=yuv420p\";\n\n const filter = FilterAPI.create(scale);\n\n const encoderOptions: Record<string, string | number> = {\n preset: options.preset,\n crf: String(options.crf),\n };\n\n const encoderBase: Record<string, unknown> = {\n options: encoderOptions,\n };\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n encoderOptions.maxrate = bitrate;\n encoderOptions.bufsize = Math.round(bitrate * 1.5);\n encoderBase.bitrate = bitrate;\n }\n\n const encoder = await Encoder.create(\n FF_ENCODER_LIBX264,\n encoderBase as Parameters<typeof Encoder.create>[1],\n );\n\n const index = output.addStream(encoder, { inputStream: video });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(video.index));\n const scaled = filter.frames(frames);\n\n for await (const packet of encoder.packets(scaled)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n filter.close();\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n const audio = input.audio();\n if (audio) {\n const decoder = await Decoder.create(audio);\n\n const encoder = await Encoder.create(FF_ENCODER_AAC, {\n decoder,\n bitrate: 128_000,\n });\n\n const index = output.addStream(encoder, { inputStream: audio });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(audio.index));\n\n for await (const packet of encoder.packets(frames)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n await Promise.all(tasks);\n await output.close();\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const hls = hlsMuxerOptions(outputDirectory, options);\n const playlist = join(outputDirectory, PLAYLIST);\n\n /*\n * FFmpeg's HLS muxer does not create the output directory.\n * Ensure it exists so segments/playlist can be written.\n */\n await mkdir(outputDirectory, { recursive: true });\n\n if (options.mode === \"single\") {\n await remuxToHls(source, playlist, hls);\n } else {\n await transcodeToHls(source, playlist, hls, options, variant);\n }\n\n if (options.segmentType === \"fmp4\") {\n await ensureInitSegment(outputDirectory);\n }\n}\n\nexport async function createEncodeDirectory(): Promise<string> {\n return mkdtemp(join(tmpdir(), \"vite-plugin-hls-\"));\n}\n\nexport async function removeEncodeDirectory(directory: string): Promise<void> {\n await rm(directory, {\n recursive: true,\n force: true,\n });\n}\n","/**\n * Converts a human-readable bitrate such as \"1200k\", \"2.5M\" or \"1G\"\n * into the equivalent number of bits per second.\n */\nexport function bitrateToNumber(bitrate: string): number {\n const match = /^([\\d.]+)\\s*([kKmMgG]?)$/.exec(bitrate.trim());\n\n if (!match) {\n throw new Error(`[vite-plugin-hls] Invalid bitrate: ${bitrate}`);\n }\n\n const amount = Number(match[1]);\n\n const suffix = (match[2] ?? \"\").toLowerCase();\n\n const multiplier =\n suffix === \"g\"\n ? 1_000_000_000\n : suffix === \"m\"\n ? 1_000_000\n : suffix === \"k\"\n ? 1_000\n : 1;\n\n return Math.round(amount * multiplier);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { collectFiles } from \"./fs\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\ninterface CacheManifest {\n directoryName: string;\n manifest: string;\n}\n\n/*\n * Bump whenever the encoder's output layout changes (e.g. a bug fix alters\n * which files are produced). Old cache entries are keyed only on source +\n * options and would otherwise be served forever, so the version is folded\n * into the key to invalidate them.\n */\nconst CACHE_VERSION = \"2\";\n\nexport async function getCacheKey(\n source: string,\n options: ResolvedHlsOptions,\n): Promise<string> {\n const sourceData = await readFile(source);\n\n return createHash(\"sha256\")\n .update(CACHE_VERSION)\n .update(sourceData)\n .update(JSON.stringify(options))\n .digest(\"hex\");\n}\n\nexport async function readCache(\n cacheRoot: string,\n key: string,\n): Promise<CachedHls | undefined> {\n const directory = join(cacheRoot, \"hls\", key);\n\n try {\n await stat(directory);\n } catch {\n return undefined;\n }\n\n const metadata = JSON.parse(\n await readFile(join(directory, \"manifest.json\"), \"utf8\"),\n ) as CacheManifest;\n\n const files = await collectFiles(directory);\n\n // manifest.json is internal bookkeeping, never an emitted asset.\n files.delete(\"manifest.json\");\n\n return {\n directoryName: metadata.directoryName,\n\n manifest: metadata.manifest,\n\n files,\n };\n}\n\nexport async function writeCache(\n cacheRoot: string,\n key: string,\n result: CachedHls,\n): Promise<void> {\n const temporaryDirectory = join(cacheRoot, \"hls\", `${key}.tmp`);\n\n const finalDirectory = join(cacheRoot, \"hls\", key);\n\n await rm(temporaryDirectory, {\n recursive: true,\n force: true,\n });\n\n await mkdir(temporaryDirectory, {\n recursive: true,\n });\n\n for (const [filename, data] of result.files) {\n const destination = join(temporaryDirectory, filename);\n\n await mkdir(resolve(destination, \"..\"), {\n recursive: true,\n });\n\n await writeFile(destination, data);\n }\n\n await writeFile(\n join(temporaryDirectory, \"manifest.json\"),\n JSON.stringify({\n directoryName: result.directoryName,\n manifest: result.manifest,\n } satisfies CacheManifest),\n \"utf8\",\n );\n\n await rm(finalDirectory, {\n recursive: true,\n force: true,\n });\n\n await rename(temporaryDirectory, finalDirectory);\n}\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Recursively collects every file under `directory` into a map of\n * directory-relative POSIX paths to their buffer contents.\n */\nexport async function collectFiles(\n directory: string,\n): Promise<Map<string, Buffer>> {\n const result = new Map<string, Buffer>();\n\n async function visit(current: string): Promise<void> {\n for (const entry of await readdir(current, { withFileTypes: true })) {\n const filename = join(current, entry.name);\n\n if (entry.isDirectory()) {\n await visit(filename);\n continue;\n }\n\n result.set(\n relative(directory, filename).replaceAll(\"\\\\\", \"/\"),\n await readFile(filename),\n );\n }\n }\n\n await visit(directory);\n\n return result;\n}\n","import { extname } from \"node:path\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { HlsVariant } from \"./types\";\n\n/** Turns a filename into a filesystem-safe slug used for the output folder. */\nexport function slugify(filename: string): string {\n return (\n filename\n .replace(extname(filename), \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase() || \"video\"\n );\n}\n\n/** Builds the adaptive master playlist pointing at each rendition. */\nexport function createMasterPlaylist(variants: readonly HlsVariant[]): string {\n const lines = [\"#EXTM3U\", \"#EXT-X-VERSION:7\"];\n\n for (const variant of variants) {\n // BANDWIDTH is an estimate derived from the configured bitrate.\n const bandwidth = Math.round(bitrateToNumber(variant.bitrate) * 1.15);\n\n lines.push(\n `#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth}`,\n `${variant.height}p/index.m3u8`,\n );\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import type { PluginContext } from \"rolldown\";\n\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport function emitHls(\n context: PluginContext,\n result: CachedHls,\n options: ResolvedHlsOptions,\n): void {\n const prefix = `${options.outputDir}/${result.directoryName}`;\n\n const manifestSource = result.files.get(result.manifest);\n\n if (!manifestSource) {\n throw new Error(`[vite-plugin-hls] Missing manifest ${result.manifest}`);\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${result.manifest}`,\n source: manifestSource,\n });\n\n for (const [filename, data] of result.files) {\n if (filename === result.manifest) {\n continue;\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${filename}`,\n source: data,\n });\n }\n}\n\nexport function publicManifestExpression(\n result: CachedHls,\n options: ResolvedHlsOptions,\n base: string,\n): string {\n const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;\n\n /*\n * Bake Vite's resolved base straight into the module. Vite does not always\n * statically replace import.meta.env.BASE_URL (e.g. in SSR bundles), where\n * import.meta.env is undefined at runtime and crashes. Emitting the base as\n * a plain string is equivalent and works for client and SSR alike.\n *\n * Examples (base -> export):\n *\n * \"/\" -> \"/assets/hls/...\"\n * \"/notes/\" -> \"/notes/assets/hls/...\"\n */\n return JSON.stringify(`${base}${path}`);\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\n\nimport { resolveOptions } from \"./options\";\nimport {\n isVideoSource,\n resolveVideoSource,\n sourceFromVirtualId,\n virtualIdForSource,\n} from \"./resolve\";\nimport { generateHls } from \"./hls\";\nimport { emitHls, publicManifestExpression } from \"./assets\";\nimport type { HlsPluginOptions } from \"./types\";\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function hlsVideos(input?: HlsPluginOptions): Plugin {\n const options = resolveOptions(input);\n\n let config: ResolvedConfig;\n\n const resolvedSources = new Map<string, string>();\n\n /*\n * This only caches inside the current Vite build.\n *\n * The persistent cache in cache.ts handles the\n * client/server build boundary.\n */\n const generated = new Map<string, Awaited<ReturnType<typeof generateHls>>>();\n\n return {\n name: \"vite-plugin-hls\",\n\n apply: \"build\",\n\n enforce: \"pre\",\n\n configResolved(resolved) {\n config = resolved;\n },\n\n async resolveId(source, importer) {\n if (!importer || !isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await resolveVideoSource(source, importer, this);\n\n if (!resolved) {\n return undefined;\n }\n\n const id = virtualIdForSource(resolved);\n\n resolvedSources.set(id, resolved);\n\n return id;\n },\n\n async load(id, loadOptions) {\n if (!id.startsWith(VIRTUAL_PREFIX)) {\n return undefined;\n }\n\n const source = resolvedSources.get(id) ?? sourceFromVirtualId(id);\n\n let result = generated.get(source);\n\n if (!result) {\n result = await generateHls(source, config.cacheDir, options);\n\n generated.set(source, result);\n }\n\n /*\n * Important:\n *\n * The SSR build must NOT use Vite's emitted-file\n * URL because that resolves to a file:// URL.\n *\n * We only emit the actual files during the client\n * build. SSR receives the public URL directly.\n */\n if (!loadOptions?.ssr) {\n emitHls(this, result, options);\n }\n\n const manifest = publicManifestExpression(result, options, config.base);\n\n return `\n export default ${manifest};\n `;\n },\n };\n}\n"],"mappings":"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","names":["import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_node_path","VIRTUAL_PREFIX"]}
|
package/dist/index.js
CHANGED
|
@@ -110,7 +110,7 @@ import { basename, extname as extname3, join as join4 } from "path";
|
|
|
110
110
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
111
111
|
|
|
112
112
|
// src/encoder.ts
|
|
113
|
-
import { mkdir, mkdtemp, rm } from "fs/promises";
|
|
113
|
+
import { access, copyFile, mkdir, mkdtemp, rm } from "fs/promises";
|
|
114
114
|
import { join } from "path";
|
|
115
115
|
import { tmpdir } from "os";
|
|
116
116
|
import { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from "node-av/api";
|
|
@@ -143,6 +143,22 @@ function hlsMuxerOptions(outputDirectory, options) {
|
|
|
143
143
|
}
|
|
144
144
|
return hls;
|
|
145
145
|
}
|
|
146
|
+
async function ensureInitSegment(outputDirectory) {
|
|
147
|
+
const target = join(outputDirectory, "init.mp4");
|
|
148
|
+
try {
|
|
149
|
+
await access(target);
|
|
150
|
+
return;
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
const leaked = join(process.cwd(), "init.mp4");
|
|
154
|
+
try {
|
|
155
|
+
await access(leaked);
|
|
156
|
+
} catch {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
await copyFile(leaked, target);
|
|
160
|
+
await rm(leaked, { force: true });
|
|
161
|
+
}
|
|
146
162
|
async function remuxToHls(source, playlist, hls) {
|
|
147
163
|
var _stack = [];
|
|
148
164
|
try {
|
|
@@ -281,6 +297,9 @@ async function encodeVariant(source, outputDirectory, options, variant = {}) {
|
|
|
281
297
|
} else {
|
|
282
298
|
await transcodeToHls(source, playlist, hls, options, variant);
|
|
283
299
|
}
|
|
300
|
+
if (options.segmentType === "fmp4") {
|
|
301
|
+
await ensureInitSegment(outputDirectory);
|
|
302
|
+
}
|
|
284
303
|
}
|
|
285
304
|
async function createEncodeDirectory() {
|
|
286
305
|
return mkdtemp(join(tmpdir(), "vite-plugin-hls-"));
|
|
@@ -320,9 +339,10 @@ async function collectFiles(directory) {
|
|
|
320
339
|
}
|
|
321
340
|
|
|
322
341
|
// src/cache.ts
|
|
342
|
+
var CACHE_VERSION = "2";
|
|
323
343
|
async function getCacheKey(source, options) {
|
|
324
344
|
const sourceData = await readFile2(source);
|
|
325
|
-
return createHash("sha256").update(sourceData).update(JSON.stringify(options)).digest("hex");
|
|
345
|
+
return createHash("sha256").update(CACHE_VERSION).update(sourceData).update(JSON.stringify(options)).digest("hex");
|
|
326
346
|
}
|
|
327
347
|
async function readCache(cacheRoot, key) {
|
|
328
348
|
const directory = join3(cacheRoot, "hls", key);
|
|
@@ -461,9 +481,9 @@ function emitHls(context, result, options) {
|
|
|
461
481
|
});
|
|
462
482
|
}
|
|
463
483
|
}
|
|
464
|
-
function publicManifestExpression(result, options) {
|
|
484
|
+
function publicManifestExpression(result, options, base) {
|
|
465
485
|
const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;
|
|
466
|
-
return
|
|
486
|
+
return JSON.stringify(`${base}${path}`);
|
|
467
487
|
}
|
|
468
488
|
|
|
469
489
|
// src/plugin.ts
|
|
@@ -505,7 +525,7 @@ function hlsVideos(input) {
|
|
|
505
525
|
if (!loadOptions?.ssr) {
|
|
506
526
|
emitHls(this, result, options);
|
|
507
527
|
}
|
|
508
|
-
const manifest = publicManifestExpression(result, options);
|
|
528
|
+
const manifest = publicManifestExpression(result, options, config.base);
|
|
509
529
|
return `
|
|
510
530
|
export default ${manifest};
|
|
511
531
|
`;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/options.ts","../src/resolve.ts","../src/hls.ts","../src/encoder.ts","../src/bitrate.ts","../src/cache.ts","../src/fs.ts","../src/playlist.ts","../src/assets.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n segmentDuration: input.segmentDuration ?? 4,\n\n segmentType: input.segmentType ?? \"fmp4\",\n\n outputDir: input.outputDir ?? \"assets/hls\",\n\n preset: input.preset ?? \"medium\",\n\n crf: input.crf ?? 23,\n };\n\n if (input.mode === \"adaptive\") {\n if (!input.variants || input.variants.length === 0) {\n throw new Error(\n \"[vite-plugin-hls] Adaptive mode requires at least one variant.\",\n );\n }\n\n return {\n ...common,\n mode: \"adaptive\",\n variants: input.variants,\n };\n }\n\n return {\n ...common,\n mode: \"single\",\n };\n}\n","import { extname } from \"node:path\";\n\nimport type { PluginContext } from \"rolldown\";\n\nconst VIDEO_EXTENSIONS = new Set([\n \".mp4\",\n \".mov\",\n \".m4v\",\n \".webm\",\n \".mkv\",\n \".avi\",\n]);\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function isVideoSource(source: string): boolean {\n const pathname = source.split(\"?\")[0] ?? source;\n\n return VIDEO_EXTENSIONS.has(extname(pathname).toLowerCase());\n}\n\nexport function virtualIdForSource(source: string): string {\n return `${VIRTUAL_PREFIX}${encodeURIComponent(source)}`;\n}\n\nexport function sourceFromVirtualId(id: string): string {\n return decodeURIComponent(id.slice(VIRTUAL_PREFIX.length));\n}\n\nexport async function resolveVideoSource(\n source: string,\n importer: string,\n context: PluginContext,\n): Promise<string | undefined> {\n if (!isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await context.resolve(source, importer, {\n skipSelf: true,\n });\n\n if (!resolved || resolved.external) {\n return undefined;\n }\n\n /*\n * Queries such as ?url are irrelevant here.\n * FFmpeg needs the physical file.\n */\n return resolved.id.split(\"?\", 1)[0];\n}\n","import { basename, extname, join } from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport {\n encodeVariant,\n createEncodeDirectory,\n removeEncodeDirectory,\n} from \"./encoder\";\nimport { readCache, getCacheKey, writeCache } from \"./cache\";\nimport { collectFiles } from \"./fs\";\nimport { slugify, createMasterPlaylist } from \"./playlist\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport async function generateHls(\n source: string,\n cacheRoot: string,\n options: ResolvedHlsOptions,\n): Promise<CachedHls> {\n const key = await getCacheKey(source, options);\n\n const cached = await readCache(cacheRoot, key);\n\n if (cached) {\n console.log(`[vite-plugin-hls] cache hit ${source}`);\n\n return cached;\n }\n\n console.log(`[vite-plugin-hls] encoding ${source}`);\n\n const temporaryDirectory = await createEncodeDirectory();\n\n try {\n const videoName = slugify(basename(source, extname(source)));\n\n const directoryName = `${videoName}-${key.slice(0, 12)}`;\n\n let manifest: string;\n\n if (options.mode === \"single\") {\n await encodeVariant(source, temporaryDirectory, options);\n\n manifest = \"index.m3u8\";\n } else {\n for (const variant of options.variants) {\n await encodeVariant(\n source,\n join(temporaryDirectory, `${variant.height}p`),\n options,\n {\n height: variant.height,\n bitrate: variant.bitrate,\n },\n );\n }\n\n const master = createMasterPlaylist(options.variants);\n\n await writeFile(join(temporaryDirectory, \"master.m3u8\"), master, \"utf8\");\n\n manifest = \"master.m3u8\";\n }\n\n const result: CachedHls = {\n directoryName,\n manifest,\n files: await collectFiles(temporaryDirectory),\n };\n\n await writeCache(cacheRoot, key, result);\n\n console.log(`[vite-plugin-hls] generated ${directoryName}/${manifest}`);\n\n return result;\n } finally {\n await removeEncodeDirectory(temporaryDirectory);\n }\n}\n","import { mkdir, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from \"node-av/api\";\nimport { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from \"node-av/constants\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nconst PLAYLIST = \"index.m3u8\";\n\ntype HlsOptions = {\n hls_time: string;\n hls_playlist_type: \"vod\";\n hls_segment_filename: string;\n hls_segment_type?: \"mpegts\" | \"fmp4\";\n hls_fmp4_init_filename?: string;\n};\n\nfunction hlsMuxerOptions(\n outputDirectory: string,\n options: ResolvedHlsOptions,\n): HlsOptions {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const hls: HlsOptions = {\n hls_time: String(options.segmentDuration),\n hls_playlist_type: \"vod\",\n hls_segment_filename: join(outputDirectory, `segment-%05d.${extension}`),\n };\n\n if (options.segmentType === \"fmp4\") {\n hls.hls_segment_type = \"fmp4\";\n hls.hls_fmp4_init_filename = \"init.mp4\";\n }\n\n return hls;\n}\n\n/*\n * SINGLE MODE (remux): copy the streams, only repackage into HLS.\n */\nasync function remuxToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n): Promise<void> {\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const routed = new Map<number, number>();\n\n const video = input.video();\n if (video) {\n routed.set(video.index, output.addStream(video));\n }\n\n const audio = input.audio();\n if (audio) {\n routed.set(audio.index, output.addStream(audio));\n }\n\n for await (const packet of input.packets()) {\n if (packet === null) {\n break;\n }\n\n const index = routed.get(packet.streamIndex);\n\n if (index !== undefined) {\n await output.writePacket(packet, index);\n }\n\n packet.free();\n }\n\n await output.close();\n}\n\n/*\n * ADAPTIVE MODE (transcode): re-encode each rendition's video (scaled,\n * libx264) and audio (aac) into its HLS output.\n */\nasync function transcodeToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions,\n): Promise<void> {\n if (options.mode !== \"adaptive\") {\n throw new Error(\n \"[vite-plugin-hls] internal: transcode requires adaptive mode\",\n );\n }\n\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const tasks: Promise<void>[] = [];\n\n const video = input.video();\n if (video) {\n const decoder = await Decoder.create(video);\n\n const scale = variant.height\n ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p`\n : \"format=yuv420p\";\n\n const filter = FilterAPI.create(scale);\n\n const encoderOptions: Record<string, string | number> = {\n preset: options.preset,\n crf: String(options.crf),\n };\n\n const encoderBase: Record<string, unknown> = {\n options: encoderOptions,\n };\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n encoderOptions.maxrate = bitrate;\n encoderOptions.bufsize = Math.round(bitrate * 1.5);\n encoderBase.bitrate = bitrate;\n }\n\n const encoder = await Encoder.create(\n FF_ENCODER_LIBX264,\n encoderBase as Parameters<typeof Encoder.create>[1],\n );\n\n const index = output.addStream(encoder, { inputStream: video });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(video.index));\n const scaled = filter.frames(frames);\n\n for await (const packet of encoder.packets(scaled)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n filter.close();\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n const audio = input.audio();\n if (audio) {\n const decoder = await Decoder.create(audio);\n\n const encoder = await Encoder.create(FF_ENCODER_AAC, {\n decoder,\n bitrate: 128_000,\n });\n\n const index = output.addStream(encoder, { inputStream: audio });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(audio.index));\n\n for await (const packet of encoder.packets(frames)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n await Promise.all(tasks);\n await output.close();\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const hls = hlsMuxerOptions(outputDirectory, options);\n const playlist = join(outputDirectory, PLAYLIST);\n\n /*\n * FFmpeg's HLS muxer does not create the output directory.\n * Ensure it exists so segments/playlist can be written.\n */\n await mkdir(outputDirectory, { recursive: true });\n\n if (options.mode === \"single\") {\n await remuxToHls(source, playlist, hls);\n } else {\n await transcodeToHls(source, playlist, hls, options, variant);\n }\n}\n\nexport async function createEncodeDirectory(): Promise<string> {\n return mkdtemp(join(tmpdir(), \"vite-plugin-hls-\"));\n}\n\nexport async function removeEncodeDirectory(directory: string): Promise<void> {\n await rm(directory, {\n recursive: true,\n force: true,\n });\n}\n","/**\n * Converts a human-readable bitrate such as \"1200k\", \"2.5M\" or \"1G\"\n * into the equivalent number of bits per second.\n */\nexport function bitrateToNumber(bitrate: string): number {\n const match = /^([\\d.]+)\\s*([kKmMgG]?)$/.exec(bitrate.trim());\n\n if (!match) {\n throw new Error(`[vite-plugin-hls] Invalid bitrate: ${bitrate}`);\n }\n\n const amount = Number(match[1]);\n\n const suffix = (match[2] ?? \"\").toLowerCase();\n\n const multiplier =\n suffix === \"g\"\n ? 1_000_000_000\n : suffix === \"m\"\n ? 1_000_000\n : suffix === \"k\"\n ? 1_000\n : 1;\n\n return Math.round(amount * multiplier);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { collectFiles } from \"./fs\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\ninterface CacheManifest {\n directoryName: string;\n manifest: string;\n}\n\nexport async function getCacheKey(\n source: string,\n options: ResolvedHlsOptions,\n): Promise<string> {\n const sourceData = await readFile(source);\n\n return createHash(\"sha256\")\n .update(sourceData)\n .update(JSON.stringify(options))\n .digest(\"hex\");\n}\n\nexport async function readCache(\n cacheRoot: string,\n key: string,\n): Promise<CachedHls | undefined> {\n const directory = join(cacheRoot, \"hls\", key);\n\n try {\n await stat(directory);\n } catch {\n return undefined;\n }\n\n const metadata = JSON.parse(\n await readFile(join(directory, \"manifest.json\"), \"utf8\"),\n ) as CacheManifest;\n\n const files = await collectFiles(directory);\n\n // manifest.json is internal bookkeeping, never an emitted asset.\n files.delete(\"manifest.json\");\n\n return {\n directoryName: metadata.directoryName,\n\n manifest: metadata.manifest,\n\n files,\n };\n}\n\nexport async function writeCache(\n cacheRoot: string,\n key: string,\n result: CachedHls,\n): Promise<void> {\n const temporaryDirectory = join(cacheRoot, \"hls\", `${key}.tmp`);\n\n const finalDirectory = join(cacheRoot, \"hls\", key);\n\n await rm(temporaryDirectory, {\n recursive: true,\n force: true,\n });\n\n await mkdir(temporaryDirectory, {\n recursive: true,\n });\n\n for (const [filename, data] of result.files) {\n const destination = join(temporaryDirectory, filename);\n\n await mkdir(resolve(destination, \"..\"), {\n recursive: true,\n });\n\n await writeFile(destination, data);\n }\n\n await writeFile(\n join(temporaryDirectory, \"manifest.json\"),\n JSON.stringify({\n directoryName: result.directoryName,\n manifest: result.manifest,\n } satisfies CacheManifest),\n \"utf8\",\n );\n\n await rm(finalDirectory, {\n recursive: true,\n force: true,\n });\n\n await rename(temporaryDirectory, finalDirectory);\n}\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Recursively collects every file under `directory` into a map of\n * directory-relative POSIX paths to their buffer contents.\n */\nexport async function collectFiles(\n directory: string,\n): Promise<Map<string, Buffer>> {\n const result = new Map<string, Buffer>();\n\n async function visit(current: string): Promise<void> {\n for (const entry of await readdir(current, { withFileTypes: true })) {\n const filename = join(current, entry.name);\n\n if (entry.isDirectory()) {\n await visit(filename);\n continue;\n }\n\n result.set(\n relative(directory, filename).replaceAll(\"\\\\\", \"/\"),\n await readFile(filename),\n );\n }\n }\n\n await visit(directory);\n\n return result;\n}\n","import { extname } from \"node:path\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { HlsVariant } from \"./types\";\n\n/** Turns a filename into a filesystem-safe slug used for the output folder. */\nexport function slugify(filename: string): string {\n return (\n filename\n .replace(extname(filename), \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase() || \"video\"\n );\n}\n\n/** Builds the adaptive master playlist pointing at each rendition. */\nexport function createMasterPlaylist(variants: readonly HlsVariant[]): string {\n const lines = [\"#EXTM3U\", \"#EXT-X-VERSION:7\"];\n\n for (const variant of variants) {\n // BANDWIDTH is an estimate derived from the configured bitrate.\n const bandwidth = Math.round(bitrateToNumber(variant.bitrate) * 1.15);\n\n lines.push(\n `#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth}`,\n `${variant.height}p/index.m3u8`,\n );\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import type { PluginContext } from \"rolldown\";\n\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport function emitHls(\n context: PluginContext,\n result: CachedHls,\n options: ResolvedHlsOptions,\n): void {\n const prefix = `${options.outputDir}/${result.directoryName}`;\n\n const manifestSource = result.files.get(result.manifest);\n\n if (!manifestSource) {\n throw new Error(`[vite-plugin-hls] Missing manifest ${result.manifest}`);\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${result.manifest}`,\n source: manifestSource,\n });\n\n for (const [filename, data] of result.files) {\n if (filename === result.manifest) {\n continue;\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${filename}`,\n source: data,\n });\n }\n}\n\nexport function publicManifestExpression(\n result: CachedHls,\n options: ResolvedHlsOptions,\n): string {\n const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;\n\n /*\n * BASE_URL is replaced by Vite in both the client\n * and SSR builds.\n *\n * Examples:\n *\n * \"/\" -> /assets/hls/...\n * \"/notes/\" -> /notes/assets/hls/...\n * \"./\" -> ./assets/hls/...\n */\n return `import.meta.env.BASE_URL + ${JSON.stringify(path)}`;\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\n\nimport { resolveOptions } from \"./options\";\nimport {\n isVideoSource,\n resolveVideoSource,\n sourceFromVirtualId,\n virtualIdForSource,\n} from \"./resolve\";\nimport { generateHls } from \"./hls\";\nimport { emitHls, publicManifestExpression } from \"./assets\";\nimport type { HlsPluginOptions } from \"./types\";\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function hlsVideos(input?: HlsPluginOptions): Plugin {\n const options = resolveOptions(input);\n\n let config: ResolvedConfig;\n\n const resolvedSources = new Map<string, string>();\n\n /*\n * This only caches inside the current Vite build.\n *\n * The persistent cache in cache.ts handles the\n * client/server build boundary.\n */\n const generated = new Map<string, Awaited<ReturnType<typeof generateHls>>>();\n\n return {\n name: \"vite-plugin-hls\",\n\n apply: \"build\",\n\n enforce: \"pre\",\n\n configResolved(resolved) {\n config = resolved;\n },\n\n async resolveId(source, importer) {\n if (!importer || !isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await resolveVideoSource(source, importer, this);\n\n if (!resolved) {\n return undefined;\n }\n\n const id = virtualIdForSource(resolved);\n\n resolvedSources.set(id, resolved);\n\n return id;\n },\n\n async load(id, loadOptions) {\n if (!id.startsWith(VIRTUAL_PREFIX)) {\n return undefined;\n }\n\n const source = resolvedSources.get(id) ?? sourceFromVirtualId(id);\n\n let result = generated.get(source);\n\n if (!result) {\n result = await generateHls(source, config.cacheDir, options);\n\n generated.set(source, result);\n }\n\n /*\n * Important:\n *\n * The SSR build must NOT use Vite's emitted-file\n * URL because that resolves to a file:// URL.\n *\n * We only emit the actual files during the client\n * build. SSR receives the public URL directly.\n */\n if (!loadOptions?.ssr) {\n emitHls(this, result, options);\n }\n\n const manifest = publicManifestExpression(result, options);\n\n return `\n export default ${manifest};\n `;\n },\n };\n}\n","import { hlsVideos } from \"./plugin\";\n\nexport { hlsVideos };\nexport default hlsVideos;\n\nexport type { HlsPluginOptions, HlsVariant } from \"./types\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEO,SAAS,eACd,QAA0B,CAAC,GACP;AACpB,QAAM,SAAS;AAAA,IACb,iBAAiB,MAAM,mBAAmB;AAAA,IAE1C,aAAa,MAAM,eAAe;AAAA,IAElC,WAAW,MAAM,aAAa;AAAA,IAE9B,QAAQ,MAAM,UAAU;AAAA,IAExB,KAAK,MAAM,OAAO;AAAA,EACpB;AAEA,MAAI,MAAM,SAAS,YAAY;AAC7B,QAAI,CAAC,MAAM,YAAY,MAAM,SAAS,WAAW,GAAG;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,MAAM;AAAA,MACN,UAAU,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,EACR;AACF;;;ACnCA,SAAS,eAAe;AAIxB,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,iBAAiB;AAEhB,SAAS,cAAc,QAAyB;AACrD,QAAM,WAAW,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK;AAEzC,SAAO,iBAAiB,IAAI,QAAQ,QAAQ,EAAE,YAAY,CAAC;AAC7D;AAEO,SAAS,mBAAmB,QAAwB;AACzD,SAAO,GAAG,cAAc,GAAG,mBAAmB,MAAM,CAAC;AACvD;AAEO,SAAS,oBAAoB,IAAoB;AACtD,SAAO,mBAAmB,GAAG,MAAM,eAAe,MAAM,CAAC;AAC3D;AAEA,eAAsB,mBACpB,QACA,UACA,SAC6B;AAC7B,MAAI,CAAC,cAAc,MAAM,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,QAAQ,QAAQ,QAAQ,UAAU;AAAA,IACvD,UAAU;AAAA,EACZ,CAAC;AAED,MAAI,CAAC,YAAY,SAAS,UAAU;AAClC,WAAO;AAAA,EACT;AAMA,SAAO,SAAS,GAAG,MAAM,KAAK,CAAC,EAAE,CAAC;AACpC;;;ACnDA,SAAS,UAAU,WAAAA,UAAS,QAAAC,aAAY;AACxC,SAAS,aAAAC,kBAAiB;;;ACD1B,SAAS,OAAO,SAAS,UAAU;AACnC,SAAS,YAAY;AACrB,SAAS,cAAc;AAEvB,SAAS,SAAS,SAAS,SAAS,WAAW,aAAa;AAC5D,SAAS,gBAAgB,0BAA0B;;;ACD5C,SAAS,gBAAgB,SAAyB;AACvD,QAAM,QAAQ,2BAA2B,KAAK,QAAQ,KAAK,CAAC;AAE5D,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,sCAAsC,OAAO,EAAE;AAAA,EACjE;AAEA,QAAM,SAAS,OAAO,MAAM,CAAC,CAAC;AAE9B,QAAM,UAAU,MAAM,CAAC,KAAK,IAAI,YAAY;AAE5C,QAAM,aACJ,WAAW,MACP,MACA,WAAW,MACT,MACA,WAAW,MACT,MACA;AAEV,SAAO,KAAK,MAAM,SAAS,UAAU;AACvC;;;ADVA,IAAM,WAAW;AAUjB,SAAS,gBACP,iBACA,SACY;AACZ,QAAM,YAAY,QAAQ,gBAAgB,SAAS,QAAQ;AAE3D,QAAM,MAAkB;AAAA,IACtB,UAAU,OAAO,QAAQ,eAAe;AAAA,IACxC,mBAAmB;AAAA,IACnB,sBAAsB,KAAK,iBAAiB,gBAAgB,SAAS,EAAE;AAAA,EACzE;AAEA,MAAI,QAAQ,gBAAgB,QAAQ;AAClC,QAAI,mBAAmB;AACvB,QAAI,yBAAyB;AAAA,EAC/B;AAEA,SAAO;AACT;AAKA,eAAe,WACb,QACA,UACA,KACe;AACf;AAAA;AAAA,UAAY,QAAQ,sBAAM,QAAQ,KAAK,MAAM,GAAzB;AACpB,UAAY,SAAS,sBAAM,MAAM,KAAK,UAAU;AAAA,MAC9C,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,IACX,CAAC,GAJoB;AAMrB,UAAM,SAAS,oBAAI,IAAoB;AAEvC,UAAM,QAAQ,MAAM,MAAM;AAC1B,QAAI,OAAO;AACT,aAAO,IAAI,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;AAAA,IACjD;AAEA,UAAM,QAAQ,MAAM,MAAM;AAC1B,QAAI,OAAO;AACT,aAAO,IAAI,MAAM,OAAO,OAAO,UAAU,KAAK,CAAC;AAAA,IACjD;AAEA,qBAAiB,UAAU,MAAM,QAAQ,GAAG;AAC1C,UAAI,WAAW,MAAM;AACnB;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,IAAI,OAAO,WAAW;AAE3C,UAAI,UAAU,QAAW;AACvB,cAAM,OAAO,YAAY,QAAQ,KAAK;AAAA,MACxC;AAEA,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,OAAO,MAAM;AAAA,WAjCnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCF;AAMA,eAAe,eACb,QACA,UACA,KACA,SACA,SACe;AAOf;AAAA;AANA,QAAI,QAAQ,SAAS,YAAY;AAC/B,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAY,QAAQ,sBAAM,QAAQ,KAAK,MAAM,GAAzB;AACpB,UAAY,SAAS,sBAAM,MAAM,KAAK,UAAU;AAAA,MAC9C,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,IACX,CAAC,GAJoB;AAMrB,UAAM,QAAyB,CAAC;AAEhC,UAAM,QAAQ,MAAM,MAAM;AAC1B,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,QAAQ,OAAO,KAAK;AAE1C,YAAM,QAAQ,QAAQ,SAClB,YAAY,QAAQ,MAAM,yDAC1B;AAEJ,YAAM,SAAS,UAAU,OAAO,KAAK;AAErC,YAAM,iBAAkD;AAAA,QACtD,QAAQ,QAAQ;AAAA,QAChB,KAAK,OAAO,QAAQ,GAAG;AAAA,MACzB;AAEA,YAAM,cAAuC;AAAA,QAC3C,SAAS;AAAA,MACX;AAEA,UAAI,QAAQ,YAAY,QAAW;AACjC,cAAM,UAAU,gBAAgB,QAAQ,OAAO;AAE/C,uBAAe,UAAU;AACzB,uBAAe,UAAU,KAAK,MAAM,UAAU,GAAG;AACjD,oBAAY,UAAU;AAAA,MACxB;AAEA,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B;AAAA,QACA;AAAA,MACF;AAEA,YAAM,QAAQ,OAAO,UAAU,SAAS,EAAE,aAAa,MAAM,CAAC;AAE9D,YAAM;AAAA,SACH,YAAY;AACX,cAAI;AACF,kBAAM,SAAS,QAAQ,OAAO,MAAM,QAAQ,MAAM,KAAK,CAAC;AACxD,kBAAM,SAAS,OAAO,OAAO,MAAM;AAEnC,6BAAiB,UAAU,QAAQ,QAAQ,MAAM,GAAG;AAClD,kBAAI,WAAW,MAAM;AACnB;AAAA,cACF;AACA,oBAAM,OAAO,YAAY,QAAQ,KAAK;AACtC,qBAAO,KAAK;AAAA,YACd;AAAA,UACF,UAAE;AACA,mBAAO,MAAM;AACb,oBAAQ,MAAM;AACd,oBAAQ,MAAM;AAAA,UAChB;AAAA,QACF,GAAG;AAAA,MACL;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,MAAM;AAC1B,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,QAAQ,OAAO,KAAK;AAE1C,YAAM,UAAU,MAAM,QAAQ,OAAO,gBAAgB;AAAA,QACnD;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAED,YAAM,QAAQ,OAAO,UAAU,SAAS,EAAE,aAAa,MAAM,CAAC;AAE9D,YAAM;AAAA,SACH,YAAY;AACX,cAAI;AACF,kBAAM,SAAS,QAAQ,OAAO,MAAM,QAAQ,MAAM,KAAK,CAAC;AAExD,6BAAiB,UAAU,QAAQ,QAAQ,MAAM,GAAG;AAClD,kBAAI,WAAW,MAAM;AACnB;AAAA,cACF;AACA,oBAAM,OAAO,YAAY,QAAQ,KAAK;AACtC,qBAAO,KAAK;AAAA,YACd;AAAA,UACF,UAAE;AACA,oBAAQ,MAAM;AACd,oBAAQ,MAAM;AAAA,UAChB;AAAA,QACF,GAAG;AAAA,MACL;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,KAAK;AACvB,UAAM,OAAO,MAAM;AAAA,WAjGnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAkGF;AAEA,eAAsB,cACpB,QACA,iBACA,SACA,UAAgC,CAAC,GAClB;AACf,QAAM,MAAM,gBAAgB,iBAAiB,OAAO;AACpD,QAAM,WAAW,KAAK,iBAAiB,QAAQ;AAM/C,QAAM,MAAM,iBAAiB,EAAE,WAAW,KAAK,CAAC;AAEhD,MAAI,QAAQ,SAAS,UAAU;AAC7B,UAAM,WAAW,QAAQ,UAAU,GAAG;AAAA,EACxC,OAAO;AACL,UAAM,eAAe,QAAQ,UAAU,KAAK,SAAS,OAAO;AAAA,EAC9D;AACF;AAEA,eAAsB,wBAAyC;AAC7D,SAAO,QAAQ,KAAK,OAAO,GAAG,kBAAkB,CAAC;AACnD;AAEA,eAAsB,sBAAsB,WAAkC;AAC5E,QAAM,GAAG,WAAW;AAAA,IAClB,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AACH;;;AE7OA,SAAS,kBAAkB;AAC3B,SAAS,SAAAC,QAAO,YAAAC,WAAU,QAAQ,MAAAC,KAAI,MAAM,iBAAiB;AAC7D,SAAS,QAAAC,OAAM,eAAe;;;ACF9B,SAAS,UAAU,eAAe;AAClC,SAAS,QAAAC,OAAM,gBAAgB;AAM/B,eAAsB,aACpB,WAC8B;AAC9B,QAAM,SAAS,oBAAI,IAAoB;AAEvC,iBAAe,MAAM,SAAgC;AACnD,eAAW,SAAS,MAAM,QAAQ,SAAS,EAAE,eAAe,KAAK,CAAC,GAAG;AACnE,YAAM,WAAWA,MAAK,SAAS,MAAM,IAAI;AAEzC,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,MAAM,QAAQ;AACpB;AAAA,MACF;AAEA,aAAO;AAAA,QACL,SAAS,WAAW,QAAQ,EAAE,WAAW,MAAM,GAAG;AAAA,QAClD,MAAM,SAAS,QAAQ;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,SAAS;AAErB,SAAO;AACT;;;ADnBA,eAAsB,YACpB,QACA,SACiB;AACjB,QAAM,aAAa,MAAMC,UAAS,MAAM;AAExC,SAAO,WAAW,QAAQ,EACvB,OAAO,UAAU,EACjB,OAAO,KAAK,UAAU,OAAO,CAAC,EAC9B,OAAO,KAAK;AACjB;AAEA,eAAsB,UACpB,WACA,KACgC;AAChC,QAAM,YAAYC,MAAK,WAAW,OAAO,GAAG;AAE5C,MAAI;AACF,UAAM,KAAK,SAAS;AAAA,EACtB,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,KAAK;AAAA,IACpB,MAAMD,UAASC,MAAK,WAAW,eAAe,GAAG,MAAM;AAAA,EACzD;AAEA,QAAM,QAAQ,MAAM,aAAa,SAAS;AAG1C,QAAM,OAAO,eAAe;AAE5B,SAAO;AAAA,IACL,eAAe,SAAS;AAAA,IAExB,UAAU,SAAS;AAAA,IAEnB;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,WACA,KACA,QACe;AACf,QAAM,qBAAqBA,MAAK,WAAW,OAAO,GAAG,GAAG,MAAM;AAE9D,QAAM,iBAAiBA,MAAK,WAAW,OAAO,GAAG;AAEjD,QAAMC,IAAG,oBAAoB;AAAA,IAC3B,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,QAAMC,OAAM,oBAAoB;AAAA,IAC9B,WAAW;AAAA,EACb,CAAC;AAED,aAAW,CAAC,UAAU,IAAI,KAAK,OAAO,OAAO;AAC3C,UAAM,cAAcF,MAAK,oBAAoB,QAAQ;AAErD,UAAME,OAAM,QAAQ,aAAa,IAAI,GAAG;AAAA,MACtC,WAAW;AAAA,IACb,CAAC;AAED,UAAM,UAAU,aAAa,IAAI;AAAA,EACnC;AAEA,QAAM;AAAA,IACJF,MAAK,oBAAoB,eAAe;AAAA,IACxC,KAAK,UAAU;AAAA,MACb,eAAe,OAAO;AAAA,MACtB,UAAU,OAAO;AAAA,IACnB,CAAyB;AAAA,IACzB;AAAA,EACF;AAEA,QAAMC,IAAG,gBAAgB;AAAA,IACvB,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AAED,QAAM,OAAO,oBAAoB,cAAc;AACjD;;;AEjGA,SAAS,WAAAE,gBAAe;AAMjB,SAAS,QAAQ,UAA0B;AAChD,SACE,SACG,QAAQC,SAAQ,QAAQ,GAAG,EAAE,EAC7B,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,YAAY,EAAE,EACtB,YAAY,KAAK;AAExB;AAGO,SAAS,qBAAqB,UAAyC;AAC5E,QAAM,QAAQ,CAAC,WAAW,kBAAkB;AAE5C,aAAW,WAAW,UAAU;AAE9B,UAAM,YAAY,KAAK,MAAM,gBAAgB,QAAQ,OAAO,IAAI,IAAI;AAEpE,UAAM;AAAA,MACJ,+BAA+B,SAAS;AAAA,MACxC,GAAG,QAAQ,MAAM;AAAA,IACnB;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,KAAK,IAAI,CAAC;AAAA;AAC5B;;;ALlBA,eAAsB,YACpB,QACA,WACA,SACoB;AACpB,QAAM,MAAM,MAAM,YAAY,QAAQ,OAAO;AAE7C,QAAM,SAAS,MAAM,UAAU,WAAW,GAAG;AAE7C,MAAI,QAAQ;AACV,YAAQ,IAAI,+BAA+B,MAAM,EAAE;AAEnD,WAAO;AAAA,EACT;AAEA,UAAQ,IAAI,8BAA8B,MAAM,EAAE;AAElD,QAAM,qBAAqB,MAAM,sBAAsB;AAEvD,MAAI;AACF,UAAM,YAAY,QAAQ,SAAS,QAAQC,SAAQ,MAAM,CAAC,CAAC;AAE3D,UAAM,gBAAgB,GAAG,SAAS,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAEtD,QAAI;AAEJ,QAAI,QAAQ,SAAS,UAAU;AAC7B,YAAM,cAAc,QAAQ,oBAAoB,OAAO;AAEvD,iBAAW;AAAA,IACb,OAAO;AACL,iBAAW,WAAW,QAAQ,UAAU;AACtC,cAAM;AAAA,UACJ;AAAA,UACAC,MAAK,oBAAoB,GAAG,QAAQ,MAAM,GAAG;AAAA,UAC7C;AAAA,UACA;AAAA,YACE,QAAQ,QAAQ;AAAA,YAChB,SAAS,QAAQ;AAAA,UACnB;AAAA,QACF;AAAA,MACF;AAEA,YAAM,SAAS,qBAAqB,QAAQ,QAAQ;AAEpD,YAAMC,WAAUD,MAAK,oBAAoB,aAAa,GAAG,QAAQ,MAAM;AAEvE,iBAAW;AAAA,IACb;AAEA,UAAM,SAAoB;AAAA,MACxB;AAAA,MACA;AAAA,MACA,OAAO,MAAM,aAAa,kBAAkB;AAAA,IAC9C;AAEA,UAAM,WAAW,WAAW,KAAK,MAAM;AAEvC,YAAQ,IAAI,+BAA+B,aAAa,IAAI,QAAQ,EAAE;AAEtE,WAAO;AAAA,EACT,UAAE;AACA,UAAM,sBAAsB,kBAAkB;AAAA,EAChD;AACF;;;AMzEO,SAAS,QACd,SACA,QACA,SACM;AACN,QAAM,SAAS,GAAG,QAAQ,SAAS,IAAI,OAAO,aAAa;AAE3D,QAAM,iBAAiB,OAAO,MAAM,IAAI,OAAO,QAAQ;AAEvD,MAAI,CAAC,gBAAgB;AACnB,UAAM,IAAI,MAAM,sCAAsC,OAAO,QAAQ,EAAE;AAAA,EACzE;AAEA,UAAQ,SAAS;AAAA,IACf,MAAM;AAAA,IACN,UAAU,GAAG,MAAM,IAAI,OAAO,QAAQ;AAAA,IACtC,QAAQ;AAAA,EACV,CAAC;AAED,aAAW,CAAC,UAAU,IAAI,KAAK,OAAO,OAAO;AAC3C,QAAI,aAAa,OAAO,UAAU;AAChC;AAAA,IACF;AAEA,YAAQ,SAAS;AAAA,MACf,MAAM;AAAA,MACN,UAAU,GAAG,MAAM,IAAI,QAAQ;AAAA,MAC/B,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACF;AAEO,SAAS,yBACd,QACA,SACQ;AACR,QAAM,OAAO,GAAG,QAAQ,SAAS,IAAI,OAAO,aAAa,IAAI,OAAO,QAAQ;AAY5E,SAAO,8BAA8B,KAAK,UAAU,IAAI,CAAC;AAC3D;;;ACxCA,IAAME,kBAAiB;AAEhB,SAAS,UAAU,OAAkC;AAC1D,QAAM,UAAU,eAAe,KAAK;AAEpC,MAAI;AAEJ,QAAM,kBAAkB,oBAAI,IAAoB;AAQhD,QAAM,YAAY,oBAAI,IAAqD;AAE3E,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,OAAO;AAAA,IAEP,SAAS;AAAA,IAET,eAAe,UAAU;AACvB,eAAS;AAAA,IACX;AAAA,IAEA,MAAM,UAAU,QAAQ,UAAU;AAChC,UAAI,CAAC,YAAY,CAAC,cAAc,MAAM,GAAG;AACvC,eAAO;AAAA,MACT;AAEA,YAAM,WAAW,MAAM,mBAAmB,QAAQ,UAAU,IAAI;AAEhE,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,MACT;AAEA,YAAM,KAAK,mBAAmB,QAAQ;AAEtC,sBAAgB,IAAI,IAAI,QAAQ;AAEhC,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,KAAK,IAAI,aAAa;AAC1B,UAAI,CAAC,GAAG,WAAWA,eAAc,GAAG;AAClC,eAAO;AAAA,MACT;AAEA,YAAM,SAAS,gBAAgB,IAAI,EAAE,KAAK,oBAAoB,EAAE;AAEhE,UAAI,SAAS,UAAU,IAAI,MAAM;AAEjC,UAAI,CAAC,QAAQ;AACX,iBAAS,MAAM,YAAY,QAAQ,OAAO,UAAU,OAAO;AAE3D,kBAAU,IAAI,QAAQ,MAAM;AAAA,MAC9B;AAWA,UAAI,CAAC,aAAa,KAAK;AACrB,gBAAQ,MAAM,QAAQ,OAAO;AAAA,MAC/B;AAEA,YAAM,WAAW,yBAAyB,QAAQ,OAAO;AAEzD,aAAO;AAAA,yBACY,QAAQ;AAAA;AAAA,IAE7B;AAAA,EACF;AACF;;;AC3FA,IAAO,gBAAQ;","names":["extname","join","writeFile","mkdir","readFile","rm","join","join","readFile","join","rm","mkdir","extname","extname","extname","join","writeFile","VIRTUAL_PREFIX"]}
|
|
1
|
+
{"version":3,"sources":["../src/options.ts","../src/resolve.ts","../src/hls.ts","../src/encoder.ts","../src/bitrate.ts","../src/cache.ts","../src/fs.ts","../src/playlist.ts","../src/assets.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n segmentDuration: input.segmentDuration ?? 4,\n\n segmentType: input.segmentType ?? \"fmp4\",\n\n outputDir: input.outputDir ?? \"assets/hls\",\n\n preset: input.preset ?? \"medium\",\n\n crf: input.crf ?? 23,\n };\n\n if (input.mode === \"adaptive\") {\n if (!input.variants || input.variants.length === 0) {\n throw new Error(\n \"[vite-plugin-hls] Adaptive mode requires at least one variant.\",\n );\n }\n\n return {\n ...common,\n mode: \"adaptive\",\n variants: input.variants,\n };\n }\n\n return {\n ...common,\n mode: \"single\",\n };\n}\n","import { extname } from \"node:path\";\n\nimport type { PluginContext } from \"rolldown\";\n\nconst VIDEO_EXTENSIONS = new Set([\n \".mp4\",\n \".mov\",\n \".m4v\",\n \".webm\",\n \".mkv\",\n \".avi\",\n]);\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function isVideoSource(source: string): boolean {\n const pathname = source.split(\"?\")[0] ?? source;\n\n return VIDEO_EXTENSIONS.has(extname(pathname).toLowerCase());\n}\n\nexport function virtualIdForSource(source: string): string {\n return `${VIRTUAL_PREFIX}${encodeURIComponent(source)}`;\n}\n\nexport function sourceFromVirtualId(id: string): string {\n return decodeURIComponent(id.slice(VIRTUAL_PREFIX.length));\n}\n\nexport async function resolveVideoSource(\n source: string,\n importer: string,\n context: PluginContext,\n): Promise<string | undefined> {\n if (!isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await context.resolve(source, importer, {\n skipSelf: true,\n });\n\n if (!resolved || resolved.external) {\n return undefined;\n }\n\n /*\n * Queries such as ?url are irrelevant here.\n * FFmpeg needs the physical file.\n */\n return resolved.id.split(\"?\", 1)[0];\n}\n","import { basename, extname, join } from \"node:path\";\nimport { writeFile } from \"node:fs/promises\";\n\nimport {\n encodeVariant,\n createEncodeDirectory,\n removeEncodeDirectory,\n} from \"./encoder\";\nimport { readCache, getCacheKey, writeCache } from \"./cache\";\nimport { collectFiles } from \"./fs\";\nimport { slugify, createMasterPlaylist } from \"./playlist\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport async function generateHls(\n source: string,\n cacheRoot: string,\n options: ResolvedHlsOptions,\n): Promise<CachedHls> {\n const key = await getCacheKey(source, options);\n\n const cached = await readCache(cacheRoot, key);\n\n if (cached) {\n console.log(`[vite-plugin-hls] cache hit ${source}`);\n\n return cached;\n }\n\n console.log(`[vite-plugin-hls] encoding ${source}`);\n\n const temporaryDirectory = await createEncodeDirectory();\n\n try {\n const videoName = slugify(basename(source, extname(source)));\n\n const directoryName = `${videoName}-${key.slice(0, 12)}`;\n\n let manifest: string;\n\n if (options.mode === \"single\") {\n await encodeVariant(source, temporaryDirectory, options);\n\n manifest = \"index.m3u8\";\n } else {\n for (const variant of options.variants) {\n await encodeVariant(\n source,\n join(temporaryDirectory, `${variant.height}p`),\n options,\n {\n height: variant.height,\n bitrate: variant.bitrate,\n },\n );\n }\n\n const master = createMasterPlaylist(options.variants);\n\n await writeFile(join(temporaryDirectory, \"master.m3u8\"), master, \"utf8\");\n\n manifest = \"master.m3u8\";\n }\n\n const result: CachedHls = {\n directoryName,\n manifest,\n files: await collectFiles(temporaryDirectory),\n };\n\n await writeCache(cacheRoot, key, result);\n\n console.log(`[vite-plugin-hls] generated ${directoryName}/${manifest}`);\n\n return result;\n } finally {\n await removeEncodeDirectory(temporaryDirectory);\n }\n}\n","import { access, copyFile, mkdir, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from \"node-av/api\";\nimport { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from \"node-av/constants\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nconst PLAYLIST = \"index.m3u8\";\n\ntype HlsOptions = {\n hls_time: string;\n hls_playlist_type: \"vod\";\n hls_segment_filename: string;\n hls_segment_type?: \"mpegts\" | \"fmp4\";\n hls_fmp4_init_filename?: string;\n};\n\nfunction hlsMuxerOptions(\n outputDirectory: string,\n options: ResolvedHlsOptions,\n): HlsOptions {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const hls: HlsOptions = {\n hls_time: String(options.segmentDuration),\n hls_playlist_type: \"vod\",\n hls_segment_filename: join(outputDirectory, `segment-%05d.${extension}`),\n };\n\n if (options.segmentType === \"fmp4\") {\n hls.hls_segment_type = \"fmp4\";\n\n /*\n * ffmpeg base-joins hls_fmp4_init_filename onto the HLS output dir, so an\n * absolute path is doubled (e.g. on POSIX). Keep it relative and rely on\n * ensureInitSegment() to place it correctly.\n */\n hls.hls_fmp4_init_filename = \"init.mp4\";\n }\n\n return hls;\n}\n\n/*\n * ffmpeg writes the relative init filename into the HLS output directory on\n * POSIX, but on some platforms (e.g. Windows) it can instead land in the\n * process CWD. Make sure it ends up next to the playlist.\n */\nasync function ensureInitSegment(outputDirectory: string): Promise<void> {\n const target = join(outputDirectory, \"init.mp4\");\n\n try {\n await access(target);\n return;\n } catch {\n // fall through\n }\n\n const leaked = join(process.cwd(), \"init.mp4\");\n\n try {\n await access(leaked);\n } catch {\n return;\n }\n\n await copyFile(leaked, target);\n await rm(leaked, { force: true });\n}\n\n/*\n * SINGLE MODE (remux): copy the streams, only repackage into HLS.\n */\nasync function remuxToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n): Promise<void> {\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const routed = new Map<number, number>();\n\n const video = input.video();\n if (video) {\n routed.set(video.index, output.addStream(video));\n }\n\n const audio = input.audio();\n if (audio) {\n routed.set(audio.index, output.addStream(audio));\n }\n\n for await (const packet of input.packets()) {\n if (packet === null) {\n break;\n }\n\n const index = routed.get(packet.streamIndex);\n\n if (index !== undefined) {\n await output.writePacket(packet, index);\n }\n\n packet.free();\n }\n\n await output.close();\n}\n\n/*\n * ADAPTIVE MODE (transcode): re-encode each rendition's video (scaled,\n * libx264) and audio (aac) into its HLS output.\n */\nasync function transcodeToHls(\n source: string,\n playlist: string,\n hls: HlsOptions,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions,\n): Promise<void> {\n if (options.mode !== \"adaptive\") {\n throw new Error(\n \"[vite-plugin-hls] internal: transcode requires adaptive mode\",\n );\n }\n\n await using input = await Demuxer.open(source);\n await using output = await Muxer.open(playlist, {\n format: \"hls\",\n input,\n options: hls,\n });\n\n const tasks: Promise<void>[] = [];\n\n const video = input.video();\n if (video) {\n const decoder = await Decoder.create(video);\n\n const scale = variant.height\n ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p`\n : \"format=yuv420p\";\n\n const filter = FilterAPI.create(scale);\n\n const encoderOptions: Record<string, string | number> = {\n preset: options.preset,\n crf: String(options.crf),\n };\n\n const encoderBase: Record<string, unknown> = {\n options: encoderOptions,\n };\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n encoderOptions.maxrate = bitrate;\n encoderOptions.bufsize = Math.round(bitrate * 1.5);\n encoderBase.bitrate = bitrate;\n }\n\n const encoder = await Encoder.create(\n FF_ENCODER_LIBX264,\n encoderBase as Parameters<typeof Encoder.create>[1],\n );\n\n const index = output.addStream(encoder, { inputStream: video });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(video.index));\n const scaled = filter.frames(frames);\n\n for await (const packet of encoder.packets(scaled)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n filter.close();\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n const audio = input.audio();\n if (audio) {\n const decoder = await Decoder.create(audio);\n\n const encoder = await Encoder.create(FF_ENCODER_AAC, {\n decoder,\n bitrate: 128_000,\n });\n\n const index = output.addStream(encoder, { inputStream: audio });\n\n tasks.push(\n (async () => {\n try {\n const frames = decoder.frames(input.packets(audio.index));\n\n for await (const packet of encoder.packets(frames)) {\n if (packet === null) {\n break;\n }\n await output.writePacket(packet, index);\n packet.free();\n }\n } finally {\n decoder.close();\n encoder.close();\n }\n })(),\n );\n }\n\n await Promise.all(tasks);\n await output.close();\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const hls = hlsMuxerOptions(outputDirectory, options);\n const playlist = join(outputDirectory, PLAYLIST);\n\n /*\n * FFmpeg's HLS muxer does not create the output directory.\n * Ensure it exists so segments/playlist can be written.\n */\n await mkdir(outputDirectory, { recursive: true });\n\n if (options.mode === \"single\") {\n await remuxToHls(source, playlist, hls);\n } else {\n await transcodeToHls(source, playlist, hls, options, variant);\n }\n\n if (options.segmentType === \"fmp4\") {\n await ensureInitSegment(outputDirectory);\n }\n}\n\nexport async function createEncodeDirectory(): Promise<string> {\n return mkdtemp(join(tmpdir(), \"vite-plugin-hls-\"));\n}\n\nexport async function removeEncodeDirectory(directory: string): Promise<void> {\n await rm(directory, {\n recursive: true,\n force: true,\n });\n}\n","/**\n * Converts a human-readable bitrate such as \"1200k\", \"2.5M\" or \"1G\"\n * into the equivalent number of bits per second.\n */\nexport function bitrateToNumber(bitrate: string): number {\n const match = /^([\\d.]+)\\s*([kKmMgG]?)$/.exec(bitrate.trim());\n\n if (!match) {\n throw new Error(`[vite-plugin-hls] Invalid bitrate: ${bitrate}`);\n }\n\n const amount = Number(match[1]);\n\n const suffix = (match[2] ?? \"\").toLowerCase();\n\n const multiplier =\n suffix === \"g\"\n ? 1_000_000_000\n : suffix === \"m\"\n ? 1_000_000\n : suffix === \"k\"\n ? 1_000\n : 1;\n\n return Math.round(amount * multiplier);\n}\n","import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\n\nimport { collectFiles } from \"./fs\";\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\ninterface CacheManifest {\n directoryName: string;\n manifest: string;\n}\n\n/*\n * Bump whenever the encoder's output layout changes (e.g. a bug fix alters\n * which files are produced). Old cache entries are keyed only on source +\n * options and would otherwise be served forever, so the version is folded\n * into the key to invalidate them.\n */\nconst CACHE_VERSION = \"2\";\n\nexport async function getCacheKey(\n source: string,\n options: ResolvedHlsOptions,\n): Promise<string> {\n const sourceData = await readFile(source);\n\n return createHash(\"sha256\")\n .update(CACHE_VERSION)\n .update(sourceData)\n .update(JSON.stringify(options))\n .digest(\"hex\");\n}\n\nexport async function readCache(\n cacheRoot: string,\n key: string,\n): Promise<CachedHls | undefined> {\n const directory = join(cacheRoot, \"hls\", key);\n\n try {\n await stat(directory);\n } catch {\n return undefined;\n }\n\n const metadata = JSON.parse(\n await readFile(join(directory, \"manifest.json\"), \"utf8\"),\n ) as CacheManifest;\n\n const files = await collectFiles(directory);\n\n // manifest.json is internal bookkeeping, never an emitted asset.\n files.delete(\"manifest.json\");\n\n return {\n directoryName: metadata.directoryName,\n\n manifest: metadata.manifest,\n\n files,\n };\n}\n\nexport async function writeCache(\n cacheRoot: string,\n key: string,\n result: CachedHls,\n): Promise<void> {\n const temporaryDirectory = join(cacheRoot, \"hls\", `${key}.tmp`);\n\n const finalDirectory = join(cacheRoot, \"hls\", key);\n\n await rm(temporaryDirectory, {\n recursive: true,\n force: true,\n });\n\n await mkdir(temporaryDirectory, {\n recursive: true,\n });\n\n for (const [filename, data] of result.files) {\n const destination = join(temporaryDirectory, filename);\n\n await mkdir(resolve(destination, \"..\"), {\n recursive: true,\n });\n\n await writeFile(destination, data);\n }\n\n await writeFile(\n join(temporaryDirectory, \"manifest.json\"),\n JSON.stringify({\n directoryName: result.directoryName,\n manifest: result.manifest,\n } satisfies CacheManifest),\n \"utf8\",\n );\n\n await rm(finalDirectory, {\n recursive: true,\n force: true,\n });\n\n await rename(temporaryDirectory, finalDirectory);\n}\n","import { readFile, readdir } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\n\n/**\n * Recursively collects every file under `directory` into a map of\n * directory-relative POSIX paths to their buffer contents.\n */\nexport async function collectFiles(\n directory: string,\n): Promise<Map<string, Buffer>> {\n const result = new Map<string, Buffer>();\n\n async function visit(current: string): Promise<void> {\n for (const entry of await readdir(current, { withFileTypes: true })) {\n const filename = join(current, entry.name);\n\n if (entry.isDirectory()) {\n await visit(filename);\n continue;\n }\n\n result.set(\n relative(directory, filename).replaceAll(\"\\\\\", \"/\"),\n await readFile(filename),\n );\n }\n }\n\n await visit(directory);\n\n return result;\n}\n","import { extname } from \"node:path\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { HlsVariant } from \"./types\";\n\n/** Turns a filename into a filesystem-safe slug used for the output folder. */\nexport function slugify(filename: string): string {\n return (\n filename\n .replace(extname(filename), \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase() || \"video\"\n );\n}\n\n/** Builds the adaptive master playlist pointing at each rendition. */\nexport function createMasterPlaylist(variants: readonly HlsVariant[]): string {\n const lines = [\"#EXTM3U\", \"#EXT-X-VERSION:7\"];\n\n for (const variant of variants) {\n // BANDWIDTH is an estimate derived from the configured bitrate.\n const bandwidth = Math.round(bitrateToNumber(variant.bitrate) * 1.15);\n\n lines.push(\n `#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth}`,\n `${variant.height}p/index.m3u8`,\n );\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n","import type { PluginContext } from \"rolldown\";\n\nimport type { CachedHls, ResolvedHlsOptions } from \"./types\";\n\nexport function emitHls(\n context: PluginContext,\n result: CachedHls,\n options: ResolvedHlsOptions,\n): void {\n const prefix = `${options.outputDir}/${result.directoryName}`;\n\n const manifestSource = result.files.get(result.manifest);\n\n if (!manifestSource) {\n throw new Error(`[vite-plugin-hls] Missing manifest ${result.manifest}`);\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${result.manifest}`,\n source: manifestSource,\n });\n\n for (const [filename, data] of result.files) {\n if (filename === result.manifest) {\n continue;\n }\n\n context.emitFile({\n type: \"asset\",\n fileName: `${prefix}/${filename}`,\n source: data,\n });\n }\n}\n\nexport function publicManifestExpression(\n result: CachedHls,\n options: ResolvedHlsOptions,\n base: string,\n): string {\n const path = `${options.outputDir}/${result.directoryName}/${result.manifest}`;\n\n /*\n * Bake Vite's resolved base straight into the module. Vite does not always\n * statically replace import.meta.env.BASE_URL (e.g. in SSR bundles), where\n * import.meta.env is undefined at runtime and crashes. Emitting the base as\n * a plain string is equivalent and works for client and SSR alike.\n *\n * Examples (base -> export):\n *\n * \"/\" -> \"/assets/hls/...\"\n * \"/notes/\" -> \"/notes/assets/hls/...\"\n */\n return JSON.stringify(`${base}${path}`);\n}\n","import type { Plugin, ResolvedConfig } from \"vite\";\n\nimport { resolveOptions } from \"./options\";\nimport {\n isVideoSource,\n resolveVideoSource,\n sourceFromVirtualId,\n virtualIdForSource,\n} from \"./resolve\";\nimport { generateHls } from \"./hls\";\nimport { emitHls, publicManifestExpression } from \"./assets\";\nimport type { HlsPluginOptions } from \"./types\";\n\nconst VIRTUAL_PREFIX = \"\\0vite-plugin-hls:\";\n\nexport function hlsVideos(input?: HlsPluginOptions): Plugin {\n const options = resolveOptions(input);\n\n let config: ResolvedConfig;\n\n const resolvedSources = new Map<string, string>();\n\n /*\n * This only caches inside the current Vite build.\n *\n * The persistent cache in cache.ts handles the\n * client/server build boundary.\n */\n const generated = new Map<string, Awaited<ReturnType<typeof generateHls>>>();\n\n return {\n name: \"vite-plugin-hls\",\n\n apply: \"build\",\n\n enforce: \"pre\",\n\n configResolved(resolved) {\n config = resolved;\n },\n\n async resolveId(source, importer) {\n if (!importer || !isVideoSource(source)) {\n return undefined;\n }\n\n const resolved = await resolveVideoSource(source, importer, this);\n\n if (!resolved) {\n return undefined;\n }\n\n const id = virtualIdForSource(resolved);\n\n resolvedSources.set(id, resolved);\n\n return id;\n },\n\n async load(id, loadOptions) {\n if (!id.startsWith(VIRTUAL_PREFIX)) {\n return undefined;\n }\n\n const source = resolvedSources.get(id) ?? sourceFromVirtualId(id);\n\n let result = generated.get(source);\n\n if (!result) {\n result = await generateHls(source, config.cacheDir, options);\n\n generated.set(source, result);\n }\n\n /*\n * Important:\n *\n * The SSR build must NOT use Vite's emitted-file\n * URL because that resolves to a file:// URL.\n *\n * We only emit the actual files during the client\n * build. SSR receives the public URL directly.\n */\n if (!loadOptions?.ssr) {\n emitHls(this, result, options);\n }\n\n const manifest = publicManifestExpression(result, options, config.base);\n\n return `\n export default ${manifest};\n `;\n },\n };\n}\n","import { hlsVideos } from \"./plugin\";\n\nexport { hlsVideos };\nexport default hlsVideos;\n\nexport type { HlsPluginOptions, HlsVariant } from \"./types\";\n"],"mappings":"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","names":["extname","join","writeFile","mkdir","readFile","rm","join","join","readFile","join","rm","mkdir","extname","extname","extname","join","writeFile","VIRTUAL_PREFIX"]}
|