@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,691 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var zod = require('zod');
5
+ var ffmpeg2 = require('fluent-ffmpeg');
6
+ var ffprobeStatic = require('ffprobe-static');
7
+ var path = require('path');
8
+ var url = require('url');
9
+ var payload = require('payload');
10
+ var promises = require('fs/promises');
11
+ var ffmpegStatic = require('ffmpeg-static');
12
+ var bullmq = require('bullmq');
13
+ var IORedis = require('ioredis');
14
+ var fs = require('fs');
15
+ var dotenv = require('dotenv');
16
+
17
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
+
19
+ var ffmpeg2__default = /*#__PURE__*/_interopDefault(ffmpeg2);
20
+ var ffprobeStatic__default = /*#__PURE__*/_interopDefault(ffprobeStatic);
21
+ var path__default = /*#__PURE__*/_interopDefault(path);
22
+ var payload__default = /*#__PURE__*/_interopDefault(payload);
23
+ var ffmpegStatic__default = /*#__PURE__*/_interopDefault(ffmpegStatic);
24
+ var IORedis__default = /*#__PURE__*/_interopDefault(IORedis);
25
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
26
+ var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
27
+
28
+ var __defProp = Object.defineProperty;
29
+ var __getOwnPropNames = Object.getOwnPropertyNames;
30
+ var __esm = (fn, res) => function __init() {
31
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
32
+ };
33
+ var __export = (target, all) => {
34
+ for (var name in all)
35
+ __defProp(target, name, { get: all[name], enumerable: true });
36
+ };
37
+ var presetSchema, videoPluginOptionsSchema, ensureOptions;
38
+ var init_options = __esm({
39
+ "src/options.ts"() {
40
+ presetSchema = zod.z.object({
41
+ args: zod.z.array(zod.z.string()),
42
+ label: zod.z.string().optional(),
43
+ enableCrop: zod.z.boolean().optional()
44
+ });
45
+ videoPluginOptionsSchema = zod.z.object({
46
+ presets: zod.z.record(presetSchema).refine((value) => Object.keys(value).length > 0, {
47
+ message: "At least one preset must be defined."
48
+ }),
49
+ queue: zod.z.object({
50
+ name: zod.z.string().min(1).optional(),
51
+ redisUrl: zod.z.string().optional(),
52
+ concurrency: zod.z.number().int().positive().optional()
53
+ }).optional(),
54
+ access: zod.z.any().optional(),
55
+ resolvePaths: zod.z.any().optional()
56
+ });
57
+ ensureOptions = (options) => videoPluginOptionsSchema.parse(options);
58
+ }
59
+ });
60
+ var cropSchema, videoJobSchema;
61
+ var init_job_types = __esm({
62
+ "src/queue/job.types.ts"() {
63
+ cropSchema = zod.z.object({
64
+ x: zod.z.number().min(0).max(1),
65
+ y: zod.z.number().min(0).max(1),
66
+ width: zod.z.number().positive().max(1),
67
+ height: zod.z.number().positive().max(1)
68
+ }).refine((value) => value.width > 0 && value.height > 0, {
69
+ message: "Crop width and height must be > 0"
70
+ });
71
+ videoJobSchema = zod.z.object({
72
+ collection: zod.z.string().min(1),
73
+ id: zod.z.union([zod.z.string(), zod.z.number()]).transform((value) => value.toString()),
74
+ preset: zod.z.string().min(1),
75
+ crop: cropSchema.optional()
76
+ });
77
+ }
78
+ });
79
+
80
+ // src/ffmpeg/args.ts
81
+ var FASTSTART_FLAGS, CRF_FLAG, hasCrf, hasFaststart, extractFilters, clamp, buildCropFilter, buildFfmpegArgs;
82
+ var init_args = __esm({
83
+ "src/ffmpeg/args.ts"() {
84
+ FASTSTART_FLAGS = ["-movflags", "+faststart"];
85
+ CRF_FLAG = "-crf";
86
+ hasCrf = (args) => {
87
+ for (let i = 0; i < args.length; i += 1) {
88
+ if (args[i] === CRF_FLAG) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ };
94
+ hasFaststart = (args) => {
95
+ for (let i = 0; i < args.length; i += 1) {
96
+ if (args[i] === "-movflags") {
97
+ const value = args[i + 1];
98
+ if (typeof value === "string" && value.includes("faststart")) {
99
+ return true;
100
+ }
101
+ }
102
+ }
103
+ return false;
104
+ };
105
+ extractFilters = (args) => {
106
+ const rest = [];
107
+ const filters = [];
108
+ for (let i = 0; i < args.length; i += 1) {
109
+ const current = args[i];
110
+ if (current === "-vf" || current === "-filter:v") {
111
+ const value = args[i + 1];
112
+ if (typeof value === "string") {
113
+ filters.push(value);
114
+ }
115
+ i += 1;
116
+ } else {
117
+ rest.push(current);
118
+ }
119
+ }
120
+ return { rest, filters };
121
+ };
122
+ clamp = (value, min, max) => {
123
+ if (Number.isNaN(value)) return min;
124
+ if (value < min) return min;
125
+ if (value > max) return max;
126
+ return value;
127
+ };
128
+ buildCropFilter = (crop, dimensions) => {
129
+ if (!dimensions?.width || !dimensions?.height) return void 0;
130
+ const cropWidth = Math.max(1, Math.round(dimensions.width * crop.width));
131
+ const cropHeight = Math.max(1, Math.round(dimensions.height * crop.height));
132
+ const maxX = Math.max(0, dimensions.width - cropWidth);
133
+ const maxY = Math.max(0, dimensions.height - cropHeight);
134
+ const x = clamp(Math.round(dimensions.width * crop.x), 0, maxX);
135
+ const y = clamp(Math.round(dimensions.height * crop.y), 0, maxY);
136
+ return `crop=${cropWidth}:${cropHeight}:${x}:${y}`;
137
+ };
138
+ buildFfmpegArgs = ({
139
+ presetArgs,
140
+ crop,
141
+ dimensions,
142
+ defaultCrf = 24
143
+ }) => {
144
+ const args = [...presetArgs];
145
+ const { rest, filters } = extractFilters(args);
146
+ if (!hasCrf(rest)) {
147
+ rest.push(CRF_FLAG, String(defaultCrf));
148
+ }
149
+ if (!hasFaststart(rest)) {
150
+ rest.push(...FASTSTART_FLAGS);
151
+ }
152
+ if (crop) {
153
+ const cropFilter = buildCropFilter(crop, dimensions);
154
+ if (cropFilter) {
155
+ filters.push(cropFilter);
156
+ }
157
+ }
158
+ if (filters.length > 0) {
159
+ rest.push("-vf", filters.join(","));
160
+ }
161
+ return {
162
+ globalOptions: ["-y"],
163
+ outputOptions: rest
164
+ };
165
+ };
166
+ }
167
+ });
168
+ var probeVideo;
169
+ var init_probe = __esm({
170
+ "src/ffmpeg/probe.ts"() {
171
+ if (ffprobeStatic__default.default.path) {
172
+ ffmpeg2__default.default.setFfprobePath(ffprobeStatic__default.default.path);
173
+ }
174
+ probeVideo = async (filePath) => new Promise((resolve, reject) => {
175
+ ffmpeg2__default.default.ffprobe(filePath, (error, metadata) => {
176
+ if (error) {
177
+ reject(error);
178
+ return;
179
+ }
180
+ const videoStream = metadata.streams.find(
181
+ (stream) => stream.codec_type === "video"
182
+ );
183
+ const width = videoStream?.width;
184
+ const height = videoStream?.height;
185
+ const durationRaw = videoStream?.duration ?? metadata.format?.duration;
186
+ const duration = typeof durationRaw !== "undefined" ? Number(durationRaw) : void 0;
187
+ const bitrateRaw = videoStream?.bit_rate ?? metadata.format?.bit_rate;
188
+ const bitrate = typeof bitrateRaw !== "undefined" ? Number(bitrateRaw) : void 0;
189
+ resolve({
190
+ width: width ?? void 0,
191
+ height: height ?? void 0,
192
+ duration: Number.isNaN(duration) ? void 0 : duration,
193
+ bitrate: Number.isNaN(bitrate) ? void 0 : bitrate
194
+ });
195
+ });
196
+ });
197
+ }
198
+ });
199
+ var normalizeUrl, defaultResolvePaths, buildStoredPath, buildWritePath;
200
+ var init_paths = __esm({
201
+ "src/utils/paths.ts"() {
202
+ normalizeUrl = (input, filename) => {
203
+ if (!input) return filename ?? "";
204
+ const parts = input.split("?");
205
+ const base = parts[0];
206
+ const query = parts[1] ? `?${parts.slice(1).join("?")}` : "";
207
+ const lastSlash = base.lastIndexOf("/");
208
+ if (lastSlash === -1) {
209
+ return filename ?? base;
210
+ }
211
+ const prefix = base.slice(0, lastSlash);
212
+ const sanitized = filename ?? base.slice(lastSlash + 1);
213
+ return `${prefix}/${sanitized}${query}`;
214
+ };
215
+ defaultResolvePaths = ({
216
+ original,
217
+ presetName
218
+ }) => {
219
+ const originalFilename = original.filename ?? path__default.default.basename(original.path);
220
+ const extension = path__default.default.extname(originalFilename) || path__default.default.extname(original.path) || ".mp4";
221
+ const baseName = path__default.default.basename(originalFilename, extension);
222
+ const variantFilename = `${baseName}_${presetName}${extension}`;
223
+ const originalDir = path__default.default.dirname(original.path);
224
+ const absoluteDir = path__default.default.isAbsolute(original.path) ? originalDir : path__default.default.join(process.cwd(), originalDir);
225
+ const url = normalizeUrl(original.url, variantFilename);
226
+ return {
227
+ dir: absoluteDir,
228
+ filename: variantFilename,
229
+ url
230
+ };
231
+ };
232
+ buildStoredPath = (originalPath, variantFilename) => {
233
+ const originalDir = path__default.default.dirname(originalPath);
234
+ return path__default.default.join(originalDir, variantFilename);
235
+ };
236
+ buildWritePath = (dir, filename) => path__default.default.join(dir, filename);
237
+ }
238
+ });
239
+ var cachedClient, localInitialized, normalizeConfigPath, buildAuthHeaders, initLocalPayload, initRestClient, getPayloadClient;
240
+ var init_payload = __esm({
241
+ "src/utils/payload.ts"() {
242
+ cachedClient = null;
243
+ localInitialized = false;
244
+ normalizeConfigPath = (configPath) => {
245
+ if (configPath.startsWith("file://")) return configPath;
246
+ return url.pathToFileURL(path__default.default.resolve(configPath)).href;
247
+ };
248
+ buildAuthHeaders = (token) => ({
249
+ Authorization: `Bearer ${token}`,
250
+ "X-Payload-API-Key": token
251
+ });
252
+ initLocalPayload = async () => {
253
+ const secret = process.env.PAYLOAD_SECRET;
254
+ const mongoURL = process.env.MONGODB_URI;
255
+ if (!secret || !mongoURL) {
256
+ return null;
257
+ }
258
+ try {
259
+ const configPath = process.env.PAYLOAD_CONFIG_PATH;
260
+ let configModule;
261
+ if (configPath) {
262
+ const imported = await import(normalizeConfigPath(configPath));
263
+ configModule = imported?.default ?? imported;
264
+ }
265
+ if (!localInitialized) {
266
+ const initOptions = {
267
+ secret,
268
+ mongoURL,
269
+ local: true
270
+ };
271
+ if (configModule) {
272
+ initOptions.config = configModule;
273
+ }
274
+ await payload__default.default.init(initOptions);
275
+ localInitialized = true;
276
+ }
277
+ const instance = payload__default.default;
278
+ return {
279
+ findByID: ({ collection, id }) => instance.findByID({
280
+ collection,
281
+ id
282
+ }),
283
+ update: ({ collection, id, data }) => instance.update({
284
+ collection,
285
+ id,
286
+ data
287
+ }),
288
+ getCollectionConfig: (slug) => instance.collections?.[slug]?.config
289
+ };
290
+ } catch (error) {
291
+ console.warn(
292
+ "[video-processor] Failed to initialize Payload locally, falling back to REST client.",
293
+ error
294
+ );
295
+ return null;
296
+ }
297
+ };
298
+ initRestClient = async () => {
299
+ const baseUrl = process.env.PAYLOAD_REST_URL || process.env.PAYLOAD_PUBLIC_URL || process.env.PAYLOAD_SERVER_URL;
300
+ const token = process.env.PAYLOAD_ADMIN_TOKEN;
301
+ if (!baseUrl || !token) {
302
+ throw new Error(
303
+ "Unable to establish Payload REST client. Provide PAYLOAD_REST_URL (or PAYLOAD_PUBLIC_URL) and PAYLOAD_ADMIN_TOKEN."
304
+ );
305
+ }
306
+ const base = baseUrl.replace(/\/$/, "");
307
+ const headers = buildAuthHeaders(token);
308
+ const request = async (url, init) => {
309
+ const response = await fetch(url, {
310
+ ...init,
311
+ headers: {
312
+ "Content-Type": "application/json",
313
+ Accept: "application/json",
314
+ ...headers,
315
+ ...init?.headers
316
+ }
317
+ });
318
+ if (!response.ok) {
319
+ const text = await response.text();
320
+ throw new Error(
321
+ `Payload REST request failed (${response.status}): ${text}`
322
+ );
323
+ }
324
+ return await response.json();
325
+ };
326
+ return {
327
+ findByID: async ({ collection, id }) => {
328
+ const result = await request(
329
+ `${base}/api/${collection}/${id}`
330
+ );
331
+ return result.doc ?? result;
332
+ },
333
+ update: async ({ collection, id, data }) => {
334
+ const result = await request(
335
+ `${base}/api/${collection}/${id}`,
336
+ {
337
+ method: "PATCH",
338
+ body: JSON.stringify(data)
339
+ }
340
+ );
341
+ return result.doc ?? result;
342
+ }
343
+ };
344
+ };
345
+ getPayloadClient = async () => {
346
+ if (cachedClient) return cachedClient;
347
+ const localClient = await initLocalPayload();
348
+ if (localClient) {
349
+ cachedClient = localClient;
350
+ return localClient;
351
+ }
352
+ const restClient = await initRestClient();
353
+ cachedClient = restClient;
354
+ return restClient;
355
+ };
356
+ }
357
+ });
358
+
359
+ // src/queue/createWorker.ts
360
+ var createWorker_exports = {};
361
+ __export(createWorker_exports, {
362
+ createWorker: () => createWorker
363
+ });
364
+ var envFfmpegPath, ffmpegBinary, createWorker;
365
+ var init_createWorker = __esm({
366
+ "src/queue/createWorker.ts"() {
367
+ init_options();
368
+ init_job_types();
369
+ init_args();
370
+ init_probe();
371
+ init_paths();
372
+ init_payload();
373
+ envFfmpegPath = process.env.FFMPEG_BIN?.trim();
374
+ ffmpegBinary = envFfmpegPath && envFfmpegPath.length > 0 ? envFfmpegPath : typeof ffmpegStatic__default.default === "string" ? ffmpegStatic__default.default : null;
375
+ if (ffmpegBinary) {
376
+ ffmpeg2__default.default.setFfmpegPath(ffmpegBinary);
377
+ }
378
+ if (ffprobeStatic__default.default.path) {
379
+ ffmpeg2__default.default.setFfprobePath(ffprobeStatic__default.default.path);
380
+ }
381
+ createWorker = async (rawOptions) => {
382
+ const options = ensureOptions(rawOptions);
383
+ const presets = options.presets;
384
+ const queueName = options.queue?.name ?? "video-transcode";
385
+ const concurrency = options.queue?.concurrency ?? 1;
386
+ const redisUrl = options.queue?.redisUrl ?? process.env.REDIS_URL;
387
+ const connection = redisUrl ? new IORedis__default.default(redisUrl, { maxRetriesPerRequest: null }) : new IORedis__default.default({ maxRetriesPerRequest: null });
388
+ const worker = new bullmq.Worker(
389
+ queueName,
390
+ async (job) => {
391
+ const parsed = videoJobSchema.parse(job.data);
392
+ const preset = presets[parsed.preset];
393
+ if (!preset) {
394
+ throw new Error(`Unknown preset ${parsed.preset}`);
395
+ }
396
+ job.updateProgress(5);
397
+ const client = await getPayloadClient();
398
+ const document = await client.findByID({
399
+ collection: parsed.collection,
400
+ id: parsed.id
401
+ });
402
+ if (!document) {
403
+ throw new Error(
404
+ `Document ${parsed.id} in collection ${parsed.collection} not found`
405
+ );
406
+ }
407
+ const originalPath = document?.path;
408
+ const filename = document?.filename;
409
+ const url = document?.url;
410
+ if (!originalPath) {
411
+ throw new Error("Source document does not expose a `path` property.");
412
+ }
413
+ const absoluteInputPath = path__default.default.isAbsolute(originalPath) ? originalPath : path__default.default.join(process.cwd(), originalPath);
414
+ const inputMetadata = await probeVideo(absoluteInputPath);
415
+ job.updateProgress(15);
416
+ const resolvePaths = options.resolvePaths ?? defaultResolvePaths;
417
+ const collectionConfig = client.getCollectionConfig?.(parsed.collection) ?? null;
418
+ const resolved = resolvePaths({
419
+ doc: document,
420
+ collection: collectionConfig,
421
+ collectionSlug: parsed.collection,
422
+ original: {
423
+ filename: filename ?? path__default.default.basename(originalPath),
424
+ path: originalPath,
425
+ url: url ?? ""
426
+ },
427
+ presetName: parsed.preset
428
+ });
429
+ const writeDir = resolved.dir;
430
+ const writeFilename = resolved.filename;
431
+ const targetUrl = resolved.url;
432
+ const writePath = buildWritePath(writeDir, writeFilename);
433
+ await promises.mkdir(writeDir, { recursive: true });
434
+ const { globalOptions, outputOptions } = buildFfmpegArgs({
435
+ presetArgs: preset.args,
436
+ crop: parsed.crop,
437
+ dimensions: {
438
+ width: inputMetadata.width,
439
+ height: inputMetadata.height
440
+ }
441
+ });
442
+ await new Promise((resolve, reject) => {
443
+ const command = ffmpeg2__default.default(absoluteInputPath);
444
+ globalOptions.forEach((option) => command.addOption(option));
445
+ command.outputOptions(outputOptions);
446
+ command.output(writePath);
447
+ command.on("progress", (progress) => {
448
+ if (typeof progress.percent === "number") {
449
+ const bounded = Math.min(95, 15 + progress.percent * 0.7);
450
+ void job.updateProgress(bounded);
451
+ }
452
+ });
453
+ command.on("end", () => resolve());
454
+ command.on("error", (error) => reject(error));
455
+ command.run();
456
+ });
457
+ const fileStats = await promises.stat(writePath);
458
+ const outputMetadata = await probeVideo(writePath);
459
+ const storedPath = buildStoredPath(originalPath, writeFilename);
460
+ const variant = {
461
+ preset: parsed.preset,
462
+ url: targetUrl,
463
+ path: storedPath,
464
+ size: fileStats.size,
465
+ duration: outputMetadata.duration ?? inputMetadata.duration,
466
+ width: outputMetadata.width ?? inputMetadata.width,
467
+ height: outputMetadata.height ?? inputMetadata.height,
468
+ bitrate: outputMetadata.bitrate,
469
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
470
+ };
471
+ const existingVariants = Array.isArray(document.variants) ? document.variants : [];
472
+ const nextVariants = [
473
+ ...existingVariants.filter((item) => item?.preset !== variant.preset),
474
+ variant
475
+ ];
476
+ await client.update({
477
+ collection: parsed.collection,
478
+ id: parsed.id,
479
+ data: {
480
+ variants: nextVariants
481
+ }
482
+ });
483
+ await job.updateProgress(100);
484
+ return variant;
485
+ },
486
+ {
487
+ connection,
488
+ concurrency
489
+ }
490
+ );
491
+ worker.on("failed", (job, error) => {
492
+ console.error(`[video-processor] Job ${job?.id} failed`, error);
493
+ });
494
+ worker.on("completed", (job) => {
495
+ console.log(`[video-processor] Job ${job.id} completed`);
496
+ });
497
+ await worker.waitUntilReady();
498
+ console.log(`[video-processor] Worker listening on queue ${queueName}`);
499
+ const shutdown = async () => {
500
+ await worker.close();
501
+ await connection.quit();
502
+ };
503
+ process.once("SIGINT", () => {
504
+ void shutdown().then(() => process.exit(0));
505
+ });
506
+ process.once("SIGTERM", () => {
507
+ void shutdown().then(() => process.exit(0));
508
+ });
509
+ return worker;
510
+ };
511
+ }
512
+ });
513
+ var helpText = `
514
+ payload-video-worker
515
+
516
+ Start the Payload video processing worker using the same options you pass to the plugin.
517
+
518
+ Usage:
519
+ payload-video-worker --config ./dist-config/videoPluginOptions.js [options]
520
+
521
+ Options:
522
+ -c, --config <path> Path to a module exporting the worker options (defaults to PAYLOAD_VIDEO_WORKER_CONFIG)
523
+ -p, --payload-config <path> Path to a Payload config file for local execution (sets PAYLOAD_CONFIG_PATH)
524
+ -s, --static-dir <path> Override STATIC_DIR before starting the worker
525
+ -e, --env <path> Load additional .env file (can be repeated)
526
+ --no-default-env Skip automatic loading of .env, .env.local, .env.development, .env.production
527
+ -h, --help Show this message and exit
528
+ `.trim();
529
+ var parseArgs = (argv) => {
530
+ const options = {
531
+ envFiles: [],
532
+ loadDefaultEnv: true
533
+ };
534
+ for (let index = 0; index < argv.length; index += 1) {
535
+ const arg = argv[index];
536
+ switch (arg) {
537
+ case "-c":
538
+ case "--config": {
539
+ const next = argv[index + 1];
540
+ if (!next) {
541
+ throw new Error("Missing value for --config option.");
542
+ }
543
+ options.configPath = next;
544
+ index += 1;
545
+ break;
546
+ }
547
+ case "-p":
548
+ case "--payload-config": {
549
+ const next = argv[index + 1];
550
+ if (!next) {
551
+ throw new Error("Missing value for --payload-config option.");
552
+ }
553
+ options.payloadConfigPath = next;
554
+ index += 1;
555
+ break;
556
+ }
557
+ case "-s":
558
+ case "--static-dir": {
559
+ const next = argv[index + 1];
560
+ if (!next) {
561
+ throw new Error("Missing value for --static-dir option.");
562
+ }
563
+ options.staticDir = next;
564
+ index += 1;
565
+ break;
566
+ }
567
+ case "-e":
568
+ case "--env": {
569
+ const next = argv[index + 1];
570
+ if (!next) {
571
+ throw new Error("Missing value for --env option.");
572
+ }
573
+ options.envFiles.push(next);
574
+ index += 1;
575
+ break;
576
+ }
577
+ case "--no-default-env": {
578
+ options.loadDefaultEnv = false;
579
+ break;
580
+ }
581
+ case "-h":
582
+ case "--help": {
583
+ options.showHelp = true;
584
+ break;
585
+ }
586
+ default: {
587
+ if (arg.startsWith("-")) {
588
+ throw new Error(
589
+ `Unknown option \`${arg}\`. Use --help to list supported flags.`
590
+ );
591
+ }
592
+ options.envFiles.push(arg);
593
+ }
594
+ }
595
+ }
596
+ return options;
597
+ };
598
+ var printHelp = () => {
599
+ console.log(helpText);
600
+ };
601
+ var resolvePath = (input) => path__default.default.isAbsolute(input) ? input : path__default.default.resolve(process.cwd(), input);
602
+ var loadEnvFile = (filePath) => {
603
+ const resolved = resolvePath(filePath);
604
+ if (!fs__default.default.existsSync(resolved)) {
605
+ return false;
606
+ }
607
+ dotenv__default.default.config({ path: resolved, override: false });
608
+ console.log(`[video-processor] Loaded env file ${resolved}`);
609
+ return true;
610
+ };
611
+ var ensureEnvVariables = () => {
612
+ if (!process.env.MONGODB_URI && process.env.DATABASE_URI) {
613
+ process.env.MONGODB_URI = process.env.DATABASE_URI;
614
+ }
615
+ if (!process.env.PAYLOAD_SECRET) {
616
+ process.env.PAYLOAD_SECRET = "dev-secret";
617
+ }
618
+ };
619
+ var toModuleUrl = (input) => url.pathToFileURL(resolvePath(input)).href;
620
+ var loadWorkerOptions = async (modulePath) => {
621
+ const moduleUrl = toModuleUrl(modulePath);
622
+ const imported = await import(moduleUrl);
623
+ const value = imported?.default ?? imported;
624
+ if (!value || typeof value !== "object") {
625
+ throw new Error(
626
+ `Worker options module at ${modulePath} did not export a configuration object.`
627
+ );
628
+ }
629
+ return value;
630
+ };
631
+ void (async () => {
632
+ try {
633
+ const options = parseArgs(process.argv.slice(2));
634
+ if (options.showHelp) {
635
+ printHelp();
636
+ process.exit(0);
637
+ }
638
+ const defaultEnvFiles = [
639
+ ".env",
640
+ ".env.local",
641
+ ".env.development",
642
+ ".env.production"
643
+ ];
644
+ if (options.loadDefaultEnv) {
645
+ for (const candidate of defaultEnvFiles) {
646
+ loadEnvFile(candidate);
647
+ }
648
+ }
649
+ for (const envFile of options.envFiles) {
650
+ loadEnvFile(envFile);
651
+ }
652
+ if (options.staticDir) {
653
+ process.env.STATIC_DIR = resolvePath(options.staticDir);
654
+ console.log(
655
+ `[video-processor] Using STATIC_DIR=${process.env.STATIC_DIR}`
656
+ );
657
+ }
658
+ if (options.payloadConfigPath) {
659
+ const resolvedPayloadConfig = resolvePath(options.payloadConfigPath);
660
+ process.env.PAYLOAD_CONFIG_PATH = resolvedPayloadConfig;
661
+ console.log(
662
+ `[video-processor] Using PAYLOAD_CONFIG_PATH=${resolvedPayloadConfig}`
663
+ );
664
+ }
665
+ const configPath = options.configPath ?? process.env.PAYLOAD_VIDEO_WORKER_CONFIG;
666
+ if (!configPath) {
667
+ throw new Error(
668
+ "No worker config provided. Pass --config or set PAYLOAD_VIDEO_WORKER_CONFIG."
669
+ );
670
+ }
671
+ process.env.PAYLOAD_VIDEO_WORKER_CONFIG = resolvePath(configPath);
672
+ ensureEnvVariables();
673
+ const workerOptions = await loadWorkerOptions(
674
+ process.env.PAYLOAD_VIDEO_WORKER_CONFIG
675
+ );
676
+ const { createWorker: createWorker2 } = await Promise.resolve().then(() => (init_createWorker(), createWorker_exports));
677
+ await createWorker2(workerOptions);
678
+ } catch (error) {
679
+ if (error instanceof Error) {
680
+ console.error("[video-processor] Worker failed to start:", error.message);
681
+ if (error.stack) {
682
+ console.error(error.stack);
683
+ }
684
+ } else {
685
+ console.error("[video-processor] Worker failed to start:", error);
686
+ }
687
+ process.exit(1);
688
+ }
689
+ })();
690
+ //# sourceMappingURL=start-worker.cjs.map
691
+ //# sourceMappingURL=start-worker.cjs.map