@openinary/core 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/LICENSE +661 -0
- package/dist/config/deps.d.ts +14 -0
- package/dist/config/deps.d.ts.map +1 -0
- package/dist/config/deps.js +2 -0
- package/dist/config/deps.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/authenticated.d.ts +4 -0
- package/dist/routes/authenticated.d.ts.map +1 -0
- package/dist/routes/authenticated.js +130 -0
- package/dist/routes/authenticated.js.map +1 -0
- package/dist/routes/download-folder.d.ts +4 -0
- package/dist/routes/download-folder.d.ts.map +1 -0
- package/dist/routes/download-folder.js +101 -0
- package/dist/routes/download-folder.js.map +1 -0
- package/dist/routes/download-zip.d.ts +15 -0
- package/dist/routes/download-zip.d.ts.map +1 -0
- package/dist/routes/download-zip.js +147 -0
- package/dist/routes/download-zip.js.map +1 -0
- package/dist/routes/download.d.ts +4 -0
- package/dist/routes/download.d.ts.map +1 -0
- package/dist/routes/download.js +83 -0
- package/dist/routes/download.js.map +1 -0
- package/dist/routes/invalidate.d.ts +4 -0
- package/dist/routes/invalidate.d.ts.map +1 -0
- package/dist/routes/invalidate.js +114 -0
- package/dist/routes/invalidate.js.map +1 -0
- package/dist/routes/queue-events.d.ts +4 -0
- package/dist/routes/queue-events.d.ts.map +1 -0
- package/dist/routes/queue-events.js +125 -0
- package/dist/routes/queue-events.js.map +1 -0
- package/dist/routes/queue.d.ts +4 -0
- package/dist/routes/queue.d.ts.map +1 -0
- package/dist/routes/queue.js +130 -0
- package/dist/routes/queue.js.map +1 -0
- package/dist/routes/storage.d.ts +4 -0
- package/dist/routes/storage.d.ts.map +1 -0
- package/dist/routes/storage.js +694 -0
- package/dist/routes/storage.js.map +1 -0
- package/dist/routes/transform-helpers.d.ts +62 -0
- package/dist/routes/transform-helpers.d.ts.map +1 -0
- package/dist/routes/transform-helpers.js +372 -0
- package/dist/routes/transform-helpers.js.map +1 -0
- package/dist/routes/transform.d.ts +4 -0
- package/dist/routes/transform.d.ts.map +1 -0
- package/dist/routes/transform.js +65 -0
- package/dist/routes/transform.js.map +1 -0
- package/dist/routes/video-status.d.ts +4 -0
- package/dist/routes/video-status.d.ts.map +1 -0
- package/dist/routes/video-status.js +208 -0
- package/dist/routes/video-status.js.map +1 -0
- package/dist/services/transform.service.d.ts +71 -0
- package/dist/services/transform.service.d.ts.map +1 -0
- package/dist/services/transform.service.js +362 -0
- package/dist/services/transform.service.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/asset-deletion.d.ts +16 -0
- package/dist/utils/asset-deletion.d.ts.map +1 -0
- package/dist/utils/asset-deletion.js +131 -0
- package/dist/utils/asset-deletion.js.map +1 -0
- package/dist/utils/cache.d.ts +44 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +248 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/get-unique-file-path.d.ts +8 -0
- package/dist/utils/get-unique-file-path.d.ts.map +1 -0
- package/dist/utils/get-unique-file-path.js +26 -0
- package/dist/utils/get-unique-file-path.js.map +1 -0
- package/dist/utils/image/aspect-ratio.d.ts +7 -0
- package/dist/utils/image/aspect-ratio.d.ts.map +1 -0
- package/dist/utils/image/aspect-ratio.js +40 -0
- package/dist/utils/image/aspect-ratio.js.map +1 -0
- package/dist/utils/image/background.d.ts +6 -0
- package/dist/utils/image/background.d.ts.map +1 -0
- package/dist/utils/image/background.js +22 -0
- package/dist/utils/image/background.js.map +1 -0
- package/dist/utils/image/compression.d.ts +44 -0
- package/dist/utils/image/compression.d.ts.map +1 -0
- package/dist/utils/image/compression.js +345 -0
- package/dist/utils/image/compression.js.map +1 -0
- package/dist/utils/image/gravity.d.ts +6 -0
- package/dist/utils/image/gravity.d.ts.map +1 -0
- package/dist/utils/image/gravity.js +24 -0
- package/dist/utils/image/gravity.js.map +1 -0
- package/dist/utils/image/index.d.ts +8 -0
- package/dist/utils/image/index.d.ts.map +1 -0
- package/dist/utils/image/index.js +77 -0
- package/dist/utils/image/index.js.map +1 -0
- package/dist/utils/image/param-registry.d.ts +63 -0
- package/dist/utils/image/param-registry.d.ts.map +1 -0
- package/dist/utils/image/param-registry.js +196 -0
- package/dist/utils/image/param-registry.js.map +1 -0
- package/dist/utils/image/quality.d.ts +8 -0
- package/dist/utils/image/quality.d.ts.map +1 -0
- package/dist/utils/image/quality.js +9 -0
- package/dist/utils/image/quality.js.map +1 -0
- package/dist/utils/image/resize.d.ts +7 -0
- package/dist/utils/image/resize.d.ts.map +1 -0
- package/dist/utils/image/resize.js +62 -0
- package/dist/utils/image/resize.js.map +1 -0
- package/dist/utils/image/rotation.d.ts +6 -0
- package/dist/utils/image/rotation.d.ts.map +1 -0
- package/dist/utils/image/rotation.js +20 -0
- package/dist/utils/image/rotation.js.map +1 -0
- package/dist/utils/image/round-corners.d.ts +12 -0
- package/dist/utils/image/round-corners.d.ts.map +1 -0
- package/dist/utils/image/round-corners.js +102 -0
- package/dist/utils/image/round-corners.js.map +1 -0
- package/dist/utils/image/types.d.ts +2 -0
- package/dist/utils/image/types.d.ts.map +1 -0
- package/dist/utils/image/types.js +2 -0
- package/dist/utils/image/types.js.map +1 -0
- package/dist/utils/logger.d.ts +21 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +82 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/parser.d.ts +8 -0
- package/dist/utils/parser.d.ts.map +1 -0
- package/dist/utils/parser.js +226 -0
- package/dist/utils/parser.js.map +1 -0
- package/dist/utils/signature.d.ts +35 -0
- package/dist/utils/signature.d.ts.map +1 -0
- package/dist/utils/signature.js +100 -0
- package/dist/utils/signature.js.map +1 -0
- package/dist/utils/storage/cache.d.ts +41 -0
- package/dist/utils/storage/cache.d.ts.map +1 -0
- package/dist/utils/storage/cache.js +90 -0
- package/dist/utils/storage/cache.js.map +1 -0
- package/dist/utils/storage/cloud-storage.d.ts +188 -0
- package/dist/utils/storage/cloud-storage.d.ts.map +1 -0
- package/dist/utils/storage/cloud-storage.js +514 -0
- package/dist/utils/storage/cloud-storage.js.map +1 -0
- package/dist/utils/storage/factory.d.ts +10 -0
- package/dist/utils/storage/factory.d.ts.map +1 -0
- package/dist/utils/storage/factory.js +17 -0
- package/dist/utils/storage/factory.js.map +1 -0
- package/dist/utils/storage/index.d.ts +7 -0
- package/dist/utils/storage/index.d.ts.map +1 -0
- package/dist/utils/storage/index.js +8 -0
- package/dist/utils/storage/index.js.map +1 -0
- package/dist/utils/storage/key-generator.d.ts +11 -0
- package/dist/utils/storage/key-generator.d.ts.map +1 -0
- package/dist/utils/storage/key-generator.js +23 -0
- package/dist/utils/storage/key-generator.js.map +1 -0
- package/dist/utils/storage/listing-cache.d.ts +14 -0
- package/dist/utils/storage/listing-cache.d.ts.map +1 -0
- package/dist/utils/storage/listing-cache.js +29 -0
- package/dist/utils/storage/listing-cache.js.map +1 -0
- package/dist/utils/storage/s3-client.d.ts +97 -0
- package/dist/utils/storage/s3-client.d.ts.map +1 -0
- package/dist/utils/storage/s3-client.js +297 -0
- package/dist/utils/storage/s3-client.js.map +1 -0
- package/dist/utils/storage/stats-tracker.d.ts +45 -0
- package/dist/utils/storage/stats-tracker.d.ts.map +1 -0
- package/dist/utils/storage/stats-tracker.js +97 -0
- package/dist/utils/storage/stats-tracker.js.map +1 -0
- package/dist/utils/storage/types.d.ts +2 -0
- package/dist/utils/storage/types.d.ts.map +1 -0
- package/dist/utils/storage/types.js +2 -0
- package/dist/utils/storage/types.js.map +1 -0
- package/dist/utils/storage-level.d.ts +66 -0
- package/dist/utils/storage-level.d.ts.map +1 -0
- package/dist/utils/storage-level.js +106 -0
- package/dist/utils/storage-level.js.map +1 -0
- package/dist/utils/storage-tree.d.ts +30 -0
- package/dist/utils/storage-tree.d.ts.map +1 -0
- package/dist/utils/storage-tree.js +81 -0
- package/dist/utils/storage-tree.js.map +1 -0
- package/dist/utils/upload-signature.d.ts +25 -0
- package/dist/utils/upload-signature.d.ts.map +1 -0
- package/dist/utils/upload-signature.js +56 -0
- package/dist/utils/upload-signature.js.map +1 -0
- package/dist/utils/video/auto-downscale.d.ts +12 -0
- package/dist/utils/video/auto-downscale.d.ts.map +1 -0
- package/dist/utils/video/auto-downscale.js +29 -0
- package/dist/utils/video/auto-downscale.js.map +1 -0
- package/dist/utils/video/command-builder.d.ts +22 -0
- package/dist/utils/video/command-builder.d.ts.map +1 -0
- package/dist/utils/video/command-builder.js +79 -0
- package/dist/utils/video/command-builder.js.map +1 -0
- package/dist/utils/video/config.d.ts +51 -0
- package/dist/utils/video/config.d.ts.map +1 -0
- package/dist/utils/video/config.js +170 -0
- package/dist/utils/video/config.js.map +1 -0
- package/dist/utils/video/format.d.ts +22 -0
- package/dist/utils/video/format.d.ts.map +1 -0
- package/dist/utils/video/format.js +64 -0
- package/dist/utils/video/format.js.map +1 -0
- package/dist/utils/video/index.d.ts +25 -0
- package/dist/utils/video/index.d.ts.map +1 -0
- package/dist/utils/video/index.js +104 -0
- package/dist/utils/video/index.js.map +1 -0
- package/dist/utils/video/param-registry.d.ts +64 -0
- package/dist/utils/video/param-registry.d.ts.map +1 -0
- package/dist/utils/video/param-registry.js +201 -0
- package/dist/utils/video/param-registry.js.map +1 -0
- package/dist/utils/video/quality.d.ts +12 -0
- package/dist/utils/video/quality.d.ts.map +1 -0
- package/dist/utils/video/quality.js +45 -0
- package/dist/utils/video/quality.js.map +1 -0
- package/dist/utils/video/queue-store.d.ts +51 -0
- package/dist/utils/video/queue-store.d.ts.map +1 -0
- package/dist/utils/video/queue-store.js +2 -0
- package/dist/utils/video/queue-store.js.map +1 -0
- package/dist/utils/video/resize.d.ts +7 -0
- package/dist/utils/video/resize.d.ts.map +1 -0
- package/dist/utils/video/resize.js +40 -0
- package/dist/utils/video/resize.js.map +1 -0
- package/dist/utils/video/sqlite-video-job-store.d.ts +29 -0
- package/dist/utils/video/sqlite-video-job-store.d.ts.map +1 -0
- package/dist/utils/video/sqlite-video-job-store.js +320 -0
- package/dist/utils/video/sqlite-video-job-store.js.map +1 -0
- package/dist/utils/video/thumbnail.d.ts +7 -0
- package/dist/utils/video/thumbnail.d.ts.map +1 -0
- package/dist/utils/video/thumbnail.js +22 -0
- package/dist/utils/video/thumbnail.js.map +1 -0
- package/dist/utils/video/trim.d.ts +7 -0
- package/dist/utils/video/trim.d.ts.map +1 -0
- package/dist/utils/video/trim.js +36 -0
- package/dist/utils/video/trim.js.map +1 -0
- package/dist/utils/video/types.d.ts +19 -0
- package/dist/utils/video/types.d.ts.map +1 -0
- package/dist/utils/video/types.js +2 -0
- package/dist/utils/video/types.js.map +1 -0
- package/dist/utils/video/video-info.d.ts +25 -0
- package/dist/utils/video/video-info.d.ts.map +1 -0
- package/dist/utils/video/video-info.js +58 -0
- package/dist/utils/video/video-info.js.map +1 -0
- package/dist/utils/video/video-worker.d.ts +52 -0
- package/dist/utils/video/video-worker.d.ts.map +1 -0
- package/dist/utils/video/video-worker.js +192 -0
- package/dist/utils/video/video-worker.js.map +1 -0
- package/dist/utils/video-job-queue.d.ts +62 -0
- package/dist/utils/video-job-queue.d.ts.map +1 -0
- package/dist/utils/video-job-queue.js +122 -0
- package/dist/utils/video-job-queue.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video processing queue configuration
|
|
3
|
+
* These values can be overridden via environment variables
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
const CGROUP_V2_MAX_PATH = "/sys/fs/cgroup/memory.max";
|
|
8
|
+
const CGROUP_V1_LIMIT_PATH = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
|
|
9
|
+
// cgroup v1 reports this (or similar huge values) when no limit is set
|
|
10
|
+
const CGROUP_V1_UNLIMITED_THRESHOLD = 1e18;
|
|
11
|
+
const CGROUP_V2_CPU_MAX_PATH = "/sys/fs/cgroup/cpu.max";
|
|
12
|
+
const CGROUP_V1_CPU_QUOTA_PATH = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us";
|
|
13
|
+
const CGROUP_V1_CPU_PERIOD_PATH = "/sys/fs/cgroup/cpu/cpu.cfs_period_us";
|
|
14
|
+
/**
|
|
15
|
+
* Read the container memory limit from cgroup v2 or v1, if present.
|
|
16
|
+
* Returns null when no cgroup file is found or the limit is "unlimited".
|
|
17
|
+
*/
|
|
18
|
+
function readCgroupMemoryLimit() {
|
|
19
|
+
try {
|
|
20
|
+
const raw = fs.readFileSync(CGROUP_V2_MAX_PATH, "utf8").trim();
|
|
21
|
+
if (raw === "max")
|
|
22
|
+
return null;
|
|
23
|
+
const value = parseInt(raw, 10);
|
|
24
|
+
return Number.isFinite(value) ? value : null;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// cgroup v2 file not present, fall through to v1
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const raw = fs.readFileSync(CGROUP_V1_LIMIT_PATH, "utf8").trim();
|
|
31
|
+
const value = parseInt(raw, 10);
|
|
32
|
+
if (!Number.isFinite(value) || value >= CGROUP_V1_UNLIMITED_THRESHOLD)
|
|
33
|
+
return null;
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// cgroup v1 file not present either
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Effective memory available to the process: the lower of the host RAM
|
|
43
|
+
* and the container's cgroup memory limit (if one is set).
|
|
44
|
+
*/
|
|
45
|
+
function getEffectiveMemoryBytes() {
|
|
46
|
+
const cgroupLimit = readCgroupMemoryLimit();
|
|
47
|
+
const hostMemory = os.totalmem();
|
|
48
|
+
if (cgroupLimit !== null && cgroupLimit < hostMemory) {
|
|
49
|
+
return { bytes: cgroupLimit, source: "cgroup limit" };
|
|
50
|
+
}
|
|
51
|
+
return { bytes: hostMemory, source: "host RAM" };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read the container CPU limit from cgroup v2 or v1, if present.
|
|
55
|
+
* Returns the number of CPUs as a float (e.g. 0.5, 2), or null when
|
|
56
|
+
* no cgroup file is found or the limit is "unlimited".
|
|
57
|
+
*/
|
|
58
|
+
function readCgroupCpuLimit() {
|
|
59
|
+
try {
|
|
60
|
+
// cgroup v2 format: "<quota> <period>" or "max <period>"
|
|
61
|
+
const raw = fs.readFileSync(CGROUP_V2_CPU_MAX_PATH, "utf8").trim();
|
|
62
|
+
const [quotaRaw, periodRaw] = raw.split(/\s+/);
|
|
63
|
+
if (quotaRaw === "max")
|
|
64
|
+
return null;
|
|
65
|
+
const quota = parseInt(quotaRaw, 10);
|
|
66
|
+
const period = parseInt(periodRaw, 10);
|
|
67
|
+
if (Number.isFinite(quota) && Number.isFinite(period) && quota > 0 && period > 0) {
|
|
68
|
+
return quota / period;
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// cgroup v2 file not present, fall through to v1
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
// cgroup v1 reports quota -1 when no limit is set
|
|
77
|
+
const quota = parseInt(fs.readFileSync(CGROUP_V1_CPU_QUOTA_PATH, "utf8").trim(), 10);
|
|
78
|
+
const period = parseInt(fs.readFileSync(CGROUP_V1_CPU_PERIOD_PATH, "utf8").trim(), 10);
|
|
79
|
+
if (Number.isFinite(quota) && Number.isFinite(period) && quota > 0 && period > 0) {
|
|
80
|
+
return quota / period;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// cgroup v1 file not present either
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Effective CPU count available to the process: the lower of the host CPUs
|
|
90
|
+
* and the container's cgroup CPU quota (if one is set), floored to at least 1.
|
|
91
|
+
*/
|
|
92
|
+
function getEffectiveCpuCount() {
|
|
93
|
+
const cgroupLimit = readCgroupCpuLimit();
|
|
94
|
+
const hostCpus = os.cpus().length;
|
|
95
|
+
if (cgroupLimit !== null && cgroupLimit < hostCpus) {
|
|
96
|
+
return { count: Math.max(1, Math.floor(cgroupLimit)), source: "cgroup limit" };
|
|
97
|
+
}
|
|
98
|
+
return { count: hostCpus, source: "host CPUs" };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Detect optimal number of concurrent video jobs based on available resources.
|
|
102
|
+
* Memory: 1 worker per 2GB. CPU: at most 1 worker per effective CPU, so
|
|
103
|
+
* concurrent ffmpeg processes can never oversubscribe the container.
|
|
104
|
+
* Minimum 1, maximum 16.
|
|
105
|
+
*/
|
|
106
|
+
function detectOptimalConcurrency(memoryBytes, cpuCount) {
|
|
107
|
+
const memoryGB = memoryBytes / (1024 ** 3);
|
|
108
|
+
const byMemory = Math.max(1, Math.floor(memoryGB / 2));
|
|
109
|
+
// Cap at 16 workers to prevent excessive resource usage
|
|
110
|
+
return Math.max(1, Math.min(byMemory, cpuCount, 16));
|
|
111
|
+
}
|
|
112
|
+
const { bytes: effectiveMemoryBytes, source: memorySource } = getEffectiveMemoryBytes();
|
|
113
|
+
const { count: effectiveCpuCount, source: cpuSource } = getEffectiveCpuCount();
|
|
114
|
+
/**
|
|
115
|
+
* Effective CPU count (host CPUs or cgroup quota, whichever is lower)
|
|
116
|
+
*/
|
|
117
|
+
export const EFFECTIVE_CPU_COUNT = effectiveCpuCount;
|
|
118
|
+
/**
|
|
119
|
+
* Number of threads each ffmpeg process may use.
|
|
120
|
+
* Leaves one CPU free for the HTTP serving event loop so video jobs never
|
|
121
|
+
* starve the API, or configured via VIDEO_FFMPEG_THREADS env variable.
|
|
122
|
+
*/
|
|
123
|
+
export const FFMPEG_THREADS = process.env.VIDEO_FFMPEG_THREADS
|
|
124
|
+
? Math.max(1, parseInt(process.env.VIDEO_FFMPEG_THREADS, 10) || 1)
|
|
125
|
+
: Math.max(1, effectiveCpuCount - 1);
|
|
126
|
+
/**
|
|
127
|
+
* Niceness applied to spawned ffmpeg processes (0-19, higher = lower priority).
|
|
128
|
+
* Keeps the Node event loop responsive even when ffmpeg saturates the CPU.
|
|
129
|
+
* Configurable via VIDEO_FFMPEG_NICENESS env variable.
|
|
130
|
+
*/
|
|
131
|
+
export const FFMPEG_NICENESS = process.env.VIDEO_FFMPEG_NICENESS
|
|
132
|
+
? parseInt(process.env.VIDEO_FFMPEG_NICENESS, 10)
|
|
133
|
+
: 15;
|
|
134
|
+
/**
|
|
135
|
+
* Maximum number of concurrent video jobs
|
|
136
|
+
* Auto-detected based on available memory and CPU (host or cgroup limit,
|
|
137
|
+
* whichever is lower), or configured via VIDEO_MAX_CONCURRENT env variable
|
|
138
|
+
*/
|
|
139
|
+
export const MAX_CONCURRENT_JOBS = process.env.VIDEO_MAX_CONCURRENT
|
|
140
|
+
? parseInt(process.env.VIDEO_MAX_CONCURRENT, 10)
|
|
141
|
+
: detectOptimalConcurrency(effectiveMemoryBytes, effectiveCpuCount);
|
|
142
|
+
// Log detected configuration on module load
|
|
143
|
+
const effectiveMemoryGB = (effectiveMemoryBytes / (1024 ** 3)).toFixed(2);
|
|
144
|
+
const configSource = process.env.VIDEO_MAX_CONCURRENT ? "env variable" : `${memorySource}, ${cpuSource}`;
|
|
145
|
+
console.log(`[Video Config] Memory: ${effectiveMemoryGB}GB (${memorySource}) | CPUs: ${effectiveCpuCount} (${cpuSource}) | Max concurrent jobs: ${MAX_CONCURRENT_JOBS} (${configSource}) | ffmpeg threads: ${FFMPEG_THREADS}, niceness: ${FFMPEG_NICENESS}`);
|
|
146
|
+
/**
|
|
147
|
+
* Maximum number of retry attempts for failed jobs
|
|
148
|
+
*/
|
|
149
|
+
export const JOB_RETRY_MAX = parseInt(process.env.VIDEO_JOB_RETRY_MAX || "3", 10);
|
|
150
|
+
/**
|
|
151
|
+
* Hours after which completed/error jobs are cleaned up
|
|
152
|
+
*/
|
|
153
|
+
export const JOB_CLEANUP_HOURS = parseInt(process.env.VIDEO_JOB_CLEANUP_HOURS || "24", 10);
|
|
154
|
+
/**
|
|
155
|
+
* Worker polling interval in milliseconds
|
|
156
|
+
*/
|
|
157
|
+
export const WORKER_POLL_INTERVAL_MS = parseInt(process.env.VIDEO_WORKER_POLL_INTERVAL_MS || "1000", 10);
|
|
158
|
+
/**
|
|
159
|
+
* Priority for thumbnail extraction jobs (lower = higher priority)
|
|
160
|
+
*/
|
|
161
|
+
export const THUMBNAIL_PRIORITY = 1;
|
|
162
|
+
/**
|
|
163
|
+
* Priority for video transformation jobs
|
|
164
|
+
*/
|
|
165
|
+
export const TRANSFORMATION_PRIORITY = 2;
|
|
166
|
+
/**
|
|
167
|
+
* Priority for other video jobs
|
|
168
|
+
*/
|
|
169
|
+
export const LOW_PRIORITY = 3;
|
|
170
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/utils/video/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported image formats for video thumbnails
|
|
3
|
+
*/
|
|
4
|
+
export declare const IMAGE_FORMATS: Set<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Supported video formats
|
|
7
|
+
*/
|
|
8
|
+
export declare const VIDEO_FORMATS: Set<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Normalize format name (jpeg -> jpg)
|
|
11
|
+
*/
|
|
12
|
+
export declare const normalizeFormat: (format: string) => string;
|
|
13
|
+
/**
|
|
14
|
+
* Determine output format and whether it's an image or thumbnail
|
|
15
|
+
*/
|
|
16
|
+
export declare const determineOutputFormat: (sourceExt: string | undefined, requestedFormat: string | undefined) => {
|
|
17
|
+
format: string;
|
|
18
|
+
isImageOutput: boolean;
|
|
19
|
+
isThumbnail: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare const contentTypeForFormat: (format: string) => string;
|
|
22
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/utils/video/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,aAAa,aAAgE,CAAC;AAE3F;;GAEG;AACH,eAAO,MAAM,aAAa,aAAkC,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,MAEhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,GAAG,SAAS,EAC7B,iBAAiB,MAAM,GAAG,SAAS,KAClC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CA8BhE,CAAC;AAgBF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,KAAG,MAMrD,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported image formats for video thumbnails
|
|
3
|
+
*/
|
|
4
|
+
export const IMAGE_FORMATS = new Set(["jpg", "jpeg", "png", "webp", "avif", "gif", "psd"]);
|
|
5
|
+
/**
|
|
6
|
+
* Supported video formats
|
|
7
|
+
*/
|
|
8
|
+
export const VIDEO_FORMATS = new Set(["mp4", "mov", "webm"]);
|
|
9
|
+
/**
|
|
10
|
+
* Normalize format name (jpeg -> jpg)
|
|
11
|
+
*/
|
|
12
|
+
export const normalizeFormat = (format) => {
|
|
13
|
+
return format === "jpeg" ? "jpg" : format;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Determine output format and whether it's an image or thumbnail
|
|
17
|
+
*/
|
|
18
|
+
export const determineOutputFormat = (sourceExt, requestedFormat) => {
|
|
19
|
+
const requestedFormatLower = requestedFormat?.toLowerCase();
|
|
20
|
+
const isImageFormat = !!requestedFormatLower &&
|
|
21
|
+
IMAGE_FORMATS.has(normalizeFormat(requestedFormatLower));
|
|
22
|
+
const isVideoSource = !!sourceExt && VIDEO_FORMATS.has(sourceExt);
|
|
23
|
+
const isThumbnail = isVideoSource && isImageFormat;
|
|
24
|
+
// Decide output extension:
|
|
25
|
+
// - Thumbnail => image format (default jpg)
|
|
26
|
+
// - Video transform => requested video format or fall back to mp4
|
|
27
|
+
let format;
|
|
28
|
+
if (isThumbnail) {
|
|
29
|
+
const normalizedFormat = normalizeFormat(requestedFormatLower);
|
|
30
|
+
format = IMAGE_FORMATS.has(normalizedFormat) ? normalizedFormat : "jpg";
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const baseVideoExt = requestedFormatLower && VIDEO_FORMATS.has(requestedFormatLower)
|
|
34
|
+
? requestedFormatLower
|
|
35
|
+
: "mp4";
|
|
36
|
+
format = baseVideoExt;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
format,
|
|
40
|
+
isImageOutput: isImageFormat,
|
|
41
|
+
isThumbnail,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* MIME content type for a resolved output format.
|
|
46
|
+
*/
|
|
47
|
+
const CONTENT_TYPE_BY_FORMAT = {
|
|
48
|
+
jpg: "image/jpeg",
|
|
49
|
+
png: "image/png",
|
|
50
|
+
webp: "image/webp",
|
|
51
|
+
avif: "image/avif",
|
|
52
|
+
gif: "image/gif",
|
|
53
|
+
mp4: "video/mp4",
|
|
54
|
+
webm: "video/webm",
|
|
55
|
+
mov: "video/quicktime",
|
|
56
|
+
};
|
|
57
|
+
export const contentTypeForFormat = (format) => {
|
|
58
|
+
const normalized = normalizeFormat(format.toLowerCase());
|
|
59
|
+
if (CONTENT_TYPE_BY_FORMAT[normalized]) {
|
|
60
|
+
return CONTENT_TYPE_BY_FORMAT[normalized];
|
|
61
|
+
}
|
|
62
|
+
return IMAGE_FORMATS.has(normalized) ? `image/${normalized}` : "video/mp4";
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../../src/utils/video/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3F;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAE7D;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAc,EAAU,EAAE;IACxD,OAAO,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AAC5C,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,SAA6B,EAC7B,eAAmC,EAC+B,EAAE;IACpE,MAAM,oBAAoB,GAAG,eAAe,EAAE,WAAW,EAAE,CAAC;IAE5D,MAAM,aAAa,GACjB,CAAC,CAAC,oBAAoB;QACtB,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAE3D,MAAM,aAAa,GAAG,CAAC,CAAC,SAAS,IAAI,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,WAAW,GAAG,aAAa,IAAI,aAAa,CAAC;IAEnD,2BAA2B;IAC3B,4CAA4C;IAC5C,kEAAkE;IAClE,IAAI,MAAc,CAAC;IACnB,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,gBAAgB,GAAG,eAAe,CAAC,oBAAqB,CAAC,CAAC;QAChE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1E,CAAC;SAAM,CAAC;QACN,MAAM,YAAY,GAChB,oBAAoB,IAAI,aAAa,CAAC,GAAG,CAAC,oBAAoB,CAAC;YAC7D,CAAC,CAAC,oBAAoB;YACtB,CAAC,CAAC,KAAK,CAAC;QACZ,MAAM,GAAG,YAAY,CAAC;IACxB,CAAC;IAED,OAAO;QACL,MAAM;QACN,aAAa,EAAE,aAAa;QAC5B,WAAW;KACZ,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,sBAAsB,GAAqC;IAC/D,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,IAAI,EAAE,YAAY;IAClB,GAAG,EAAE,iBAAiB;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAc,EAAU,EAAE;IAC7D,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,IAAI,sBAAsB,CAAC,UAAU,CAAC,EAAE,CAAC;QACvC,OAAO,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;AAC7E,CAAC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { VideoTransformParams } from "../../types";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
export * from "./param-registry";
|
|
4
|
+
export * from "./video-info";
|
|
5
|
+
/**
|
|
6
|
+
* Transform a video with the specified parameters.
|
|
7
|
+
*
|
|
8
|
+
* Supports:
|
|
9
|
+
* - Video transformations (resize, quality, trim)
|
|
10
|
+
* - Thumbnail extraction (single frame as image)
|
|
11
|
+
* - Format conversion (mp4, mov, webm, jpg, png, webp, avif, …)
|
|
12
|
+
* - Auto-downscales to 720p max (unless explicit resize specified)
|
|
13
|
+
* - Default compression quality of 60/100 (CRF 31), optimised for 8K
|
|
14
|
+
* - Ultra-fast encoding preset with baseline profile for minimal CPU usage
|
|
15
|
+
* - Audio copied without re-encoding
|
|
16
|
+
* - 5-minute timeout protection (accommodates 8K videos)
|
|
17
|
+
*
|
|
18
|
+
* Thumbnail pipeline detail:
|
|
19
|
+
* 1. ffmpeg seeks to tt_N and extracts one JPEG frame (universal support).
|
|
20
|
+
* 2. If the requested format needs sharp (webp, avif, png, gif), the JPEG
|
|
21
|
+
* buffer is post-processed by sharp, this avoids relying on optional
|
|
22
|
+
* ffmpeg build flags such as libwebp.
|
|
23
|
+
*/
|
|
24
|
+
export declare const transformVideo: (inputPath: string, params: VideoTransformParams) => Promise<Buffer>;
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/video/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAexD,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAoC7B;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,MAAM,EACjB,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,MAAM,CAiEhB,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mkdtemp } from "fs/promises";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
import { determineOutputFormat, IMAGE_FORMATS, normalizeFormat, } from "./format.js";
|
|
6
|
+
import { applyThumbnailExtraction } from "./thumbnail.js";
|
|
7
|
+
import { applyTrimming } from "./trim.js";
|
|
8
|
+
import { applyAutoDownscale } from "./auto-downscale.js";
|
|
9
|
+
import { applyResize } from "./resize.js";
|
|
10
|
+
import { applyQuality } from "./quality.js";
|
|
11
|
+
import { VideoCommandBuilder } from "./command-builder.js";
|
|
12
|
+
// Re-export types for backward compatibility
|
|
13
|
+
export * from "./types.js";
|
|
14
|
+
export * from "./param-registry.js";
|
|
15
|
+
export * from "./video-info.js";
|
|
16
|
+
/**
|
|
17
|
+
* Image formats that ffmpeg cannot encode natively in all builds.
|
|
18
|
+
* For these we extract a JPEG frame via ffmpeg, then convert with sharp.
|
|
19
|
+
* This decouples thumbnail quality from ffmpeg's build flags (e.g. libwebp).
|
|
20
|
+
*/
|
|
21
|
+
const SHARP_CONVERTED_FORMATS = new Set(["webp", "avif", "png", "gif"]);
|
|
22
|
+
/**
|
|
23
|
+
* Convert an image buffer to the target format using sharp.
|
|
24
|
+
* Falls back to the original buffer on any sharp error.
|
|
25
|
+
*/
|
|
26
|
+
async function convertWithSharp(buffer, targetFormat, quality) {
|
|
27
|
+
const sharp = (await import("sharp")).default;
|
|
28
|
+
const q = quality !== undefined ? Math.round(quality) : 80;
|
|
29
|
+
switch (targetFormat) {
|
|
30
|
+
case "webp":
|
|
31
|
+
return sharp(buffer).webp({ quality: q }).toBuffer();
|
|
32
|
+
case "avif":
|
|
33
|
+
return sharp(buffer).avif({ quality: q }).toBuffer();
|
|
34
|
+
case "png":
|
|
35
|
+
return sharp(buffer).png().toBuffer();
|
|
36
|
+
case "gif":
|
|
37
|
+
// sharp doesn't encode animated GIF well; best effort single frame
|
|
38
|
+
return sharp(buffer).gif().toBuffer();
|
|
39
|
+
default:
|
|
40
|
+
return buffer;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Transform a video with the specified parameters.
|
|
45
|
+
*
|
|
46
|
+
* Supports:
|
|
47
|
+
* - Video transformations (resize, quality, trim)
|
|
48
|
+
* - Thumbnail extraction (single frame as image)
|
|
49
|
+
* - Format conversion (mp4, mov, webm, jpg, png, webp, avif, …)
|
|
50
|
+
* - Auto-downscales to 720p max (unless explicit resize specified)
|
|
51
|
+
* - Default compression quality of 60/100 (CRF 31), optimised for 8K
|
|
52
|
+
* - Ultra-fast encoding preset with baseline profile for minimal CPU usage
|
|
53
|
+
* - Audio copied without re-encoding
|
|
54
|
+
* - 5-minute timeout protection (accommodates 8K videos)
|
|
55
|
+
*
|
|
56
|
+
* Thumbnail pipeline detail:
|
|
57
|
+
* 1. ffmpeg seeks to tt_N and extracts one JPEG frame (universal support).
|
|
58
|
+
* 2. If the requested format needs sharp (webp, avif, png, gif), the JPEG
|
|
59
|
+
* buffer is post-processed by sharp, this avoids relying on optional
|
|
60
|
+
* ffmpeg build flags such as libwebp.
|
|
61
|
+
*/
|
|
62
|
+
export const transformVideo = async (inputPath, params) => {
|
|
63
|
+
// Create temporary directory for output
|
|
64
|
+
const tmpDir = await mkdtemp(join(tmpdir(), "video-"));
|
|
65
|
+
// Get source file extension
|
|
66
|
+
const sourceExt = inputPath.split(".").pop()?.toLowerCase();
|
|
67
|
+
// Determine output format and flags
|
|
68
|
+
const { format, isImageOutput, isThumbnail } = determineOutputFormat(sourceExt, params.format);
|
|
69
|
+
// For thumbnails, always let ffmpeg output JPEG, it is universally supported
|
|
70
|
+
// regardless of ffmpeg build flags (no libwebp required).
|
|
71
|
+
// We post-process with sharp afterwards if the requested format differs.
|
|
72
|
+
const ffmpegFormat = isThumbnail ? "jpg" : format;
|
|
73
|
+
const needsSharpConversion = isThumbnail &&
|
|
74
|
+
IMAGE_FORMATS.has(normalizeFormat(format)) &&
|
|
75
|
+
SHARP_CONVERTED_FORMATS.has(normalizeFormat(format));
|
|
76
|
+
// Generate output path using the ffmpeg-friendly format
|
|
77
|
+
const outputPath = join(tmpDir, `${randomUUID()}.${ffmpegFormat}`);
|
|
78
|
+
// Build context object
|
|
79
|
+
const context = {
|
|
80
|
+
inputPath,
|
|
81
|
+
outputPath,
|
|
82
|
+
tmpDir,
|
|
83
|
+
params,
|
|
84
|
+
isImageOutput,
|
|
85
|
+
isThumbnail,
|
|
86
|
+
};
|
|
87
|
+
// Apply transformations pipeline and execute
|
|
88
|
+
// Order matters: thumbnail extraction or trimming first, auto-downscale, resize, then quality
|
|
89
|
+
const builder = new VideoCommandBuilder(context);
|
|
90
|
+
let buffer = await builder
|
|
91
|
+
.apply(applyThumbnailExtraction, applyTrimming, applyAutoDownscale, applyResize, applyQuality)
|
|
92
|
+
.execute();
|
|
93
|
+
// Post-process with sharp if needed (e.g. JPEG → WebP/AVIF/PNG)
|
|
94
|
+
if (needsSharpConversion) {
|
|
95
|
+
const qualityValue = params.quality !== undefined
|
|
96
|
+
? typeof params.quality === "string"
|
|
97
|
+
? parseInt(params.quality, 10)
|
|
98
|
+
: params.quality
|
|
99
|
+
: 80;
|
|
100
|
+
buffer = await convertWithSharp(buffer, normalizeFormat(format), qualityValue);
|
|
101
|
+
}
|
|
102
|
+
return buffer;
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/video/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { FfmpegCommand } from 'fluent-ffmpeg';
|
|
2
|
+
import type { VideoContext } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Parameter processor function type for video transformations
|
|
5
|
+
* Takes an FFmpeg command and context, returns modified command
|
|
6
|
+
*/
|
|
7
|
+
export type VideoParamProcessor = (command: FfmpegCommand, value: string, context: VideoContext) => FfmpegCommand;
|
|
8
|
+
/**
|
|
9
|
+
* Parameter definition interface for video transformations
|
|
10
|
+
*/
|
|
11
|
+
export interface VideoParamDefinition {
|
|
12
|
+
/** Short parameter key used in URLs (e.g., 'w', 'h', 'so') */
|
|
13
|
+
param: string;
|
|
14
|
+
/** Human-readable name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Detailed description of the parameter */
|
|
17
|
+
description: string;
|
|
18
|
+
/** Example value for documentation */
|
|
19
|
+
example: string;
|
|
20
|
+
/** Default value if not specified */
|
|
21
|
+
defaultValue?: string | number | boolean;
|
|
22
|
+
/** Valid values or pattern description */
|
|
23
|
+
validValues?: string[];
|
|
24
|
+
/** Parameter aliases (alternative names) */
|
|
25
|
+
aliases?: string[];
|
|
26
|
+
/** The function that processes this parameter */
|
|
27
|
+
processor?: VideoParamProcessor;
|
|
28
|
+
/** Whether this parameter requires other parameters to work */
|
|
29
|
+
dependencies?: string[];
|
|
30
|
+
/** Processing order priority (lower = earlier) */
|
|
31
|
+
priority: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Complete parameter registry for video transformations
|
|
35
|
+
* Single source of truth for all video transformation parameters
|
|
36
|
+
*/
|
|
37
|
+
export declare const VIDEO_PARAMS: readonly VideoParamDefinition[];
|
|
38
|
+
/**
|
|
39
|
+
* Helper function to apply resize with all related parameters
|
|
40
|
+
* This is a composite operation that uses w, h, r, c, and g parameters
|
|
41
|
+
*/
|
|
42
|
+
export declare const applyResizeComposite: (command: FfmpegCommand, context: VideoContext) => FfmpegCommand;
|
|
43
|
+
/**
|
|
44
|
+
* Get parameter definition by param key
|
|
45
|
+
*/
|
|
46
|
+
export declare const getVideoParamDefinition: (param: string) => VideoParamDefinition | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Get all parameter definitions sorted by priority
|
|
49
|
+
*/
|
|
50
|
+
export declare const getVideoParamsByPriority: () => readonly VideoParamDefinition[];
|
|
51
|
+
/**
|
|
52
|
+
* Get default value for a parameter
|
|
53
|
+
*/
|
|
54
|
+
export declare const getVideoParamDefault: (param: string) => string | number | boolean | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Validate if a parameter value is valid
|
|
57
|
+
*/
|
|
58
|
+
export declare const isValidVideoParamValue: (param: string, value: string) => boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Export types for external use
|
|
61
|
+
*/
|
|
62
|
+
export type VideoParamKey = typeof VIDEO_PARAMS[number]['param'];
|
|
63
|
+
export type VideoParamRegistry = typeof VIDEO_PARAMS;
|
|
64
|
+
//# sourceMappingURL=param-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"param-registry.d.ts","sourceRoot":"","sources":["../../../src/utils/video/param-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM5C;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,YAAY,KAClB,aAAa,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACzC,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,iDAAiD;IACjD,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,SAAS,oBAAoB,EAgJ9C,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAS,aAAa,EACtB,SAAS,YAAY,KACpB,aAQF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,OAAO,MAAM,KAAG,oBAAoB,GAAG,SAE9E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,QAAO,SAAS,oBAAoB,EAExE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAGhF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,OAYrE,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;AACjE,MAAM,MAAM,kBAAkB,GAAG,OAAO,YAAY,CAAC"}
|