@uploadbox/video 0.4.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.
Files changed (56) hide show
  1. package/dist/ffutils.d.ts +39 -0
  2. package/dist/ffutils.d.ts.map +1 -0
  3. package/dist/ffutils.js +82 -0
  4. package/dist/ffutils.js.map +1 -0
  5. package/dist/hooks/video-processing.d.ts +47 -0
  6. package/dist/hooks/video-processing.d.ts.map +1 -0
  7. package/dist/hooks/video-processing.js +115 -0
  8. package/dist/hooks/video-processing.js.map +1 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +3 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/metadata.d.ts +19 -0
  14. package/dist/metadata.d.ts.map +1 -0
  15. package/dist/metadata.js +50 -0
  16. package/dist/metadata.js.map +1 -0
  17. package/dist/provider.d.ts +44 -0
  18. package/dist/provider.d.ts.map +1 -0
  19. package/dist/provider.js +2 -0
  20. package/dist/provider.js.map +1 -0
  21. package/dist/providers/external.d.ts +40 -0
  22. package/dist/providers/external.d.ts.map +1 -0
  23. package/dist/providers/external.js +94 -0
  24. package/dist/providers/external.js.map +1 -0
  25. package/dist/providers/ffmpeg.d.ts +27 -0
  26. package/dist/providers/ffmpeg.d.ts.map +1 -0
  27. package/dist/providers/ffmpeg.js +282 -0
  28. package/dist/providers/ffmpeg.js.map +1 -0
  29. package/dist/providers/lambda.d.ts +49 -0
  30. package/dist/providers/lambda.d.ts.map +1 -0
  31. package/dist/providers/lambda.js +80 -0
  32. package/dist/providers/lambda.js.map +1 -0
  33. package/dist/react/index.d.ts +3 -0
  34. package/dist/react/index.d.ts.map +1 -0
  35. package/dist/react/index.js +2 -0
  36. package/dist/react/index.js.map +1 -0
  37. package/dist/react/use-video-player.d.ts +110 -0
  38. package/dist/react/use-video-player.d.ts.map +1 -0
  39. package/dist/react/use-video-player.js +319 -0
  40. package/dist/react/use-video-player.js.map +1 -0
  41. package/dist/types.d.ts +90 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +45 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +83 -0
  46. package/src/ffutils.ts +128 -0
  47. package/src/hooks/video-processing.ts +160 -0
  48. package/src/index.ts +18 -0
  49. package/src/metadata.ts +57 -0
  50. package/src/provider.ts +46 -0
  51. package/src/providers/external.ts +122 -0
  52. package/src/providers/ffmpeg.ts +365 -0
  53. package/src/providers/lambda.ts +112 -0
  54. package/src/react/index.ts +7 -0
  55. package/src/react/use-video-player.ts +444 -0
  56. package/src/types.ts +130 -0
@@ -0,0 +1,282 @@
1
+ import { QUALITY_PRESETS } from "../types.js";
2
+ import { createS3Client } from "@uploadbox/core";
3
+ import { ffprobe, runFFmpeg } from "../ffutils.js";
4
+ import crypto from "crypto";
5
+ import fs from "fs/promises";
6
+ import path from "path";
7
+ import os from "os";
8
+ /**
9
+ * Local FFmpeg transcoding provider.
10
+ *
11
+ * Downloads the source video from S3, transcodes it using a local FFmpeg binary,
12
+ * and uploads HLS segments, playlists, thumbnails, and sprites back to S3.
13
+ *
14
+ * Best for development and small-scale self-hosted deployments.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { createFFmpegProvider } from "@uploadbox/video/providers/ffmpeg";
19
+ *
20
+ * const provider = createFFmpegProvider({ ffmpegPath: "/usr/bin/ffmpeg" });
21
+ * ```
22
+ */
23
+ export function createFFmpegProvider(options = {}) {
24
+ const { ffmpegPath = "ffmpeg", ffprobePath = "ffprobe", tmpDir = os.tmpdir(), } = options;
25
+ const jobs = new Map();
26
+ return {
27
+ name: "ffmpeg-local",
28
+ async submit(input) {
29
+ const jobId = crypto.randomUUID();
30
+ const abortController = new AbortController();
31
+ const jobState = {
32
+ status: "pending",
33
+ progress: 0,
34
+ outputKeys: [],
35
+ abortController,
36
+ };
37
+ jobs.set(jobId, jobState);
38
+ // Run transcoding in background (don't await)
39
+ runTranscoding(jobId, input, jobState, abortController.signal).catch((err) => {
40
+ jobState.status = "failed";
41
+ jobState.error = err.message;
42
+ });
43
+ return { jobId };
44
+ },
45
+ async getJobStatus(jobId) {
46
+ const state = jobs.get(jobId);
47
+ if (!state) {
48
+ return {
49
+ jobId,
50
+ status: "failed",
51
+ progress: 0,
52
+ outputKeys: [],
53
+ error: "Job not found",
54
+ };
55
+ }
56
+ return {
57
+ jobId,
58
+ status: state.status,
59
+ progress: state.progress,
60
+ outputKeys: state.outputKeys,
61
+ error: state.error,
62
+ };
63
+ },
64
+ async cancel(jobId) {
65
+ const state = jobs.get(jobId);
66
+ if (state?.abortController) {
67
+ state.abortController.abort();
68
+ state.status = "failed";
69
+ state.error = "Cancelled";
70
+ }
71
+ },
72
+ };
73
+ async function runTranscoding(jobId, input, state, signal) {
74
+ const workDir = path.join(tmpDir, `uploadbox-video-${jobId}`);
75
+ await fs.mkdir(workDir, { recursive: true });
76
+ const { GetObjectCommand, PutObjectCommand } = await import("@aws-sdk/client-s3");
77
+ const s3Client = createS3Client({
78
+ region: input.s3Config.region ?? "us-east-1",
79
+ bucket: input.sourceBucket,
80
+ accessKeyId: input.s3Config.accessKeyId,
81
+ secretAccessKey: input.s3Config.secretAccessKey,
82
+ endpoint: input.s3Config.endpoint,
83
+ forcePathStyle: input.s3Config.forcePathStyle,
84
+ });
85
+ try {
86
+ state.status = "processing";
87
+ state.progress = 0;
88
+ input.onProgress?.({ jobId, percent: 0, message: "Downloading source video" });
89
+ // Download source from S3
90
+ const sourcePath = path.join(workDir, "source");
91
+ const getResponse = await s3Client.send(new GetObjectCommand({ Bucket: input.sourceBucket, Key: input.sourceKey }));
92
+ const body = await getResponse.Body?.transformToByteArray();
93
+ if (!body)
94
+ throw new Error("Failed to download source video from S3");
95
+ await fs.writeFile(sourcePath, body);
96
+ if (signal.aborted)
97
+ throw new Error("Cancelled");
98
+ // Determine qualities to transcode
99
+ const qualities = input.options.qualities ?? Object.values(QUALITY_PRESETS);
100
+ const segmentDuration = input.options.segmentDuration ?? 6;
101
+ const outputPrefix = input.options.s3OutputPrefix;
102
+ const totalSteps = qualities.length + 2; // +1 thumbnail, +1 sprite
103
+ let completedSteps = 0;
104
+ // Transcode each quality
105
+ const qualityPlaylists = [];
106
+ for (const preset of qualities) {
107
+ if (signal.aborted)
108
+ throw new Error("Cancelled");
109
+ const qualityDir = path.join(workDir, preset.label);
110
+ await fs.mkdir(qualityDir, { recursive: true });
111
+ input.onProgress?.({
112
+ jobId,
113
+ percent: Math.round((completedSteps / totalSteps) * 100),
114
+ currentQuality: preset.label,
115
+ message: `Transcoding ${preset.label}`,
116
+ });
117
+ const segmentPattern = path.join(qualityDir, "segment-%03d.ts");
118
+ const playlistOutput = path.join(qualityDir, "playlist.m3u8");
119
+ await runFFmpeg([
120
+ "-i", sourcePath,
121
+ "-vf", `scale=${preset.width}:${preset.height}:force_original_aspect_ratio=decrease,pad=${preset.width}:${preset.height}:(ow-iw)/2:(oh-ih)/2`,
122
+ "-c:v", "libx264",
123
+ "-b:v", `${preset.videoBitrate}`,
124
+ "-maxrate", `${Math.round(preset.videoBitrate * 1.2)}`,
125
+ "-bufsize", `${Math.round(preset.videoBitrate * 2)}`,
126
+ "-r", `${Math.min(preset.maxFrameRate, 60)}`,
127
+ "-c:a", "aac",
128
+ "-b:a", `${preset.audioBitrate}`,
129
+ "-ac", "2",
130
+ "-hls_time", `${segmentDuration}`,
131
+ "-hls_playlist_type", "vod",
132
+ "-hls_segment_filename", segmentPattern,
133
+ "-f", "hls",
134
+ playlistOutput,
135
+ ], { ffmpegPath, signal });
136
+ // Upload segments and playlist to S3
137
+ const segmentFiles = await fs.readdir(qualityDir);
138
+ for (const file of segmentFiles) {
139
+ const filePath = path.join(qualityDir, file);
140
+ const fileContent = await fs.readFile(filePath);
141
+ const s3Key = `${outputPrefix}/${preset.label}/${file}`;
142
+ const contentType = file.endsWith(".m3u8")
143
+ ? "application/vnd.apple.mpegurl"
144
+ : "video/MP2T";
145
+ await s3Client.send(new PutObjectCommand({
146
+ Bucket: input.outputBucket,
147
+ Key: s3Key,
148
+ Body: fileContent,
149
+ ContentType: contentType,
150
+ }));
151
+ state.outputKeys.push(s3Key);
152
+ }
153
+ qualityPlaylists.push({
154
+ preset,
155
+ playlistKey: `${outputPrefix}/${preset.label}/playlist.m3u8`,
156
+ });
157
+ completedSteps++;
158
+ state.progress = Math.round((completedSteps / totalSteps) * 100);
159
+ }
160
+ if (signal.aborted)
161
+ throw new Error("Cancelled");
162
+ // Generate master playlist
163
+ let masterPlaylist = "#EXTM3U\n#EXT-X-VERSION:3\n";
164
+ for (const { preset } of qualityPlaylists) {
165
+ const bandwidth = preset.videoBitrate + preset.audioBitrate;
166
+ masterPlaylist += `#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth},RESOLUTION=${preset.width}x${preset.height}\n`;
167
+ masterPlaylist += `${preset.label}/playlist.m3u8\n`;
168
+ }
169
+ const masterKey = `${outputPrefix}/master.m3u8`;
170
+ await s3Client.send(new PutObjectCommand({
171
+ Bucket: input.outputBucket,
172
+ Key: masterKey,
173
+ Body: Buffer.from(masterPlaylist),
174
+ ContentType: "application/vnd.apple.mpegurl",
175
+ }));
176
+ state.outputKeys.push(masterKey);
177
+ // Generate poster thumbnail at ~10% duration
178
+ if (input.options.generateThumbnail !== false) {
179
+ input.onProgress?.({
180
+ jobId,
181
+ percent: Math.round((completedSteps / totalSteps) * 100),
182
+ message: "Generating thumbnail",
183
+ });
184
+ const thumbnailPath = path.join(workDir, "thumbnail.jpg");
185
+ // Get duration for timestamp calculation
186
+ const probeData = await ffprobe(sourcePath, { ffprobePath });
187
+ const duration = parseFloat(probeData.format?.duration ?? "10");
188
+ const thumbnailTime = Math.max(0.1, duration * 0.1);
189
+ await runFFmpeg([
190
+ "-ss", `${thumbnailTime}`,
191
+ "-i", sourcePath,
192
+ "-vframes", "1",
193
+ "-q:v", "2",
194
+ thumbnailPath,
195
+ ], { ffmpegPath, signal });
196
+ const thumbContent = await fs.readFile(thumbnailPath);
197
+ const thumbKey = `${outputPrefix}/thumbnail.jpg`;
198
+ await s3Client.send(new PutObjectCommand({
199
+ Bucket: input.outputBucket,
200
+ Key: thumbKey,
201
+ Body: thumbContent,
202
+ ContentType: "image/jpeg",
203
+ }));
204
+ state.outputKeys.push(thumbKey);
205
+ completedSteps++;
206
+ }
207
+ // Generate sprite sheet + WebVTT
208
+ if (input.options.generateSpriteSheet !== false) {
209
+ input.onProgress?.({
210
+ jobId,
211
+ percent: Math.round((completedSteps / totalSteps) * 100),
212
+ message: "Generating sprite sheet",
213
+ });
214
+ const spritesDir = path.join(workDir, "sprites");
215
+ await fs.mkdir(spritesDir, { recursive: true });
216
+ // Extract frames every 10 seconds
217
+ const probeData = await ffprobe(sourcePath, { ffprobePath });
218
+ const duration = parseFloat(probeData.format?.duration ?? "10");
219
+ const interval = 10;
220
+ const thumbWidth = 160;
221
+ const thumbHeight = 90;
222
+ const cols = 10;
223
+ const frameCount = Math.ceil(duration / interval);
224
+ const rows = Math.ceil(frameCount / cols);
225
+ // Generate sprite sheet using a single FFmpeg command
226
+ await runFFmpeg([
227
+ "-i", sourcePath,
228
+ "-vf", `fps=1/${interval},scale=${thumbWidth}:${thumbHeight},tile=${cols}x${rows}`,
229
+ "-frames:v", "1",
230
+ "-q:v", "5",
231
+ path.join(spritesDir, "sprite-sheet.jpg"),
232
+ ], { ffmpegPath, signal });
233
+ // Upload sprite sheet
234
+ const spriteContent = await fs.readFile(path.join(spritesDir, "sprite-sheet.jpg"));
235
+ const spriteKey = `${outputPrefix}/sprites/sprite-sheet.jpg`;
236
+ await s3Client.send(new PutObjectCommand({
237
+ Bucket: input.outputBucket,
238
+ Key: spriteKey,
239
+ Body: spriteContent,
240
+ ContentType: "image/jpeg",
241
+ }));
242
+ state.outputKeys.push(spriteKey);
243
+ // Generate WebVTT for sprite positions
244
+ let vtt = "WEBVTT\n\n";
245
+ for (let i = 0; i < frameCount; i++) {
246
+ const startTime = i * interval;
247
+ const endTime = Math.min((i + 1) * interval, duration);
248
+ const col = i % cols;
249
+ const row = Math.floor(i / cols);
250
+ const x = col * thumbWidth;
251
+ const y = row * thumbHeight;
252
+ vtt += `${formatVttTime(startTime)} --> ${formatVttTime(endTime)}\n`;
253
+ vtt += `sprite-sheet.jpg#xywh=${x},${y},${thumbWidth},${thumbHeight}\n\n`;
254
+ }
255
+ const vttKey = `${outputPrefix}/sprites/sprites.vtt`;
256
+ await s3Client.send(new PutObjectCommand({
257
+ Bucket: input.outputBucket,
258
+ Key: vttKey,
259
+ Body: Buffer.from(vtt),
260
+ ContentType: "text/vtt",
261
+ }));
262
+ state.outputKeys.push(vttKey);
263
+ completedSteps++;
264
+ }
265
+ state.status = "completed";
266
+ state.progress = 100;
267
+ input.onProgress?.({ jobId, percent: 100, message: "Transcoding complete" });
268
+ }
269
+ finally {
270
+ // Cleanup temp directory
271
+ await fs.rm(workDir, { recursive: true, force: true }).catch(() => { });
272
+ }
273
+ }
274
+ }
275
+ function formatVttTime(seconds) {
276
+ const h = Math.floor(seconds / 3600);
277
+ const m = Math.floor((seconds % 3600) / 60);
278
+ const s = Math.floor(seconds % 60);
279
+ const ms = Math.round((seconds % 1) * 1000);
280
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}.${String(ms).padStart(3, "0")}`;
281
+ }
282
+ //# sourceMappingURL=ffmpeg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/providers/ffmpeg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { TranscodingProvider } from "../provider.js";
2
+ import type { TranscodingJob } from "../types.js";
3
+ interface LambdaProviderOptions {
4
+ /** AWS region where the Lambda function is deployed. */
5
+ region: string;
6
+ /** ARN of the Lambda function that performs transcoding. */
7
+ functionArn: string;
8
+ /**
9
+ * Function to poll job status. The Lambda function should store status
10
+ * in DynamoDB or a similar store that this function can query.
11
+ */
12
+ getStatus: (jobId: string) => Promise<TranscodingJob>;
13
+ /**
14
+ * Optional function to cancel a job.
15
+ */
16
+ cancelJob?: (jobId: string) => Promise<void>;
17
+ }
18
+ /**
19
+ * AWS Lambda transcoding provider.
20
+ *
21
+ * Invokes a user-provided Lambda function asynchronously for transcoding.
22
+ * The Lambda function is NOT included — users must implement and deploy it themselves.
23
+ *
24
+ * The Lambda function receives the same input payload and should:
25
+ * 1. Download the source video from S3
26
+ * 2. Transcode to HLS using FFmpeg (via a Lambda layer)
27
+ * 3. Upload outputs back to S3
28
+ * 4. Store job status in DynamoDB for polling
29
+ *
30
+ * Requires `@aws-sdk/client-lambda` to be installed by the user.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { createLambdaProvider } from "@uploadbox/video/providers/lambda";
35
+ *
36
+ * const provider = createLambdaProvider({
37
+ * region: "us-east-1",
38
+ * functionArn: "arn:aws:lambda:us-east-1:123456789:function:video-transcode",
39
+ * getStatus: async (jobId) => {
40
+ * // Query DynamoDB for job status
41
+ * const item = await dynamodb.get({ TableName: "jobs", Key: { jobId } });
42
+ * return item;
43
+ * },
44
+ * });
45
+ * ```
46
+ */
47
+ export declare function createLambdaProvider(options: LambdaProviderOptions): TranscodingProvider;
48
+ export {};
49
+ //# sourceMappingURL=lambda.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambda.d.ts","sourceRoot":"","sources":["../../src/providers/lambda.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAA0B,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,qBAAqB;IAC7B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACtD;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,GAC7B,mBAAmB,CA4DrB"}
@@ -0,0 +1,80 @@
1
+ import crypto from "crypto";
2
+ /**
3
+ * AWS Lambda transcoding provider.
4
+ *
5
+ * Invokes a user-provided Lambda function asynchronously for transcoding.
6
+ * The Lambda function is NOT included — users must implement and deploy it themselves.
7
+ *
8
+ * The Lambda function receives the same input payload and should:
9
+ * 1. Download the source video from S3
10
+ * 2. Transcode to HLS using FFmpeg (via a Lambda layer)
11
+ * 3. Upload outputs back to S3
12
+ * 4. Store job status in DynamoDB for polling
13
+ *
14
+ * Requires `@aws-sdk/client-lambda` to be installed by the user.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { createLambdaProvider } from "@uploadbox/video/providers/lambda";
19
+ *
20
+ * const provider = createLambdaProvider({
21
+ * region: "us-east-1",
22
+ * functionArn: "arn:aws:lambda:us-east-1:123456789:function:video-transcode",
23
+ * getStatus: async (jobId) => {
24
+ * // Query DynamoDB for job status
25
+ * const item = await dynamodb.get({ TableName: "jobs", Key: { jobId } });
26
+ * return item;
27
+ * },
28
+ * });
29
+ * ```
30
+ */
31
+ export function createLambdaProvider(options) {
32
+ const { region, functionArn, getStatus, cancelJob } = options;
33
+ return {
34
+ name: "aws-lambda",
35
+ async submit(input) {
36
+ // Dynamic import to keep @aws-sdk/client-lambda optional
37
+ let LambdaClient;
38
+ let InvokeCommand;
39
+ try {
40
+ const mod = await Function('return import("@aws-sdk/client-lambda")')();
41
+ LambdaClient = mod.LambdaClient;
42
+ InvokeCommand = mod.InvokeCommand;
43
+ }
44
+ catch {
45
+ throw new Error("@aws-sdk/client-lambda is not installed. Run: npm install @aws-sdk/client-lambda");
46
+ }
47
+ const lambda = new LambdaClient({
48
+ region,
49
+ credentials: {
50
+ accessKeyId: input.s3Config.accessKeyId,
51
+ secretAccessKey: input.s3Config.secretAccessKey,
52
+ },
53
+ });
54
+ const jobId = crypto.randomUUID();
55
+ const payload = {
56
+ jobId,
57
+ sourceKey: input.sourceKey,
58
+ sourceBucket: input.sourceBucket,
59
+ outputBucket: input.outputBucket,
60
+ options: input.options,
61
+ s3Config: input.s3Config,
62
+ };
63
+ await lambda.send(new InvokeCommand({
64
+ FunctionName: functionArn,
65
+ InvocationType: "Event",
66
+ Payload: Buffer.from(JSON.stringify(payload)),
67
+ }));
68
+ return { jobId };
69
+ },
70
+ async getJobStatus(jobId) {
71
+ return getStatus(jobId);
72
+ },
73
+ async cancel(jobId) {
74
+ if (cancelJob) {
75
+ await cancelJob(jobId);
76
+ }
77
+ },
78
+ };
79
+ }
80
+ //# sourceMappingURL=lambda.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lambda.js","sourceRoot":"","sources":["../../src/providers/lambda.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAkB5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA8B;IAE9B,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAE9D,OAAO;QACL,IAAI,EAAE,YAAY;QAElB,KAAK,CAAC,MAAM,CAAC,KAA6B;YACxC,yDAAyD;YACzD,IAAI,YAAiB,CAAC;YACtB,IAAI,aAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAO,QAAQ,CAAC,yCAAyC,CAAC,EAAmB,CAAC;gBAC1F,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;gBAChC,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;YACpC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;gBAC9B,MAAM;gBACN,WAAW,EAAE;oBACX,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW;oBACvC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC,eAAe;iBAChD;aACF,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YAElC,MAAM,OAAO,GAAG;gBACd,KAAK;gBACL,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,YAAY,EAAE,KAAK,CAAC,YAAY;gBAChC,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;aACzB,CAAC;YAEF,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,aAAa,CAAC;gBAChB,YAAY,EAAE,WAAW;gBACzB,cAAc,EAAE,OAAO;gBACvB,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;aAC9C,CAAC,CACH,CAAC;YAEF,OAAO,EAAE,KAAK,EAAE,CAAC;QACnB,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,KAAa;YAC9B,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,KAAa;YACxB,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { useVideoPlayer } from "./use-video-player.js";
2
+ export type { UseVideoPlayerOptions, VideoPlayerState, QualityLevel, SpritePosition, } from "./use-video-player.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EACV,qBAAqB,EACrB,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { useVideoPlayer } from "./use-video-player.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,110 @@
1
+ import type { RefObject } from "react";
2
+ /** A single HLS quality level. */
3
+ export interface QualityLevel {
4
+ index: number;
5
+ width: number;
6
+ height: number;
7
+ bitrate: number;
8
+ label: string;
9
+ }
10
+ /** Sprite position for scrub preview. */
11
+ export interface SpritePosition {
12
+ url: string;
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ }
18
+ /** Options for the useVideoPlayer hook. */
19
+ export interface UseVideoPlayerOptions {
20
+ /** Auto-play the video when ready. @default false */
21
+ autoPlay?: boolean;
22
+ /** Initial volume (0–1). @default 1 */
23
+ initialVolume?: number;
24
+ /** URL to the WebVTT sprite sheet file. */
25
+ spriteVttUrl?: string;
26
+ }
27
+ /** Return value of the useVideoPlayer hook. */
28
+ export interface VideoPlayerState {
29
+ /** Ref to attach to the <video> element. */
30
+ videoRef: RefObject<HTMLVideoElement | null>;
31
+ /** Whether the video is currently playing. */
32
+ isPlaying: boolean;
33
+ /** Current playback position in seconds. */
34
+ currentTime: number;
35
+ /** Total duration in seconds. */
36
+ duration: number;
37
+ /** Current volume (0–1). */
38
+ volume: number;
39
+ /** Whether audio is muted. */
40
+ isMuted: boolean;
41
+ /** Current playback rate. */
42
+ playbackRate: number;
43
+ /** Available quality levels. */
44
+ qualities: QualityLevel[];
45
+ /** Index of the current quality level (-1 for auto). */
46
+ currentQuality: number;
47
+ /** Whether adaptive quality selection is active. */
48
+ isAutoQuality: boolean;
49
+ /** Whether fullscreen is active. */
50
+ isFullscreen: boolean;
51
+ /** Whether the video is buffering. */
52
+ isBuffering: boolean;
53
+ /** Start playback. */
54
+ play: () => void;
55
+ /** Pause playback. */
56
+ pause: () => void;
57
+ /** Toggle play/pause. */
58
+ toggle: () => void;
59
+ /** Seek to a position in seconds. */
60
+ seek: (time: number) => void;
61
+ /** Set volume (0–1). */
62
+ setVolume: (vol: number) => void;
63
+ /** Toggle mute/unmute. */
64
+ toggleMute: () => void;
65
+ /** Set playback rate. */
66
+ setPlaybackRate: (rate: number) => void;
67
+ /** Set quality level by index. Pass -1 for auto. */
68
+ setQuality: (index: number) => void;
69
+ /** Enter fullscreen. */
70
+ enterFullscreen: () => void;
71
+ /** Exit fullscreen. */
72
+ exitFullscreen: () => void;
73
+ /** Toggle fullscreen. */
74
+ toggleFullscreen: () => void;
75
+ /** Get sprite position for a given time (for scrub preview). */
76
+ getSpriteForTime: (time: number) => SpritePosition | null;
77
+ }
78
+ /**
79
+ * Headless React hook for HLS video playback.
80
+ *
81
+ * Provides playback controls, quality switching, fullscreen, and sprite preview
82
+ * without any UI — you build your own player controls.
83
+ *
84
+ * Uses `hls.js` for adaptive streaming with a fallback to native `<video>` HLS
85
+ * support (Safari).
86
+ *
87
+ * @param src - URL to the HLS master playlist (.m3u8) or a regular video file.
88
+ * @param options - Configuration options.
89
+ *
90
+ * @example
91
+ * ```tsx
92
+ * import { useVideoPlayer } from "@uploadbox/video/react";
93
+ *
94
+ * function Player({ url }: { url: string }) {
95
+ * const player = useVideoPlayer(url);
96
+ *
97
+ * return (
98
+ * <div>
99
+ * <video ref={player.videoRef} />
100
+ * <button onClick={player.toggle}>
101
+ * {player.isPlaying ? "Pause" : "Play"}
102
+ * </button>
103
+ * <span>{player.currentTime} / {player.duration}</span>
104
+ * </div>
105
+ * );
106
+ * }
107
+ * ```
108
+ */
109
+ export declare function useVideoPlayer(src: string, options?: UseVideoPlayerOptions): VideoPlayerState;
110
+ //# sourceMappingURL=use-video-player.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-video-player.d.ts","sourceRoot":"","sources":["../../src/react/use-video-player.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2CAA2C;AAC3C,MAAM,WAAW,qBAAqB;IACpC,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAC7C,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,6BAA6B;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,oCAAoC;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,sCAAsC;IACtC,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB;IACtB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,sBAAsB;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,yBAAyB;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,qCAAqC;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,wBAAwB;IACxB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,0BAA0B;IAC1B,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,yBAAyB;IACzB,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,oDAAoD;IACpD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,wBAAwB;IACxB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,uBAAuB;IACvB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,gEAAgE;IAChE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,GAAG,IAAI,CAAC;CAC3D;AAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,qBAA0B,GAClC,gBAAgB,CA+PlB"}