@karsten_zhou/vite-plugin-hls 1.0.1 → 1.1.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/README.md CHANGED
@@ -22,15 +22,9 @@ npm install -D @karsten_zhou/vite-plugin-hls
22
22
  ```
23
23
 
24
24
  [`vite`](https://vitejs.dev) is a **peer dependency**; install it if it is not
25
- already present in your project. FFmpeg is bundled automatically via
26
- `ffmpeg-static` (falling back to a system `ffmpeg` on `PATH` if unavailable).
27
-
28
- ## Requirements
29
-
30
- - Node.js `>=24.3`
31
- - Vite `^8` (peer)
32
- - The build runs `ffmpeg`; `ffmpeg-static` downloads a platform binary on
33
- install.
25
+ already present in your project. Encoding is powered by
26
+ [`node-av`](https://github.com/seydx/node-av), which downloads and manages a
27
+ platform-specific native FFmpeg build on install.
34
28
 
35
29
  ## Usage
36
30
 
@@ -75,16 +69,15 @@ The plugin also exports `hlsVideos` as the default export, so
75
69
 
76
70
  ## Options
77
71
 
78
- | Option | Type | Default | Description |
79
- | ----------------- | ------------------------ | --------------- | ----------------------------------------------------------------- |
80
- | `mode` | `"single" \| "adaptive"` | `"single"` | `"single"` remuxes the source; `"adaptive"` encodes renditions. |
81
- | `variants` | `HlsVariant[]` | — | Required in `adaptive` mode: `{ height, bitrate }` per rendition. |
82
- | `ffmpegPath` | `string` | `ffmpeg-static` | Explicit ffmpeg executable. |
83
- | `segmentDuration` | `number` | `4` | HLS segment duration in seconds. |
84
- | `segmentType` | `"fmp4" \| "mpegts"` | `"fmp4"` | HLS segment container format. |
85
- | `outputDir` | `string` | `"assets/hls"` | Directory (inside the Vite output) where HLS assets are written. |
86
- | `preset` | `string` | `"medium"` | FFmpeg `-preset` used in adaptive mode. |
87
- | `crf` | `number` | `23` | H.264 CRF used in adaptive mode. |
72
+ | Option | Type | Default | Description |
73
+ | ----------------- | ------------------------ | -------------- | ----------------------------------------------------------------- |
74
+ | `mode` | `"single" \| "adaptive"` | `"single"` | `"single"` remuxes the source; `"adaptive"` encodes renditions. |
75
+ | `variants` | `HlsVariant[]` | — | Required in `adaptive` mode: `{ height, bitrate }` per rendition. |
76
+ | `segmentDuration` | `number` | `4` | HLS segment duration in seconds. |
77
+ | `segmentType` | `"fmp4" \| "mpegts"` | `"fmp4"` | HLS segment container format. |
78
+ | `outputDir` | `string` | `"assets/hls"` | Directory (inside the Vite output) where HLS assets are written. |
79
+ | `preset` | `string` | `"medium"` | FFmpeg `-preset` used in adaptive mode. |
80
+ | `crf` | `number` | `23` | H.264 CRF used in adaptive mode. |
88
81
 
89
82
  ### Types
90
83
 
package/dist/index.cjs CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
8
10
  var __export = (target, all) => {
9
11
  for (var name in all)
10
12
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -17,15 +19,48 @@ var __copyProps = (to, from, except, desc) => {
17
19
  }
18
20
  return to;
19
21
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
22
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
+ var __using = (stack, value, async) => {
24
+ if (value != null) {
25
+ if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
26
+ var dispose, inner;
27
+ if (async) dispose = value[__knownSymbol("asyncDispose")];
28
+ if (dispose === void 0) {
29
+ dispose = value[__knownSymbol("dispose")];
30
+ if (async) inner = dispose;
31
+ }
32
+ if (typeof dispose !== "function") __typeError("Object not disposable");
33
+ if (inner) dispose = function() {
34
+ try {
35
+ inner.call(this);
36
+ } catch (e) {
37
+ return Promise.reject(e);
38
+ }
39
+ };
40
+ stack.push([async, dispose, value]);
41
+ } else if (async) {
42
+ stack.push([async]);
43
+ }
44
+ return value;
45
+ };
46
+ var __callDispose = (stack, error, hasError) => {
47
+ var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
48
+ return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
49
+ };
50
+ var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
51
+ var next = (it) => {
52
+ while (it = stack.pop()) {
53
+ try {
54
+ var result = it[1] && it[1].call(it[2]);
55
+ if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
56
+ } catch (e) {
57
+ fail(e);
58
+ }
59
+ }
60
+ if (hasError) throw error;
61
+ };
62
+ return next();
63
+ };
29
64
 
30
65
  // src/index.ts
31
66
  var index_exports = {};
@@ -36,10 +71,8 @@ __export(index_exports, {
36
71
  module.exports = __toCommonJS(index_exports);
37
72
 
38
73
  // src/options.ts
39
- var import_ffmpeg_static = __toESM(require("ffmpeg-static"), 1);
40
74
  function resolveOptions(input = {}) {
41
75
  const common = {
42
- ffmpegPath: input.ffmpegPath ?? import_ffmpeg_static.default ?? "ffmpeg",
43
76
  segmentDuration: input.segmentDuration ?? 4,
44
77
  segmentType: input.segmentType ?? "fmp4",
45
78
  outputDir: input.outputDir ?? "assets/hls",
@@ -106,7 +139,8 @@ var import_promises4 = require("fs/promises");
106
139
  var import_promises = require("fs/promises");
107
140
  var import_node_path2 = require("path");
108
141
  var import_node_os = require("os");
109
- var import_node_child_process = require("child_process");
142
+ var import_api = require("node-av/api");
143
+ var import_constants = require("node-av/constants");
110
144
 
111
145
  // src/bitrate.ts
112
146
  function bitrateToNumber(bitrate) {
@@ -121,121 +155,158 @@ function bitrateToNumber(bitrate) {
121
155
  }
122
156
 
123
157
  // src/encoder.ts
124
- function runFfmpeg(executable, args) {
125
- return new Promise((resolvePromise, reject) => {
126
- const child = (0, import_node_child_process.spawn)(executable, args, {
127
- stdio: ["ignore", "pipe", "pipe"]
128
- });
129
- let stderr = "";
130
- child.stderr.on("data", (data) => {
131
- stderr += data.toString();
132
- });
133
- child.on("error", (error) => {
134
- if ("code" in error && error.code === "ENOENT") {
135
- reject(
136
- new Error(
137
- [
138
- "[vite-plugin-hls] ffmpeg was not found.",
139
- `Executable: ${executable}`,
140
- "Set ffmpegPath or install ffmpeg."
141
- ].join("\n")
142
- )
143
- );
144
- return;
158
+ var PLAYLIST = "index.m3u8";
159
+ function hlsMuxerOptions(outputDirectory, options) {
160
+ const extension = options.segmentType === "fmp4" ? "m4s" : "ts";
161
+ const hls = {
162
+ hls_time: String(options.segmentDuration),
163
+ hls_playlist_type: "vod",
164
+ hls_segment_filename: (0, import_node_path2.join)(outputDirectory, `segment-%05d.${extension}`)
165
+ };
166
+ if (options.segmentType === "fmp4") {
167
+ hls.hls_segment_type = "fmp4";
168
+ hls.hls_fmp4_init_filename = "init.mp4";
169
+ }
170
+ return hls;
171
+ }
172
+ async function remuxToHls(source, playlist, hls) {
173
+ var _stack = [];
174
+ try {
175
+ const input = __using(_stack, await import_api.Demuxer.open(source), true);
176
+ const output = __using(_stack, await import_api.Muxer.open(playlist, {
177
+ format: "hls",
178
+ input,
179
+ options: hls
180
+ }), true);
181
+ const routed = /* @__PURE__ */ new Map();
182
+ const video = input.video();
183
+ if (video) {
184
+ routed.set(video.index, output.addStream(video));
185
+ }
186
+ const audio = input.audio();
187
+ if (audio) {
188
+ routed.set(audio.index, output.addStream(audio));
189
+ }
190
+ for await (const packet of input.packets()) {
191
+ if (packet === null) {
192
+ break;
145
193
  }
146
- reject(error);
147
- });
148
- child.on("close", (code) => {
149
- if (code === 0) {
150
- resolvePromise();
151
- return;
194
+ const index = routed.get(packet.streamIndex);
195
+ if (index !== void 0) {
196
+ await output.writePacket(packet, index);
152
197
  }
153
- reject(
154
- new Error(
155
- [
156
- `[vite-plugin-hls] ffmpeg failed with exit code ${code ?? "unknown"}.`,
157
- stderr.trim()
158
- ].filter(Boolean).join("\n")
159
- )
160
- );
161
- });
162
- });
198
+ packet.free();
199
+ }
200
+ await output.close();
201
+ } catch (_) {
202
+ var _error = _, _hasError = true;
203
+ } finally {
204
+ var _promise = __callDispose(_stack, _error, _hasError);
205
+ _promise && await _promise;
206
+ }
163
207
  }
164
- async function encodeVariant(source, outputDirectory, options, variant = {}) {
165
- const extension = options.segmentType === "fmp4" ? "m4s" : "ts";
166
- const playlist = (0, import_node_path2.join)(outputDirectory, "index.m3u8");
167
- const args = [
168
- "-y",
169
- "-i",
170
- source,
171
- "-map",
172
- "0:v:0",
173
- "-map",
174
- "0:a:0?"
175
- ];
176
- if (options.mode === "single") {
177
- args.push(
178
- "-c:v",
179
- "copy",
180
- "-c:a",
181
- "copy"
182
- );
183
- } else {
184
- args.push(
185
- "-c:v",
186
- "libx264",
187
- "-preset",
188
- options.preset,
189
- "-crf",
190
- String(options.crf),
191
- "-pix_fmt",
192
- "yuv420p",
193
- "-c:a",
194
- "aac",
195
- "-b:a",
196
- "128k"
197
- );
198
- if (variant.height !== void 0) {
199
- args.push(
200
- "-vf",
201
- `scale=-2:${variant.height}:force_original_aspect_ratio=decrease`
208
+ async function transcodeToHls(source, playlist, hls, options, variant) {
209
+ var _stack = [];
210
+ try {
211
+ if (options.mode !== "adaptive") {
212
+ throw new Error(
213
+ "[vite-plugin-hls] internal: transcode requires adaptive mode"
214
+ );
215
+ }
216
+ const input = __using(_stack, await import_api.Demuxer.open(source), true);
217
+ const output = __using(_stack, await import_api.Muxer.open(playlist, {
218
+ format: "hls",
219
+ input,
220
+ options: hls
221
+ }), true);
222
+ const tasks = [];
223
+ const video = input.video();
224
+ if (video) {
225
+ const decoder = await import_api.Decoder.create(video);
226
+ const scale = variant.height ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p` : "format=yuv420p";
227
+ const filter = import_api.FilterAPI.create(scale);
228
+ const encoderOptions = {
229
+ preset: options.preset,
230
+ crf: String(options.crf)
231
+ };
232
+ const encoderBase = {
233
+ options: encoderOptions
234
+ };
235
+ if (variant.bitrate !== void 0) {
236
+ const bitrate = bitrateToNumber(variant.bitrate);
237
+ encoderOptions.maxrate = bitrate;
238
+ encoderOptions.bufsize = Math.round(bitrate * 1.5);
239
+ encoderBase.bitrate = bitrate;
240
+ }
241
+ const encoder = await import_api.Encoder.create(
242
+ import_constants.FF_ENCODER_LIBX264,
243
+ encoderBase
244
+ );
245
+ const index = output.addStream(encoder, { inputStream: video });
246
+ tasks.push(
247
+ (async () => {
248
+ try {
249
+ const frames = decoder.frames(input.packets(video.index));
250
+ const scaled = filter.frames(frames);
251
+ for await (const packet of encoder.packets(scaled)) {
252
+ if (packet === null) {
253
+ break;
254
+ }
255
+ await output.writePacket(packet, index);
256
+ packet.free();
257
+ }
258
+ } finally {
259
+ filter.close();
260
+ decoder.close();
261
+ encoder.close();
262
+ }
263
+ })()
202
264
  );
203
265
  }
204
- if (variant.bitrate !== void 0) {
205
- const bitrate = bitrateToNumber(variant.bitrate);
206
- args.push(
207
- "-b:v",
208
- variant.bitrate,
209
- "-maxrate",
210
- variant.bitrate,
211
- "-bufsize",
212
- String(Math.round(bitrate * 1.5))
266
+ const audio = input.audio();
267
+ if (audio) {
268
+ const decoder = await import_api.Decoder.create(audio);
269
+ const encoder = await import_api.Encoder.create(import_constants.FF_ENCODER_AAC, {
270
+ decoder,
271
+ bitrate: 128e3
272
+ });
273
+ const index = output.addStream(encoder, { inputStream: audio });
274
+ tasks.push(
275
+ (async () => {
276
+ try {
277
+ const frames = decoder.frames(input.packets(audio.index));
278
+ for await (const packet of encoder.packets(frames)) {
279
+ if (packet === null) {
280
+ break;
281
+ }
282
+ await output.writePacket(packet, index);
283
+ packet.free();
284
+ }
285
+ } finally {
286
+ decoder.close();
287
+ encoder.close();
288
+ }
289
+ })()
213
290
  );
214
291
  }
292
+ await Promise.all(tasks);
293
+ await output.close();
294
+ } catch (_) {
295
+ var _error = _, _hasError = true;
296
+ } finally {
297
+ var _promise = __callDispose(_stack, _error, _hasError);
298
+ _promise && await _promise;
215
299
  }
216
- args.push(
217
- "-f",
218
- "hls",
219
- "-hls_time",
220
- String(options.segmentDuration),
221
- "-hls_playlist_type",
222
- "vod",
223
- "-hls_segment_filename",
224
- (0, import_node_path2.join)(outputDirectory, `segment-%05d.${extension}`)
225
- );
226
- if (options.segmentType === "fmp4") {
227
- args.push(
228
- "-hls_segment_type",
229
- "fmp4",
230
- "-hls_fmp4_init_filename",
231
- "init.mp4"
232
- );
300
+ }
301
+ async function encodeVariant(source, outputDirectory, options, variant = {}) {
302
+ const hls = hlsMuxerOptions(outputDirectory, options);
303
+ const playlist = (0, import_node_path2.join)(outputDirectory, PLAYLIST);
304
+ await (0, import_promises.mkdir)(outputDirectory, { recursive: true });
305
+ if (options.mode === "single") {
306
+ await remuxToHls(source, playlist, hls);
307
+ } else {
308
+ await transcodeToHls(source, playlist, hls, options, variant);
233
309
  }
234
- args.push(playlist);
235
- await (0, import_promises.mkdir)(outputDirectory, {
236
- recursive: true
237
- });
238
- await runFfmpeg(options.ffmpegPath, args);
239
310
  }
240
311
  async function createEncodeDirectory() {
241
312
  return (0, import_promises.mkdtemp)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), "vite-plugin-hls-"));
@@ -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 ffmpegStatic from \"ffmpeg-static\";\n\nimport type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n ffmpegPath: input.ffmpegPath ?? ffmpegStatic ?? \"ffmpeg\",\n\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\";\nimport { spawn } from \"node:child_process\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nfunction runFfmpeg(executable: string, args: string[]): Promise<void> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(executable, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stderr = \"\";\n\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on(\"error\", (error) => {\n if (\"code\" in error && error.code === \"ENOENT\") {\n reject(\n new Error(\n [\n \"[vite-plugin-hls] ffmpeg was not found.\",\n `Executable: ${executable}`,\n \"Set ffmpegPath or install ffmpeg.\",\n ].join(\"\\n\"),\n ),\n );\n return;\n }\n\n reject(error);\n });\n\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolvePromise();\n return;\n }\n\n reject(\n new Error(\n [\n `[vite-plugin-hls] ffmpeg failed with exit code ${code ?? \"unknown\"}.`,\n stderr.trim(),\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n ),\n );\n });\n });\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const playlist = join(outputDirectory, \"index.m3u8\");\n\n const args: string[] = [\n \"-y\",\n \"-i\",\n source,\n\n \"-map\",\n \"0:v:0\",\n \"-map\",\n \"0:a:0?\",\n ];\n\n /*\n * SINGLE MODE:\n *\n * Do not encode the streams again.\n * FFmpeg only remuxes/segments them into HLS.\n */\n if (options.mode === \"single\") {\n args.push(\n \"-c:v\",\n \"copy\",\n\n \"-c:a\",\n \"copy\",\n );\n } else {\n /*\n * ADAPTIVE MODE:\n *\n * Each rendition needs to be encoded because\n * resolution and bitrate are changed.\n */\n args.push(\n \"-c:v\",\n \"libx264\",\n\n \"-preset\",\n options.preset,\n\n \"-crf\",\n String(options.crf),\n\n \"-pix_fmt\",\n \"yuv420p\",\n\n \"-c:a\",\n \"aac\",\n\n \"-b:a\",\n \"128k\",\n );\n\n if (variant.height !== undefined) {\n args.push(\n \"-vf\",\n `scale=-2:${variant.height}:force_original_aspect_ratio=decrease`,\n );\n }\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n args.push(\n \"-b:v\",\n variant.bitrate,\n\n \"-maxrate\",\n variant.bitrate,\n\n \"-bufsize\",\n String(Math.round(bitrate * 1.5)),\n );\n }\n }\n\n args.push(\n \"-f\",\n \"hls\",\n\n \"-hls_time\",\n String(options.segmentDuration),\n\n \"-hls_playlist_type\",\n \"vod\",\n\n \"-hls_segment_filename\",\n join(outputDirectory, `segment-%05d.${extension}`),\n );\n\n if (options.segmentType === \"fmp4\") {\n args.push(\n \"-hls_segment_type\",\n \"fmp4\",\n\n \"-hls_fmp4_init_filename\",\n \"init.mp4\",\n );\n }\n\n args.push(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, {\n recursive: true,\n });\n\n await runFfmpeg(options.ffmpegPath, args);\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":["ffmpegStatic","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 { 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"]}
package/dist/index.d.cts CHANGED
@@ -5,12 +5,6 @@ interface HlsVariant {
5
5
  bitrate: string;
6
6
  }
7
7
  interface HlsCommonOptions {
8
- /**
9
- * Explicit ffmpeg executable.
10
- *
11
- * Defaults to ffmpeg-static, then falls back to "ffmpeg".
12
- */
13
- ffmpegPath?: string;
14
8
  /**
15
9
  * HLS segment duration in seconds.
16
10
  *
package/dist/index.d.ts CHANGED
@@ -5,12 +5,6 @@ interface HlsVariant {
5
5
  bitrate: string;
6
6
  }
7
7
  interface HlsCommonOptions {
8
- /**
9
- * Explicit ffmpeg executable.
10
- *
11
- * Defaults to ffmpeg-static, then falls back to "ffmpeg".
12
- */
13
- ffmpegPath?: string;
14
8
  /**
15
9
  * HLS segment duration in seconds.
16
10
  *
package/dist/index.js CHANGED
@@ -1,8 +1,52 @@
1
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __using = (stack, value, async) => {
6
+ if (value != null) {
7
+ if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
8
+ var dispose, inner;
9
+ if (async) dispose = value[__knownSymbol("asyncDispose")];
10
+ if (dispose === void 0) {
11
+ dispose = value[__knownSymbol("dispose")];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") __typeError("Object not disposable");
15
+ if (inner) dispose = function() {
16
+ try {
17
+ inner.call(this);
18
+ } catch (e) {
19
+ return Promise.reject(e);
20
+ }
21
+ };
22
+ stack.push([async, dispose, value]);
23
+ } else if (async) {
24
+ stack.push([async]);
25
+ }
26
+ return value;
27
+ };
28
+ var __callDispose = (stack, error, hasError) => {
29
+ var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
30
+ return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
31
+ };
32
+ var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
33
+ var next = (it) => {
34
+ while (it = stack.pop()) {
35
+ try {
36
+ var result = it[1] && it[1].call(it[2]);
37
+ if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
38
+ } catch (e) {
39
+ fail(e);
40
+ }
41
+ }
42
+ if (hasError) throw error;
43
+ };
44
+ return next();
45
+ };
46
+
1
47
  // src/options.ts
2
- import ffmpegStatic from "ffmpeg-static";
3
48
  function resolveOptions(input = {}) {
4
49
  const common = {
5
- ffmpegPath: input.ffmpegPath ?? ffmpegStatic ?? "ffmpeg",
6
50
  segmentDuration: input.segmentDuration ?? 4,
7
51
  segmentType: input.segmentType ?? "fmp4",
8
52
  outputDir: input.outputDir ?? "assets/hls",
@@ -69,7 +113,8 @@ import { writeFile as writeFile2 } from "fs/promises";
69
113
  import { mkdir, mkdtemp, rm } from "fs/promises";
70
114
  import { join } from "path";
71
115
  import { tmpdir } from "os";
72
- import { spawn } from "child_process";
116
+ import { Decoder, Demuxer, Encoder, FilterAPI, Muxer } from "node-av/api";
117
+ import { FF_ENCODER_AAC, FF_ENCODER_LIBX264 } from "node-av/constants";
73
118
 
74
119
  // src/bitrate.ts
75
120
  function bitrateToNumber(bitrate) {
@@ -84,121 +129,158 @@ function bitrateToNumber(bitrate) {
84
129
  }
85
130
 
86
131
  // src/encoder.ts
87
- function runFfmpeg(executable, args) {
88
- return new Promise((resolvePromise, reject) => {
89
- const child = spawn(executable, args, {
90
- stdio: ["ignore", "pipe", "pipe"]
91
- });
92
- let stderr = "";
93
- child.stderr.on("data", (data) => {
94
- stderr += data.toString();
95
- });
96
- child.on("error", (error) => {
97
- if ("code" in error && error.code === "ENOENT") {
98
- reject(
99
- new Error(
100
- [
101
- "[vite-plugin-hls] ffmpeg was not found.",
102
- `Executable: ${executable}`,
103
- "Set ffmpegPath or install ffmpeg."
104
- ].join("\n")
105
- )
106
- );
107
- return;
132
+ var PLAYLIST = "index.m3u8";
133
+ function hlsMuxerOptions(outputDirectory, options) {
134
+ const extension = options.segmentType === "fmp4" ? "m4s" : "ts";
135
+ const hls = {
136
+ hls_time: String(options.segmentDuration),
137
+ hls_playlist_type: "vod",
138
+ hls_segment_filename: join(outputDirectory, `segment-%05d.${extension}`)
139
+ };
140
+ if (options.segmentType === "fmp4") {
141
+ hls.hls_segment_type = "fmp4";
142
+ hls.hls_fmp4_init_filename = "init.mp4";
143
+ }
144
+ return hls;
145
+ }
146
+ async function remuxToHls(source, playlist, hls) {
147
+ var _stack = [];
148
+ try {
149
+ const input = __using(_stack, await Demuxer.open(source), true);
150
+ const output = __using(_stack, await Muxer.open(playlist, {
151
+ format: "hls",
152
+ input,
153
+ options: hls
154
+ }), true);
155
+ const routed = /* @__PURE__ */ new Map();
156
+ const video = input.video();
157
+ if (video) {
158
+ routed.set(video.index, output.addStream(video));
159
+ }
160
+ const audio = input.audio();
161
+ if (audio) {
162
+ routed.set(audio.index, output.addStream(audio));
163
+ }
164
+ for await (const packet of input.packets()) {
165
+ if (packet === null) {
166
+ break;
108
167
  }
109
- reject(error);
110
- });
111
- child.on("close", (code) => {
112
- if (code === 0) {
113
- resolvePromise();
114
- return;
168
+ const index = routed.get(packet.streamIndex);
169
+ if (index !== void 0) {
170
+ await output.writePacket(packet, index);
115
171
  }
116
- reject(
117
- new Error(
118
- [
119
- `[vite-plugin-hls] ffmpeg failed with exit code ${code ?? "unknown"}.`,
120
- stderr.trim()
121
- ].filter(Boolean).join("\n")
122
- )
123
- );
124
- });
125
- });
172
+ packet.free();
173
+ }
174
+ await output.close();
175
+ } catch (_) {
176
+ var _error = _, _hasError = true;
177
+ } finally {
178
+ var _promise = __callDispose(_stack, _error, _hasError);
179
+ _promise && await _promise;
180
+ }
126
181
  }
127
- async function encodeVariant(source, outputDirectory, options, variant = {}) {
128
- const extension = options.segmentType === "fmp4" ? "m4s" : "ts";
129
- const playlist = join(outputDirectory, "index.m3u8");
130
- const args = [
131
- "-y",
132
- "-i",
133
- source,
134
- "-map",
135
- "0:v:0",
136
- "-map",
137
- "0:a:0?"
138
- ];
139
- if (options.mode === "single") {
140
- args.push(
141
- "-c:v",
142
- "copy",
143
- "-c:a",
144
- "copy"
145
- );
146
- } else {
147
- args.push(
148
- "-c:v",
149
- "libx264",
150
- "-preset",
151
- options.preset,
152
- "-crf",
153
- String(options.crf),
154
- "-pix_fmt",
155
- "yuv420p",
156
- "-c:a",
157
- "aac",
158
- "-b:a",
159
- "128k"
160
- );
161
- if (variant.height !== void 0) {
162
- args.push(
163
- "-vf",
164
- `scale=-2:${variant.height}:force_original_aspect_ratio=decrease`
182
+ async function transcodeToHls(source, playlist, hls, options, variant) {
183
+ var _stack = [];
184
+ try {
185
+ if (options.mode !== "adaptive") {
186
+ throw new Error(
187
+ "[vite-plugin-hls] internal: transcode requires adaptive mode"
165
188
  );
166
189
  }
167
- if (variant.bitrate !== void 0) {
168
- const bitrate = bitrateToNumber(variant.bitrate);
169
- args.push(
170
- "-b:v",
171
- variant.bitrate,
172
- "-maxrate",
173
- variant.bitrate,
174
- "-bufsize",
175
- String(Math.round(bitrate * 1.5))
190
+ const input = __using(_stack, await Demuxer.open(source), true);
191
+ const output = __using(_stack, await Muxer.open(playlist, {
192
+ format: "hls",
193
+ input,
194
+ options: hls
195
+ }), true);
196
+ const tasks = [];
197
+ const video = input.video();
198
+ if (video) {
199
+ const decoder = await Decoder.create(video);
200
+ const scale = variant.height ? `scale=-2:${variant.height}:force_original_aspect_ratio=decrease,format=yuv420p` : "format=yuv420p";
201
+ const filter = FilterAPI.create(scale);
202
+ const encoderOptions = {
203
+ preset: options.preset,
204
+ crf: String(options.crf)
205
+ };
206
+ const encoderBase = {
207
+ options: encoderOptions
208
+ };
209
+ if (variant.bitrate !== void 0) {
210
+ const bitrate = bitrateToNumber(variant.bitrate);
211
+ encoderOptions.maxrate = bitrate;
212
+ encoderOptions.bufsize = Math.round(bitrate * 1.5);
213
+ encoderBase.bitrate = bitrate;
214
+ }
215
+ const encoder = await Encoder.create(
216
+ FF_ENCODER_LIBX264,
217
+ encoderBase
218
+ );
219
+ const index = output.addStream(encoder, { inputStream: video });
220
+ tasks.push(
221
+ (async () => {
222
+ try {
223
+ const frames = decoder.frames(input.packets(video.index));
224
+ const scaled = filter.frames(frames);
225
+ for await (const packet of encoder.packets(scaled)) {
226
+ if (packet === null) {
227
+ break;
228
+ }
229
+ await output.writePacket(packet, index);
230
+ packet.free();
231
+ }
232
+ } finally {
233
+ filter.close();
234
+ decoder.close();
235
+ encoder.close();
236
+ }
237
+ })()
176
238
  );
177
239
  }
240
+ const audio = input.audio();
241
+ if (audio) {
242
+ const decoder = await Decoder.create(audio);
243
+ const encoder = await Encoder.create(FF_ENCODER_AAC, {
244
+ decoder,
245
+ bitrate: 128e3
246
+ });
247
+ const index = output.addStream(encoder, { inputStream: audio });
248
+ tasks.push(
249
+ (async () => {
250
+ try {
251
+ const frames = decoder.frames(input.packets(audio.index));
252
+ for await (const packet of encoder.packets(frames)) {
253
+ if (packet === null) {
254
+ break;
255
+ }
256
+ await output.writePacket(packet, index);
257
+ packet.free();
258
+ }
259
+ } finally {
260
+ decoder.close();
261
+ encoder.close();
262
+ }
263
+ })()
264
+ );
265
+ }
266
+ await Promise.all(tasks);
267
+ await output.close();
268
+ } catch (_) {
269
+ var _error = _, _hasError = true;
270
+ } finally {
271
+ var _promise = __callDispose(_stack, _error, _hasError);
272
+ _promise && await _promise;
178
273
  }
179
- args.push(
180
- "-f",
181
- "hls",
182
- "-hls_time",
183
- String(options.segmentDuration),
184
- "-hls_playlist_type",
185
- "vod",
186
- "-hls_segment_filename",
187
- join(outputDirectory, `segment-%05d.${extension}`)
188
- );
189
- if (options.segmentType === "fmp4") {
190
- args.push(
191
- "-hls_segment_type",
192
- "fmp4",
193
- "-hls_fmp4_init_filename",
194
- "init.mp4"
195
- );
274
+ }
275
+ async function encodeVariant(source, outputDirectory, options, variant = {}) {
276
+ const hls = hlsMuxerOptions(outputDirectory, options);
277
+ const playlist = join(outputDirectory, PLAYLIST);
278
+ await mkdir(outputDirectory, { recursive: true });
279
+ if (options.mode === "single") {
280
+ await remuxToHls(source, playlist, hls);
281
+ } else {
282
+ await transcodeToHls(source, playlist, hls, options, variant);
196
283
  }
197
- args.push(playlist);
198
- await mkdir(outputDirectory, {
199
- recursive: true
200
- });
201
- await runFfmpeg(options.ffmpegPath, args);
202
284
  }
203
285
  async function createEncodeDirectory() {
204
286
  return mkdtemp(join(tmpdir(), "vite-plugin-hls-"));
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 ffmpegStatic from \"ffmpeg-static\";\n\nimport type { HlsPluginOptions, ResolvedHlsOptions } from \"./types\";\n\nexport function resolveOptions(\n input: HlsPluginOptions = {},\n): ResolvedHlsOptions {\n const common = {\n ffmpegPath: input.ffmpegPath ?? ffmpegStatic ?? \"ffmpeg\",\n\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\";\nimport { spawn } from \"node:child_process\";\n\nimport { bitrateToNumber } from \"./bitrate\";\nimport type { ResolvedHlsOptions } from \"./types\";\n\ninterface EncodeVariantOptions {\n height?: number;\n bitrate?: string;\n}\n\nfunction runFfmpeg(executable: string, args: string[]): Promise<void> {\n return new Promise((resolvePromise, reject) => {\n const child = spawn(executable, args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stderr = \"\";\n\n child.stderr.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n\n child.on(\"error\", (error) => {\n if (\"code\" in error && error.code === \"ENOENT\") {\n reject(\n new Error(\n [\n \"[vite-plugin-hls] ffmpeg was not found.\",\n `Executable: ${executable}`,\n \"Set ffmpegPath or install ffmpeg.\",\n ].join(\"\\n\"),\n ),\n );\n return;\n }\n\n reject(error);\n });\n\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolvePromise();\n return;\n }\n\n reject(\n new Error(\n [\n `[vite-plugin-hls] ffmpeg failed with exit code ${code ?? \"unknown\"}.`,\n stderr.trim(),\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n ),\n );\n });\n });\n}\n\nexport async function encodeVariant(\n source: string,\n outputDirectory: string,\n options: ResolvedHlsOptions,\n variant: EncodeVariantOptions = {},\n): Promise<void> {\n const extension = options.segmentType === \"fmp4\" ? \"m4s\" : \"ts\";\n\n const playlist = join(outputDirectory, \"index.m3u8\");\n\n const args: string[] = [\n \"-y\",\n \"-i\",\n source,\n\n \"-map\",\n \"0:v:0\",\n \"-map\",\n \"0:a:0?\",\n ];\n\n /*\n * SINGLE MODE:\n *\n * Do not encode the streams again.\n * FFmpeg only remuxes/segments them into HLS.\n */\n if (options.mode === \"single\") {\n args.push(\n \"-c:v\",\n \"copy\",\n\n \"-c:a\",\n \"copy\",\n );\n } else {\n /*\n * ADAPTIVE MODE:\n *\n * Each rendition needs to be encoded because\n * resolution and bitrate are changed.\n */\n args.push(\n \"-c:v\",\n \"libx264\",\n\n \"-preset\",\n options.preset,\n\n \"-crf\",\n String(options.crf),\n\n \"-pix_fmt\",\n \"yuv420p\",\n\n \"-c:a\",\n \"aac\",\n\n \"-b:a\",\n \"128k\",\n );\n\n if (variant.height !== undefined) {\n args.push(\n \"-vf\",\n `scale=-2:${variant.height}:force_original_aspect_ratio=decrease`,\n );\n }\n\n if (variant.bitrate !== undefined) {\n const bitrate = bitrateToNumber(variant.bitrate);\n\n args.push(\n \"-b:v\",\n variant.bitrate,\n\n \"-maxrate\",\n variant.bitrate,\n\n \"-bufsize\",\n String(Math.round(bitrate * 1.5)),\n );\n }\n }\n\n args.push(\n \"-f\",\n \"hls\",\n\n \"-hls_time\",\n String(options.segmentDuration),\n\n \"-hls_playlist_type\",\n \"vod\",\n\n \"-hls_segment_filename\",\n join(outputDirectory, `segment-%05d.${extension}`),\n );\n\n if (options.segmentType === \"fmp4\") {\n args.push(\n \"-hls_segment_type\",\n \"fmp4\",\n\n \"-hls_fmp4_init_filename\",\n \"init.mp4\",\n );\n }\n\n args.push(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, {\n recursive: true,\n });\n\n await runFfmpeg(options.ffmpegPath, args);\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":"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","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 { 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":"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","names":["extname","join","writeFile","mkdir","readFile","rm","join","join","readFile","join","rm","mkdir","extname","extname","extname","join","writeFile","VIRTUAL_PREFIX"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karsten_zhou/vite-plugin-hls",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Transcode video files to HLS (HTTP Live Streaming) at Vite build time.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,6 +54,8 @@
54
54
  "test": "vitest run",
55
55
  "test:watch": "vitest",
56
56
  "test:coverage": "vitest run --coverage",
57
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
58
+ "test:e2e:clean": "node test/e2e/clean.ts",
57
59
  "check": "npm run typecheck && npm run lint && npm run format:check && npm run test && npm run build",
58
60
  "prepare": "husky",
59
61
  "changeset": "changeset",
@@ -71,7 +73,7 @@
71
73
  "access": "public"
72
74
  },
73
75
  "dependencies": {
74
- "ffmpeg-static": "^5.3.0"
76
+ "node-av": "^6.1.1"
75
77
  },
76
78
  "peerDependencies": {
77
79
  "vite": "^8.0.0"
@@ -94,5 +96,9 @@
94
96
  "typescript-eslint": "^8.69.0",
95
97
  "vite": "^8.2.2",
96
98
  "vitest": "^5.0.0"
99
+ },
100
+ "allowScripts": {
101
+ "node-av": true,
102
+ "esbuild": true
97
103
  }
98
104
  }