@kimjansheden/payload-video-processor 0.1.1

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.
@@ -0,0 +1,483 @@
1
+ 'use strict';
2
+
3
+ var path = require('path');
4
+ var url = require('url');
5
+ var promises = require('fs/promises');
6
+ var ffmpeg2 = require('fluent-ffmpeg');
7
+ var ffmpegStatic = require('ffmpeg-static');
8
+ var ffprobeStatic = require('ffprobe-static');
9
+ var bullmq = require('bullmq');
10
+ var IORedis = require('ioredis');
11
+ var zod = require('zod');
12
+ var payload = require('payload');
13
+
14
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
+
16
+ var path__default = /*#__PURE__*/_interopDefault(path);
17
+ var ffmpeg2__default = /*#__PURE__*/_interopDefault(ffmpeg2);
18
+ var ffmpegStatic__default = /*#__PURE__*/_interopDefault(ffmpegStatic);
19
+ var ffprobeStatic__default = /*#__PURE__*/_interopDefault(ffprobeStatic);
20
+ var IORedis__default = /*#__PURE__*/_interopDefault(IORedis);
21
+ var payload__default = /*#__PURE__*/_interopDefault(payload);
22
+
23
+ // src/queue/worker.ts
24
+ var presetSchema = zod.z.object({
25
+ args: zod.z.array(zod.z.string()),
26
+ label: zod.z.string().optional(),
27
+ enableCrop: zod.z.boolean().optional()
28
+ });
29
+ var videoPluginOptionsSchema = zod.z.object({
30
+ presets: zod.z.record(presetSchema).refine((value) => Object.keys(value).length > 0, {
31
+ message: "At least one preset must be defined."
32
+ }),
33
+ queue: zod.z.object({
34
+ name: zod.z.string().min(1).optional(),
35
+ redisUrl: zod.z.string().optional(),
36
+ concurrency: zod.z.number().int().positive().optional()
37
+ }).optional(),
38
+ access: zod.z.any().optional(),
39
+ resolvePaths: zod.z.any().optional()
40
+ });
41
+ var ensureOptions = (options) => videoPluginOptionsSchema.parse(options);
42
+ var cropSchema = zod.z.object({
43
+ x: zod.z.number().min(0).max(1),
44
+ y: zod.z.number().min(0).max(1),
45
+ width: zod.z.number().positive().max(1),
46
+ height: zod.z.number().positive().max(1)
47
+ }).refine((value) => value.width > 0 && value.height > 0, {
48
+ message: "Crop width and height must be > 0"
49
+ });
50
+ var videoJobSchema = zod.z.object({
51
+ collection: zod.z.string().min(1),
52
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform((value) => value.toString()),
53
+ preset: zod.z.string().min(1),
54
+ crop: cropSchema.optional()
55
+ });
56
+
57
+ // src/ffmpeg/args.ts
58
+ var FASTSTART_FLAGS = ["-movflags", "+faststart"];
59
+ var CRF_FLAG = "-crf";
60
+ var hasCrf = (args) => {
61
+ for (let i = 0; i < args.length; i += 1) {
62
+ if (args[i] === CRF_FLAG) {
63
+ return true;
64
+ }
65
+ }
66
+ return false;
67
+ };
68
+ var hasFaststart = (args) => {
69
+ for (let i = 0; i < args.length; i += 1) {
70
+ if (args[i] === "-movflags") {
71
+ const value = args[i + 1];
72
+ if (typeof value === "string" && value.includes("faststart")) {
73
+ return true;
74
+ }
75
+ }
76
+ }
77
+ return false;
78
+ };
79
+ var extractFilters = (args) => {
80
+ const rest = [];
81
+ const filters = [];
82
+ for (let i = 0; i < args.length; i += 1) {
83
+ const current = args[i];
84
+ if (current === "-vf" || current === "-filter:v") {
85
+ const value = args[i + 1];
86
+ if (typeof value === "string") {
87
+ filters.push(value);
88
+ }
89
+ i += 1;
90
+ } else {
91
+ rest.push(current);
92
+ }
93
+ }
94
+ return { rest, filters };
95
+ };
96
+ var clamp = (value, min, max) => {
97
+ if (Number.isNaN(value)) return min;
98
+ if (value < min) return min;
99
+ if (value > max) return max;
100
+ return value;
101
+ };
102
+ var buildCropFilter = (crop, dimensions) => {
103
+ if (!dimensions?.width || !dimensions?.height) return void 0;
104
+ const cropWidth = Math.max(1, Math.round(dimensions.width * crop.width));
105
+ const cropHeight = Math.max(1, Math.round(dimensions.height * crop.height));
106
+ const maxX = Math.max(0, dimensions.width - cropWidth);
107
+ const maxY = Math.max(0, dimensions.height - cropHeight);
108
+ const x = clamp(Math.round(dimensions.width * crop.x), 0, maxX);
109
+ const y = clamp(Math.round(dimensions.height * crop.y), 0, maxY);
110
+ return `crop=${cropWidth}:${cropHeight}:${x}:${y}`;
111
+ };
112
+ var buildFfmpegArgs = ({
113
+ presetArgs,
114
+ crop,
115
+ dimensions,
116
+ defaultCrf = 24
117
+ }) => {
118
+ const args = [...presetArgs];
119
+ const { rest, filters } = extractFilters(args);
120
+ if (!hasCrf(rest)) {
121
+ rest.push(CRF_FLAG, String(defaultCrf));
122
+ }
123
+ if (!hasFaststart(rest)) {
124
+ rest.push(...FASTSTART_FLAGS);
125
+ }
126
+ if (crop) {
127
+ const cropFilter = buildCropFilter(crop, dimensions);
128
+ if (cropFilter) {
129
+ filters.push(cropFilter);
130
+ }
131
+ }
132
+ if (filters.length > 0) {
133
+ rest.push("-vf", filters.join(","));
134
+ }
135
+ return {
136
+ globalOptions: ["-y"],
137
+ outputOptions: rest
138
+ };
139
+ };
140
+ if (ffprobeStatic__default.default.path) {
141
+ ffmpeg2__default.default.setFfprobePath(ffprobeStatic__default.default.path);
142
+ }
143
+ var probeVideo = async (filePath) => new Promise((resolve, reject) => {
144
+ ffmpeg2__default.default.ffprobe(filePath, (error, metadata) => {
145
+ if (error) {
146
+ reject(error);
147
+ return;
148
+ }
149
+ const videoStream = metadata.streams.find(
150
+ (stream) => stream.codec_type === "video"
151
+ );
152
+ const width = videoStream?.width;
153
+ const height = videoStream?.height;
154
+ const durationRaw = videoStream?.duration ?? metadata.format?.duration;
155
+ const duration = typeof durationRaw !== "undefined" ? Number(durationRaw) : void 0;
156
+ const bitrateRaw = videoStream?.bit_rate ?? metadata.format?.bit_rate;
157
+ const bitrate = typeof bitrateRaw !== "undefined" ? Number(bitrateRaw) : void 0;
158
+ resolve({
159
+ width: width ?? void 0,
160
+ height: height ?? void 0,
161
+ duration: Number.isNaN(duration) ? void 0 : duration,
162
+ bitrate: Number.isNaN(bitrate) ? void 0 : bitrate
163
+ });
164
+ });
165
+ });
166
+ var normalizeUrl = (input, filename) => {
167
+ if (!input) return filename ?? "";
168
+ const parts = input.split("?");
169
+ const base = parts[0];
170
+ const query = parts[1] ? `?${parts.slice(1).join("?")}` : "";
171
+ const lastSlash = base.lastIndexOf("/");
172
+ if (lastSlash === -1) {
173
+ return filename ?? base;
174
+ }
175
+ const prefix = base.slice(0, lastSlash);
176
+ const sanitized = filename ?? base.slice(lastSlash + 1);
177
+ return `${prefix}/${sanitized}${query}`;
178
+ };
179
+ var defaultResolvePaths = ({
180
+ original,
181
+ presetName
182
+ }) => {
183
+ const originalFilename = original.filename ?? path__default.default.basename(original.path);
184
+ const extension = path__default.default.extname(originalFilename) || path__default.default.extname(original.path) || ".mp4";
185
+ const baseName = path__default.default.basename(originalFilename, extension);
186
+ const variantFilename = `${baseName}_${presetName}${extension}`;
187
+ const originalDir = path__default.default.dirname(original.path);
188
+ const absoluteDir = path__default.default.isAbsolute(original.path) ? originalDir : path__default.default.join(process.cwd(), originalDir);
189
+ const url = normalizeUrl(original.url, variantFilename);
190
+ return {
191
+ dir: absoluteDir,
192
+ filename: variantFilename,
193
+ url
194
+ };
195
+ };
196
+ var buildStoredPath = (originalPath, variantFilename) => {
197
+ const originalDir = path__default.default.dirname(originalPath);
198
+ return path__default.default.join(originalDir, variantFilename);
199
+ };
200
+ var buildWritePath = (dir, filename) => path__default.default.join(dir, filename);
201
+ var cachedClient = null;
202
+ var localInitialized = false;
203
+ var normalizeConfigPath = (configPath) => {
204
+ if (configPath.startsWith("file://")) return configPath;
205
+ return url.pathToFileURL(path__default.default.resolve(configPath)).href;
206
+ };
207
+ var buildAuthHeaders = (token) => ({
208
+ Authorization: `Bearer ${token}`,
209
+ "X-Payload-API-Key": token
210
+ });
211
+ var initLocalPayload = async () => {
212
+ const secret = process.env.PAYLOAD_SECRET;
213
+ const mongoURL = process.env.MONGODB_URI;
214
+ if (!secret || !mongoURL) {
215
+ return null;
216
+ }
217
+ try {
218
+ const configPath = process.env.PAYLOAD_CONFIG_PATH;
219
+ let configModule;
220
+ if (configPath) {
221
+ const imported = await import(normalizeConfigPath(configPath));
222
+ configModule = imported?.default ?? imported;
223
+ }
224
+ if (!localInitialized) {
225
+ const initOptions = {
226
+ secret,
227
+ mongoURL,
228
+ local: true
229
+ };
230
+ if (configModule) {
231
+ initOptions.config = configModule;
232
+ }
233
+ await payload__default.default.init(initOptions);
234
+ localInitialized = true;
235
+ }
236
+ const instance = payload__default.default;
237
+ return {
238
+ findByID: ({ collection, id }) => instance.findByID({
239
+ collection,
240
+ id
241
+ }),
242
+ update: ({ collection, id, data }) => instance.update({
243
+ collection,
244
+ id,
245
+ data
246
+ }),
247
+ getCollectionConfig: (slug) => instance.collections?.[slug]?.config
248
+ };
249
+ } catch (error) {
250
+ console.warn(
251
+ "[video-processor] Failed to initialize Payload locally, falling back to REST client.",
252
+ error
253
+ );
254
+ return null;
255
+ }
256
+ };
257
+ var initRestClient = async () => {
258
+ const baseUrl = process.env.PAYLOAD_REST_URL || process.env.PAYLOAD_PUBLIC_URL || process.env.PAYLOAD_SERVER_URL;
259
+ const token = process.env.PAYLOAD_ADMIN_TOKEN;
260
+ if (!baseUrl || !token) {
261
+ throw new Error(
262
+ "Unable to establish Payload REST client. Provide PAYLOAD_REST_URL (or PAYLOAD_PUBLIC_URL) and PAYLOAD_ADMIN_TOKEN."
263
+ );
264
+ }
265
+ const base = baseUrl.replace(/\/$/, "");
266
+ const headers = buildAuthHeaders(token);
267
+ const request = async (url, init) => {
268
+ const response = await fetch(url, {
269
+ ...init,
270
+ headers: {
271
+ "Content-Type": "application/json",
272
+ Accept: "application/json",
273
+ ...headers,
274
+ ...init?.headers
275
+ }
276
+ });
277
+ if (!response.ok) {
278
+ const text = await response.text();
279
+ throw new Error(
280
+ `Payload REST request failed (${response.status}): ${text}`
281
+ );
282
+ }
283
+ return await response.json();
284
+ };
285
+ return {
286
+ findByID: async ({ collection, id }) => {
287
+ const result = await request(
288
+ `${base}/api/${collection}/${id}`
289
+ );
290
+ return result.doc ?? result;
291
+ },
292
+ update: async ({ collection, id, data }) => {
293
+ const result = await request(
294
+ `${base}/api/${collection}/${id}`,
295
+ {
296
+ method: "PATCH",
297
+ body: JSON.stringify(data)
298
+ }
299
+ );
300
+ return result.doc ?? result;
301
+ }
302
+ };
303
+ };
304
+ var getPayloadClient = async () => {
305
+ if (cachedClient) return cachedClient;
306
+ const localClient = await initLocalPayload();
307
+ if (localClient) {
308
+ cachedClient = localClient;
309
+ return localClient;
310
+ }
311
+ const restClient = await initRestClient();
312
+ cachedClient = restClient;
313
+ return restClient;
314
+ };
315
+
316
+ // src/queue/createWorker.ts
317
+ var envFfmpegPath = process.env.FFMPEG_BIN?.trim();
318
+ var ffmpegBinary = envFfmpegPath && envFfmpegPath.length > 0 ? envFfmpegPath : typeof ffmpegStatic__default.default === "string" ? ffmpegStatic__default.default : null;
319
+ if (ffmpegBinary) {
320
+ ffmpeg2__default.default.setFfmpegPath(ffmpegBinary);
321
+ }
322
+ if (ffprobeStatic__default.default.path) {
323
+ ffmpeg2__default.default.setFfprobePath(ffprobeStatic__default.default.path);
324
+ }
325
+ var createWorker = async (rawOptions) => {
326
+ const options = ensureOptions(rawOptions);
327
+ const presets = options.presets;
328
+ const queueName = options.queue?.name ?? "video-transcode";
329
+ const concurrency = options.queue?.concurrency ?? 1;
330
+ const redisUrl = options.queue?.redisUrl ?? process.env.REDIS_URL;
331
+ const connection = redisUrl ? new IORedis__default.default(redisUrl, { maxRetriesPerRequest: null }) : new IORedis__default.default({ maxRetriesPerRequest: null });
332
+ const worker = new bullmq.Worker(
333
+ queueName,
334
+ async (job) => {
335
+ const parsed = videoJobSchema.parse(job.data);
336
+ const preset = presets[parsed.preset];
337
+ if (!preset) {
338
+ throw new Error(`Unknown preset ${parsed.preset}`);
339
+ }
340
+ job.updateProgress(5);
341
+ const client = await getPayloadClient();
342
+ const document = await client.findByID({
343
+ collection: parsed.collection,
344
+ id: parsed.id
345
+ });
346
+ if (!document) {
347
+ throw new Error(
348
+ `Document ${parsed.id} in collection ${parsed.collection} not found`
349
+ );
350
+ }
351
+ const originalPath = document?.path;
352
+ const filename = document?.filename;
353
+ const url = document?.url;
354
+ if (!originalPath) {
355
+ throw new Error("Source document does not expose a `path` property.");
356
+ }
357
+ const absoluteInputPath = path__default.default.isAbsolute(originalPath) ? originalPath : path__default.default.join(process.cwd(), originalPath);
358
+ const inputMetadata = await probeVideo(absoluteInputPath);
359
+ job.updateProgress(15);
360
+ const resolvePaths = options.resolvePaths ?? defaultResolvePaths;
361
+ const collectionConfig = client.getCollectionConfig?.(parsed.collection) ?? null;
362
+ const resolved = resolvePaths({
363
+ doc: document,
364
+ collection: collectionConfig,
365
+ collectionSlug: parsed.collection,
366
+ original: {
367
+ filename: filename ?? path__default.default.basename(originalPath),
368
+ path: originalPath,
369
+ url: url ?? ""
370
+ },
371
+ presetName: parsed.preset
372
+ });
373
+ const writeDir = resolved.dir;
374
+ const writeFilename = resolved.filename;
375
+ const targetUrl = resolved.url;
376
+ const writePath = buildWritePath(writeDir, writeFilename);
377
+ await promises.mkdir(writeDir, { recursive: true });
378
+ const { globalOptions, outputOptions } = buildFfmpegArgs({
379
+ presetArgs: preset.args,
380
+ crop: parsed.crop,
381
+ dimensions: {
382
+ width: inputMetadata.width,
383
+ height: inputMetadata.height
384
+ }
385
+ });
386
+ await new Promise((resolve, reject) => {
387
+ const command = ffmpeg2__default.default(absoluteInputPath);
388
+ globalOptions.forEach((option) => command.addOption(option));
389
+ command.outputOptions(outputOptions);
390
+ command.output(writePath);
391
+ command.on("progress", (progress) => {
392
+ if (typeof progress.percent === "number") {
393
+ const bounded = Math.min(95, 15 + progress.percent * 0.7);
394
+ void job.updateProgress(bounded);
395
+ }
396
+ });
397
+ command.on("end", () => resolve());
398
+ command.on("error", (error) => reject(error));
399
+ command.run();
400
+ });
401
+ const fileStats = await promises.stat(writePath);
402
+ const outputMetadata = await probeVideo(writePath);
403
+ const storedPath = buildStoredPath(originalPath, writeFilename);
404
+ const variant = {
405
+ preset: parsed.preset,
406
+ url: targetUrl,
407
+ path: storedPath,
408
+ size: fileStats.size,
409
+ duration: outputMetadata.duration ?? inputMetadata.duration,
410
+ width: outputMetadata.width ?? inputMetadata.width,
411
+ height: outputMetadata.height ?? inputMetadata.height,
412
+ bitrate: outputMetadata.bitrate,
413
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
414
+ };
415
+ const existingVariants = Array.isArray(document.variants) ? document.variants : [];
416
+ const nextVariants = [
417
+ ...existingVariants.filter((item) => item?.preset !== variant.preset),
418
+ variant
419
+ ];
420
+ await client.update({
421
+ collection: parsed.collection,
422
+ id: parsed.id,
423
+ data: {
424
+ variants: nextVariants
425
+ }
426
+ });
427
+ await job.updateProgress(100);
428
+ return variant;
429
+ },
430
+ {
431
+ connection,
432
+ concurrency
433
+ }
434
+ );
435
+ worker.on("failed", (job, error) => {
436
+ console.error(`[video-processor] Job ${job?.id} failed`, error);
437
+ });
438
+ worker.on("completed", (job) => {
439
+ console.log(`[video-processor] Job ${job.id} completed`);
440
+ });
441
+ await worker.waitUntilReady();
442
+ console.log(`[video-processor] Worker listening on queue ${queueName}`);
443
+ const shutdown = async () => {
444
+ await worker.close();
445
+ await connection.quit();
446
+ };
447
+ process.once("SIGINT", () => {
448
+ void shutdown().then(() => process.exit(0));
449
+ });
450
+ process.once("SIGTERM", () => {
451
+ void shutdown().then(() => process.exit(0));
452
+ });
453
+ return worker;
454
+ };
455
+
456
+ // src/queue/worker.ts
457
+ var loadOptions = async () => {
458
+ const modulePath = process.env.PAYLOAD_VIDEO_WORKER_CONFIG;
459
+ if (!modulePath) {
460
+ throw new Error(
461
+ "PAYLOAD_VIDEO_WORKER_CONFIG must point to a module exporting plugin options."
462
+ );
463
+ }
464
+ const resolved = modulePath.startsWith(".") || modulePath.startsWith("/") ? url.pathToFileURL(path__default.default.resolve(modulePath)).href : modulePath;
465
+ const imported = await import(resolved);
466
+ const options = imported.default ?? imported;
467
+ if (!options || typeof options !== "object" || !("presets" in options)) {
468
+ throw new Error(
469
+ "Invalid worker options module. Ensure it exports VideoPluginOptions."
470
+ );
471
+ }
472
+ return options;
473
+ };
474
+ var main = async () => {
475
+ const options = await loadOptions();
476
+ await createWorker(options);
477
+ };
478
+ void main().catch((error) => {
479
+ console.error("[video-processor] Worker failed to start", error);
480
+ process.exit(1);
481
+ });
482
+ //# sourceMappingURL=worker.cjs.map
483
+ //# sourceMappingURL=worker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/options.ts","../../src/queue/job.types.ts","../../src/ffmpeg/args.ts","../../src/ffmpeg/probe.ts","../../src/utils/paths.ts","../../src/utils/payload.ts","../../src/queue/createWorker.ts","../../src/queue/worker.ts"],"names":["z","ffprobeStatic","ffmpeg","path","pathToFileURL","payload","ffmpegStatic","IORedis","Worker","mkdir","stat"],"mappings":"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","file":"worker.cjs","sourcesContent":["import { z } from \"zod\";\nimport type { Preset, VideoPluginOptions } from \"./types\";\n\nexport const presetSchema = z.object({\n args: z.array(z.string()),\n label: z.string().optional(),\n enableCrop: z.boolean().optional(),\n});\n\nexport const videoPluginOptionsSchema = z.object({\n presets: z\n .record(presetSchema)\n .refine((value) => Object.keys(value).length > 0, {\n message: \"At least one preset must be defined.\",\n }),\n queue: z\n .object({\n name: z.string().min(1).optional(),\n redisUrl: z.string().optional(),\n concurrency: z.number().int().positive().optional(),\n })\n .optional(),\n access: z.any().optional(),\n resolvePaths: z.any().optional(),\n});\n\nexport type NormalizedPresets = Record<string, Preset>;\n\nexport const normalizePresets = (\n presets: Record<string, Preset>,\n): NormalizedPresets => {\n const entries = Object.entries(presets).map(([name, preset]) => [\n name,\n { ...preset },\n ]);\n return Object.fromEntries(entries);\n};\n\nexport const ensureOptions = (\n options: VideoPluginOptions,\n): VideoPluginOptions =>\n videoPluginOptionsSchema.parse(options) as VideoPluginOptions;\n","import { z } from \"zod\";\n\nexport const cropSchema = z\n .object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n width: z.number().positive().max(1),\n height: z.number().positive().max(1),\n })\n .refine((value) => value.width > 0 && value.height > 0, {\n message: \"Crop width and height must be > 0\",\n });\n\nexport const videoJobSchema = z.object({\n collection: z.string().min(1),\n id: z.union([z.string(), z.number()]).transform((value) => value.toString()),\n preset: z.string().min(1),\n crop: cropSchema.optional(),\n});\n\nexport type VideoJobData = z.infer<typeof videoJobSchema>;\nexport type CropData = z.infer<typeof cropSchema>;\n","import { CropRect } from \"../types\";\nimport type { CropData } from \"../queue/job.types\";\n\nexport type Dimensions = {\n width?: number;\n height?: number;\n};\n\nexport type BuildArgsInput = {\n presetArgs: string[];\n crop?: CropRect | CropData;\n dimensions?: Dimensions;\n defaultCrf?: number;\n};\n\nexport type BuildArgsResult = {\n globalOptions: string[];\n outputOptions: string[];\n};\n\nconst FASTSTART_FLAGS = [\"-movflags\", \"+faststart\"];\nconst CRF_FLAG = \"-crf\";\n\nconst hasCrf = (args: string[]): boolean => {\n for (let i = 0; i < args.length; i += 1) {\n if (args[i] === CRF_FLAG) {\n return true;\n }\n }\n\n return false;\n};\n\nconst hasFaststart = (args: string[]): boolean => {\n for (let i = 0; i < args.length; i += 1) {\n if (args[i] === \"-movflags\") {\n const value = args[i + 1];\n if (typeof value === \"string\" && value.includes(\"faststart\")) {\n return true;\n }\n }\n }\n\n return false;\n};\n\nconst extractFilters = (\n args: string[],\n): { rest: string[]; filters: string[] } => {\n const rest: string[] = [];\n const filters: string[] = [];\n\n for (let i = 0; i < args.length; i += 1) {\n const current = args[i];\n if (current === \"-vf\" || current === \"-filter:v\") {\n const value = args[i + 1];\n if (typeof value === \"string\") {\n filters.push(value);\n }\n i += 1;\n } else {\n rest.push(current);\n }\n }\n\n return { rest, filters };\n};\n\nconst clamp = (value: number, min: number, max: number): number => {\n if (Number.isNaN(value)) return min;\n if (value < min) return min;\n if (value > max) return max;\n return value;\n};\n\nconst buildCropFilter = (\n crop: CropRect | CropData,\n dimensions?: Dimensions,\n): string | undefined => {\n if (!dimensions?.width || !dimensions?.height) return undefined;\n\n const cropWidth = Math.max(1, Math.round(dimensions.width * crop.width));\n const cropHeight = Math.max(1, Math.round(dimensions.height * crop.height));\n\n const maxX = Math.max(0, dimensions.width - cropWidth);\n const maxY = Math.max(0, dimensions.height - cropHeight);\n\n const x = clamp(Math.round(dimensions.width * crop.x), 0, maxX);\n const y = clamp(Math.round(dimensions.height * crop.y), 0, maxY);\n\n return `crop=${cropWidth}:${cropHeight}:${x}:${y}`;\n};\n\n/**\n * Build ffmpeg argument lists from preset args, injecting defaults such as CRF\n * and faststart flags. Crop instructions are folded into the video filter chain\n * while preserving any filters defined by the preset.\n */\nexport const buildFfmpegArgs = ({\n presetArgs,\n crop,\n dimensions,\n defaultCrf = 24,\n}: BuildArgsInput): BuildArgsResult => {\n const args = [...presetArgs];\n const { rest, filters } = extractFilters(args);\n\n if (!hasCrf(rest)) {\n rest.push(CRF_FLAG, String(defaultCrf));\n }\n\n if (!hasFaststart(rest)) {\n rest.push(...FASTSTART_FLAGS);\n }\n\n if (crop) {\n const cropFilter = buildCropFilter(crop, dimensions);\n if (cropFilter) {\n filters.push(cropFilter);\n }\n }\n\n if (filters.length > 0) {\n rest.push(\"-vf\", filters.join(\",\"));\n }\n\n return {\n globalOptions: [\"-y\"],\n outputOptions: rest,\n };\n};\n","import ffmpeg from \"fluent-ffmpeg\";\nimport ffprobeStatic from \"ffprobe-static\";\n\nexport type VideoMetadata = {\n width?: number;\n height?: number;\n duration?: number;\n bitrate?: number;\n};\n\nif (ffprobeStatic.path) {\n ffmpeg.setFfprobePath(ffprobeStatic.path);\n}\n\nexport const probeVideo = async (filePath: string): Promise<VideoMetadata> =>\n new Promise((resolve, reject) => {\n ffmpeg.ffprobe(filePath, (error, metadata) => {\n if (error) {\n reject(error);\n return;\n }\n\n const videoStream = metadata.streams.find(\n (stream) => stream.codec_type === \"video\",\n );\n const width = videoStream?.width;\n const height = videoStream?.height;\n\n const durationRaw = videoStream?.duration ?? metadata.format?.duration;\n const duration =\n typeof durationRaw !== \"undefined\" ? Number(durationRaw) : undefined;\n\n const bitrateRaw = videoStream?.bit_rate ?? metadata.format?.bit_rate;\n const bitrate =\n typeof bitrateRaw !== \"undefined\" ? Number(bitrateRaw) : undefined;\n\n resolve({\n width: width ?? undefined,\n height: height ?? undefined,\n duration: Number.isNaN(duration) ? undefined : duration,\n bitrate: Number.isNaN(bitrate) ? undefined : bitrate,\n });\n });\n });\n","import path from \"node:path\";\nimport type { ResolvePathsArgs, ResolvePathsResult } from \"../types\";\n\nconst normalizeUrl = (input?: string, filename?: string): string => {\n if (!input) return filename ?? \"\";\n const parts = input.split(\"?\");\n const base = parts[0];\n const query = parts[1] ? `?${parts.slice(1).join(\"?\")}` : \"\";\n const lastSlash = base.lastIndexOf(\"/\");\n if (lastSlash === -1) {\n return filename ?? base;\n }\n\n const prefix = base.slice(0, lastSlash);\n const sanitized = filename ?? base.slice(lastSlash + 1);\n return `${prefix}/${sanitized}${query}`;\n};\n\nexport const defaultResolvePaths = ({\n original,\n presetName,\n}: ResolvePathsArgs): ResolvePathsResult => {\n const originalFilename = original.filename ?? path.basename(original.path);\n const extension =\n path.extname(originalFilename) || path.extname(original.path) || \".mp4\";\n const baseName = path.basename(originalFilename, extension);\n const variantFilename = `${baseName}_${presetName}${extension || \".mp4\"}`;\n\n const originalDir = path.dirname(original.path);\n const absoluteDir = path.isAbsolute(original.path)\n ? originalDir\n : path.join(process.cwd(), originalDir);\n\n const url = normalizeUrl(original.url, variantFilename);\n\n return {\n dir: absoluteDir,\n filename: variantFilename,\n url,\n };\n};\n\nexport const buildStoredPath = (\n originalPath: string,\n variantFilename: string,\n): string => {\n const originalDir = path.dirname(originalPath);\n return path.join(originalDir, variantFilename);\n};\n\nexport const buildWritePath = (dir: string, filename: string): string =>\n path.join(dir, filename);\n","import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport payload, { type Payload, type PayloadRequest } from \"payload\";\nimport type { CollectionConfig, SanitizedCollectionConfig } from \"payload\";\n\nexport type PayloadClient = {\n findByID: (args: { collection: string; id: string }) => Promise<any>;\n update: (args: {\n collection: string;\n id: string;\n data: Record<string, any>;\n }) => Promise<any>;\n getCollectionConfig?: (\n slug: string,\n ) => (CollectionConfig | SanitizedCollectionConfig) | undefined;\n};\n\nlet cachedClient: PayloadClient | null = null;\nlet localInitialized = false;\n\nconst normalizeConfigPath = (configPath: string): string => {\n if (configPath.startsWith(\"file://\")) return configPath;\n return pathToFileURL(path.resolve(configPath)).href;\n};\n\nconst buildAuthHeaders = (token: string): Record<string, string> => ({\n Authorization: `Bearer ${token}`,\n \"X-Payload-API-Key\": token,\n});\n\nconst initLocalPayload = async (): Promise<PayloadClient | null> => {\n const secret = process.env.PAYLOAD_SECRET;\n const mongoURL = process.env.MONGODB_URI;\n\n if (!secret || !mongoURL) {\n return null;\n }\n\n try {\n const configPath = process.env.PAYLOAD_CONFIG_PATH;\n let configModule: unknown;\n\n if (configPath) {\n const imported = await import(normalizeConfigPath(configPath));\n configModule = imported?.default ?? imported;\n }\n\n if (!localInitialized) {\n const initOptions: Record<string, unknown> = {\n secret,\n mongoURL,\n local: true,\n };\n\n if (configModule) {\n initOptions.config = configModule;\n }\n\n await payload.init(initOptions as any);\n localInitialized = true;\n }\n\n const instance: Payload = payload;\n\n return {\n findByID: ({ collection, id }) =>\n instance.findByID({\n collection,\n id,\n }),\n update: ({ collection, id, data }) =>\n instance.update({\n collection,\n id,\n data,\n }),\n getCollectionConfig: (slug: string) =>\n instance.collections?.[slug]?.config,\n } satisfies PayloadClient;\n } catch (error) {\n console.warn(\n \"[video-processor] Failed to initialize Payload locally, falling back to REST client.\",\n error,\n );\n return null;\n }\n};\n\nconst initRestClient = async (): Promise<PayloadClient> => {\n const baseUrl =\n process.env.PAYLOAD_REST_URL ||\n process.env.PAYLOAD_PUBLIC_URL ||\n process.env.PAYLOAD_SERVER_URL;\n const token = process.env.PAYLOAD_ADMIN_TOKEN;\n\n if (!baseUrl || !token) {\n throw new Error(\n \"Unable to establish Payload REST client. Provide PAYLOAD_REST_URL (or PAYLOAD_PUBLIC_URL) and PAYLOAD_ADMIN_TOKEN.\",\n );\n }\n\n const base = baseUrl.replace(/\\/$/, \"\");\n const headers = buildAuthHeaders(token);\n\n const request = async <T>(url: string, init?: RequestInit): Promise<T> => {\n const response = await fetch(url, {\n ...init,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...headers,\n ...init?.headers,\n },\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `Payload REST request failed (${response.status}): ${text}`,\n );\n }\n\n return (await response.json()) as T;\n };\n\n return {\n findByID: async ({ collection, id }) => {\n const result = await request<{ doc?: any }>(\n `${base}/api/${collection}/${id}`,\n );\n return (result as any).doc ?? result;\n },\n update: async ({ collection, id, data }) => {\n const result = await request<{ doc?: any }>(\n `${base}/api/${collection}/${id}`,\n {\n method: \"PATCH\",\n body: JSON.stringify(data),\n },\n );\n return (result as any).doc ?? result;\n },\n } satisfies PayloadClient;\n};\n\nexport const getPayloadClient = async (): Promise<PayloadClient> => {\n if (cachedClient) return cachedClient;\n\n const localClient = await initLocalPayload();\n if (localClient) {\n cachedClient = localClient;\n return localClient;\n }\n\n const restClient = await initRestClient();\n cachedClient = restClient;\n return restClient;\n};\n\nexport const getCollectionConfigFromRequest = (\n req: PayloadRequest,\n slug: string,\n): CollectionConfig | null => {\n const payloadInstance = req.payload as Payload & {\n config?: Payload[\"config\"];\n };\n\n const collections = (payloadInstance as any)?.collections;\n const fromCollections = collections?.[slug];\n if (fromCollections) {\n if (typeof fromCollections.config === \"object\") {\n return fromCollections.config as CollectionConfig;\n }\n if (typeof fromCollections === \"object\") {\n return fromCollections as CollectionConfig;\n }\n }\n\n const configured = Array.isArray(payloadInstance.config?.collections)\n ? payloadInstance.config?.collections\n : undefined;\n\n if (configured) {\n const match = configured.find(\n (collection) => collection && collection.slug === slug,\n );\n if (match) {\n return match as CollectionConfig;\n }\n }\n\n return null;\n};\n","import path from \"node:path\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport ffmpeg from \"fluent-ffmpeg\";\nimport ffmpegStatic from \"ffmpeg-static\";\nimport ffprobeStatic from \"ffprobe-static\";\nimport { Worker } from \"bullmq\";\nimport IORedis from \"ioredis\";\nimport type { VideoPluginOptions, VariantRecord } from \"../types\";\nimport { ensureOptions } from \"../options\";\nimport { videoJobSchema, type VideoJobData } from \"./job.types\";\nimport { buildFfmpegArgs } from \"../ffmpeg/args\";\nimport { probeVideo } from \"../ffmpeg/probe\";\nimport {\n buildStoredPath,\n buildWritePath,\n defaultResolvePaths,\n} from \"../utils/paths\";\nimport { getPayloadClient } from \"../utils/payload\";\n\nconst envFfmpegPath = process.env.FFMPEG_BIN?.trim();\nconst ffmpegBinary =\n envFfmpegPath && envFfmpegPath.length > 0\n ? envFfmpegPath\n : typeof ffmpegStatic === \"string\"\n ? ffmpegStatic\n : null;\nif (ffmpegBinary) {\n ffmpeg.setFfmpegPath(ffmpegBinary);\n}\nif (ffprobeStatic.path) {\n ffmpeg.setFfprobePath(ffprobeStatic.path);\n}\n\nexport const createWorker = async (\n rawOptions: VideoPluginOptions,\n): Promise<Worker<VideoJobData>> => {\n const options = ensureOptions(rawOptions);\n const presets = options.presets;\n const queueName = options.queue?.name ?? \"video-transcode\";\n const concurrency = options.queue?.concurrency ?? 1;\n const redisUrl = options.queue?.redisUrl ?? process.env.REDIS_URL;\n\n const connection = redisUrl\n ? new IORedis(redisUrl, { maxRetriesPerRequest: null })\n : new IORedis({ maxRetriesPerRequest: null });\n\n const worker = new Worker<VideoJobData>(\n queueName,\n async (job) => {\n const parsed = videoJobSchema.parse(job.data);\n const preset = presets[parsed.preset];\n if (!preset) {\n throw new Error(`Unknown preset ${parsed.preset}`);\n }\n\n job.updateProgress(5);\n\n const client = await getPayloadClient();\n const document = await client.findByID({\n collection: parsed.collection,\n id: parsed.id,\n });\n\n if (!document) {\n throw new Error(\n `Document ${parsed.id} in collection ${parsed.collection} not found`,\n );\n }\n\n const originalPath: string | undefined = document?.path;\n const filename: string | undefined = document?.filename;\n const url: string | undefined = document?.url;\n\n if (!originalPath) {\n throw new Error(\"Source document does not expose a `path` property.\");\n }\n\n const absoluteInputPath = path.isAbsolute(originalPath)\n ? originalPath\n : path.join(process.cwd(), originalPath);\n\n const inputMetadata = await probeVideo(absoluteInputPath);\n job.updateProgress(15);\n\n const resolvePaths = options.resolvePaths ?? defaultResolvePaths;\n const collectionConfig =\n client.getCollectionConfig?.(parsed.collection) ?? null;\n\n const resolved = resolvePaths({\n doc: document,\n collection: collectionConfig,\n collectionSlug: parsed.collection,\n original: {\n filename: filename ?? path.basename(originalPath),\n path: originalPath,\n url: url ?? \"\",\n },\n presetName: parsed.preset,\n });\n\n const writeDir = resolved.dir;\n const writeFilename = resolved.filename;\n const targetUrl = resolved.url;\n const writePath = buildWritePath(writeDir, writeFilename);\n\n await mkdir(writeDir, { recursive: true });\n\n const { globalOptions, outputOptions } = buildFfmpegArgs({\n presetArgs: preset.args,\n crop: parsed.crop,\n dimensions: {\n width: inputMetadata.width,\n height: inputMetadata.height,\n },\n });\n\n await new Promise<void>((resolve, reject) => {\n const command = ffmpeg(absoluteInputPath);\n globalOptions.forEach((option) => command.addOption(option));\n command.outputOptions(outputOptions);\n command.output(writePath);\n command.on(\"progress\", (progress) => {\n if (typeof progress.percent === \"number\") {\n const bounded = Math.min(95, 15 + progress.percent * 0.7);\n void job.updateProgress(bounded);\n }\n });\n command.on(\"end\", () => resolve());\n command.on(\"error\", (error) => reject(error));\n command.run();\n });\n\n const fileStats = await stat(writePath);\n const outputMetadata = await probeVideo(writePath);\n\n const storedPath = buildStoredPath(originalPath, writeFilename);\n const variant: VariantRecord = {\n preset: parsed.preset,\n url: targetUrl,\n path: storedPath,\n size: fileStats.size,\n duration: outputMetadata.duration ?? inputMetadata.duration,\n width: outputMetadata.width ?? inputMetadata.width,\n height: outputMetadata.height ?? inputMetadata.height,\n bitrate: outputMetadata.bitrate,\n createdAt: new Date().toISOString(),\n };\n\n const existingVariants: VariantRecord[] = Array.isArray(document.variants)\n ? document.variants\n : [];\n\n const nextVariants = [\n ...existingVariants.filter((item) => item?.preset !== variant.preset),\n variant,\n ];\n\n await client.update({\n collection: parsed.collection,\n id: parsed.id,\n data: {\n variants: nextVariants,\n },\n });\n\n await job.updateProgress(100);\n\n return variant;\n },\n {\n connection,\n concurrency,\n },\n );\n\n worker.on(\"failed\", (job, error) => {\n console.error(`[video-processor] Job ${job?.id} failed`, error);\n });\n\n worker.on(\"completed\", (job) => {\n console.log(`[video-processor] Job ${job.id} completed`);\n });\n\n await worker.waitUntilReady();\n console.log(`[video-processor] Worker listening on queue ${queueName}`);\n\n const shutdown = async () => {\n await worker.close();\n await connection.quit();\n };\n\n process.once(\"SIGINT\", () => {\n void shutdown().then(() => process.exit(0));\n });\n process.once(\"SIGTERM\", () => {\n void shutdown().then(() => process.exit(0));\n });\n\n return worker;\n};\n","import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { VideoPluginOptions } from \"../types\";\nimport { createWorker } from \"./createWorker\";\n\nconst loadOptions = async (): Promise<VideoPluginOptions> => {\n const modulePath = process.env.PAYLOAD_VIDEO_WORKER_CONFIG;\n if (!modulePath) {\n throw new Error(\n \"PAYLOAD_VIDEO_WORKER_CONFIG must point to a module exporting plugin options.\",\n );\n }\n\n const resolved =\n modulePath.startsWith(\".\") || modulePath.startsWith(\"/\")\n ? pathToFileURL(path.resolve(modulePath)).href\n : modulePath;\n\n const imported = await import(resolved);\n const options = (imported.default ?? imported) as VideoPluginOptions;\n\n if (!options || typeof options !== \"object\" || !(\"presets\" in options)) {\n throw new Error(\n \"Invalid worker options module. Ensure it exports VideoPluginOptions.\",\n );\n }\n\n return options;\n};\n\nconst main = async () => {\n const options = await loadOptions();\n await createWorker(options);\n};\n\nvoid main().catch((error) => {\n console.error(\"[video-processor] Worker failed to start\", error);\n process.exit(1);\n});\n"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }