@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.
Files changed (242) hide show
  1. package/LICENSE +661 -0
  2. package/dist/config/deps.d.ts +14 -0
  3. package/dist/config/deps.d.ts.map +1 -0
  4. package/dist/config/deps.js +2 -0
  5. package/dist/config/deps.js.map +1 -0
  6. package/dist/index.d.ts +27 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +28 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/routes/authenticated.d.ts +4 -0
  11. package/dist/routes/authenticated.d.ts.map +1 -0
  12. package/dist/routes/authenticated.js +130 -0
  13. package/dist/routes/authenticated.js.map +1 -0
  14. package/dist/routes/download-folder.d.ts +4 -0
  15. package/dist/routes/download-folder.d.ts.map +1 -0
  16. package/dist/routes/download-folder.js +101 -0
  17. package/dist/routes/download-folder.js.map +1 -0
  18. package/dist/routes/download-zip.d.ts +15 -0
  19. package/dist/routes/download-zip.d.ts.map +1 -0
  20. package/dist/routes/download-zip.js +147 -0
  21. package/dist/routes/download-zip.js.map +1 -0
  22. package/dist/routes/download.d.ts +4 -0
  23. package/dist/routes/download.d.ts.map +1 -0
  24. package/dist/routes/download.js +83 -0
  25. package/dist/routes/download.js.map +1 -0
  26. package/dist/routes/invalidate.d.ts +4 -0
  27. package/dist/routes/invalidate.d.ts.map +1 -0
  28. package/dist/routes/invalidate.js +114 -0
  29. package/dist/routes/invalidate.js.map +1 -0
  30. package/dist/routes/queue-events.d.ts +4 -0
  31. package/dist/routes/queue-events.d.ts.map +1 -0
  32. package/dist/routes/queue-events.js +125 -0
  33. package/dist/routes/queue-events.js.map +1 -0
  34. package/dist/routes/queue.d.ts +4 -0
  35. package/dist/routes/queue.d.ts.map +1 -0
  36. package/dist/routes/queue.js +130 -0
  37. package/dist/routes/queue.js.map +1 -0
  38. package/dist/routes/storage.d.ts +4 -0
  39. package/dist/routes/storage.d.ts.map +1 -0
  40. package/dist/routes/storage.js +694 -0
  41. package/dist/routes/storage.js.map +1 -0
  42. package/dist/routes/transform-helpers.d.ts +62 -0
  43. package/dist/routes/transform-helpers.d.ts.map +1 -0
  44. package/dist/routes/transform-helpers.js +372 -0
  45. package/dist/routes/transform-helpers.js.map +1 -0
  46. package/dist/routes/transform.d.ts +4 -0
  47. package/dist/routes/transform.d.ts.map +1 -0
  48. package/dist/routes/transform.js +65 -0
  49. package/dist/routes/transform.js.map +1 -0
  50. package/dist/routes/video-status.d.ts +4 -0
  51. package/dist/routes/video-status.d.ts.map +1 -0
  52. package/dist/routes/video-status.js +208 -0
  53. package/dist/routes/video-status.js.map +1 -0
  54. package/dist/services/transform.service.d.ts +71 -0
  55. package/dist/services/transform.service.d.ts.map +1 -0
  56. package/dist/services/transform.service.js +362 -0
  57. package/dist/services/transform.service.js.map +1 -0
  58. package/dist/types.d.ts +82 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/asset-deletion.d.ts +16 -0
  63. package/dist/utils/asset-deletion.d.ts.map +1 -0
  64. package/dist/utils/asset-deletion.js +131 -0
  65. package/dist/utils/asset-deletion.js.map +1 -0
  66. package/dist/utils/cache.d.ts +44 -0
  67. package/dist/utils/cache.d.ts.map +1 -0
  68. package/dist/utils/cache.js +248 -0
  69. package/dist/utils/cache.js.map +1 -0
  70. package/dist/utils/get-unique-file-path.d.ts +8 -0
  71. package/dist/utils/get-unique-file-path.d.ts.map +1 -0
  72. package/dist/utils/get-unique-file-path.js +26 -0
  73. package/dist/utils/get-unique-file-path.js.map +1 -0
  74. package/dist/utils/image/aspect-ratio.d.ts +7 -0
  75. package/dist/utils/image/aspect-ratio.d.ts.map +1 -0
  76. package/dist/utils/image/aspect-ratio.js +40 -0
  77. package/dist/utils/image/aspect-ratio.js.map +1 -0
  78. package/dist/utils/image/background.d.ts +6 -0
  79. package/dist/utils/image/background.d.ts.map +1 -0
  80. package/dist/utils/image/background.js +22 -0
  81. package/dist/utils/image/background.js.map +1 -0
  82. package/dist/utils/image/compression.d.ts +44 -0
  83. package/dist/utils/image/compression.d.ts.map +1 -0
  84. package/dist/utils/image/compression.js +345 -0
  85. package/dist/utils/image/compression.js.map +1 -0
  86. package/dist/utils/image/gravity.d.ts +6 -0
  87. package/dist/utils/image/gravity.d.ts.map +1 -0
  88. package/dist/utils/image/gravity.js +24 -0
  89. package/dist/utils/image/gravity.js.map +1 -0
  90. package/dist/utils/image/index.d.ts +8 -0
  91. package/dist/utils/image/index.d.ts.map +1 -0
  92. package/dist/utils/image/index.js +77 -0
  93. package/dist/utils/image/index.js.map +1 -0
  94. package/dist/utils/image/param-registry.d.ts +63 -0
  95. package/dist/utils/image/param-registry.d.ts.map +1 -0
  96. package/dist/utils/image/param-registry.js +196 -0
  97. package/dist/utils/image/param-registry.js.map +1 -0
  98. package/dist/utils/image/quality.d.ts +8 -0
  99. package/dist/utils/image/quality.d.ts.map +1 -0
  100. package/dist/utils/image/quality.js +9 -0
  101. package/dist/utils/image/quality.js.map +1 -0
  102. package/dist/utils/image/resize.d.ts +7 -0
  103. package/dist/utils/image/resize.d.ts.map +1 -0
  104. package/dist/utils/image/resize.js +62 -0
  105. package/dist/utils/image/resize.js.map +1 -0
  106. package/dist/utils/image/rotation.d.ts +6 -0
  107. package/dist/utils/image/rotation.d.ts.map +1 -0
  108. package/dist/utils/image/rotation.js +20 -0
  109. package/dist/utils/image/rotation.js.map +1 -0
  110. package/dist/utils/image/round-corners.d.ts +12 -0
  111. package/dist/utils/image/round-corners.d.ts.map +1 -0
  112. package/dist/utils/image/round-corners.js +102 -0
  113. package/dist/utils/image/round-corners.js.map +1 -0
  114. package/dist/utils/image/types.d.ts +2 -0
  115. package/dist/utils/image/types.d.ts.map +1 -0
  116. package/dist/utils/image/types.js +2 -0
  117. package/dist/utils/image/types.js.map +1 -0
  118. package/dist/utils/logger.d.ts +21 -0
  119. package/dist/utils/logger.d.ts.map +1 -0
  120. package/dist/utils/logger.js +82 -0
  121. package/dist/utils/logger.js.map +1 -0
  122. package/dist/utils/parser.d.ts +8 -0
  123. package/dist/utils/parser.d.ts.map +1 -0
  124. package/dist/utils/parser.js +226 -0
  125. package/dist/utils/parser.js.map +1 -0
  126. package/dist/utils/signature.d.ts +35 -0
  127. package/dist/utils/signature.d.ts.map +1 -0
  128. package/dist/utils/signature.js +100 -0
  129. package/dist/utils/signature.js.map +1 -0
  130. package/dist/utils/storage/cache.d.ts +41 -0
  131. package/dist/utils/storage/cache.d.ts.map +1 -0
  132. package/dist/utils/storage/cache.js +90 -0
  133. package/dist/utils/storage/cache.js.map +1 -0
  134. package/dist/utils/storage/cloud-storage.d.ts +188 -0
  135. package/dist/utils/storage/cloud-storage.d.ts.map +1 -0
  136. package/dist/utils/storage/cloud-storage.js +514 -0
  137. package/dist/utils/storage/cloud-storage.js.map +1 -0
  138. package/dist/utils/storage/factory.d.ts +10 -0
  139. package/dist/utils/storage/factory.d.ts.map +1 -0
  140. package/dist/utils/storage/factory.js +17 -0
  141. package/dist/utils/storage/factory.js.map +1 -0
  142. package/dist/utils/storage/index.d.ts +7 -0
  143. package/dist/utils/storage/index.d.ts.map +1 -0
  144. package/dist/utils/storage/index.js +8 -0
  145. package/dist/utils/storage/index.js.map +1 -0
  146. package/dist/utils/storage/key-generator.d.ts +11 -0
  147. package/dist/utils/storage/key-generator.d.ts.map +1 -0
  148. package/dist/utils/storage/key-generator.js +23 -0
  149. package/dist/utils/storage/key-generator.js.map +1 -0
  150. package/dist/utils/storage/listing-cache.d.ts +14 -0
  151. package/dist/utils/storage/listing-cache.d.ts.map +1 -0
  152. package/dist/utils/storage/listing-cache.js +29 -0
  153. package/dist/utils/storage/listing-cache.js.map +1 -0
  154. package/dist/utils/storage/s3-client.d.ts +97 -0
  155. package/dist/utils/storage/s3-client.d.ts.map +1 -0
  156. package/dist/utils/storage/s3-client.js +297 -0
  157. package/dist/utils/storage/s3-client.js.map +1 -0
  158. package/dist/utils/storage/stats-tracker.d.ts +45 -0
  159. package/dist/utils/storage/stats-tracker.d.ts.map +1 -0
  160. package/dist/utils/storage/stats-tracker.js +97 -0
  161. package/dist/utils/storage/stats-tracker.js.map +1 -0
  162. package/dist/utils/storage/types.d.ts +2 -0
  163. package/dist/utils/storage/types.d.ts.map +1 -0
  164. package/dist/utils/storage/types.js +2 -0
  165. package/dist/utils/storage/types.js.map +1 -0
  166. package/dist/utils/storage-level.d.ts +66 -0
  167. package/dist/utils/storage-level.d.ts.map +1 -0
  168. package/dist/utils/storage-level.js +106 -0
  169. package/dist/utils/storage-level.js.map +1 -0
  170. package/dist/utils/storage-tree.d.ts +30 -0
  171. package/dist/utils/storage-tree.d.ts.map +1 -0
  172. package/dist/utils/storage-tree.js +81 -0
  173. package/dist/utils/storage-tree.js.map +1 -0
  174. package/dist/utils/upload-signature.d.ts +25 -0
  175. package/dist/utils/upload-signature.d.ts.map +1 -0
  176. package/dist/utils/upload-signature.js +56 -0
  177. package/dist/utils/upload-signature.js.map +1 -0
  178. package/dist/utils/video/auto-downscale.d.ts +12 -0
  179. package/dist/utils/video/auto-downscale.d.ts.map +1 -0
  180. package/dist/utils/video/auto-downscale.js +29 -0
  181. package/dist/utils/video/auto-downscale.js.map +1 -0
  182. package/dist/utils/video/command-builder.d.ts +22 -0
  183. package/dist/utils/video/command-builder.d.ts.map +1 -0
  184. package/dist/utils/video/command-builder.js +79 -0
  185. package/dist/utils/video/command-builder.js.map +1 -0
  186. package/dist/utils/video/config.d.ts +51 -0
  187. package/dist/utils/video/config.d.ts.map +1 -0
  188. package/dist/utils/video/config.js +170 -0
  189. package/dist/utils/video/config.js.map +1 -0
  190. package/dist/utils/video/format.d.ts +22 -0
  191. package/dist/utils/video/format.d.ts.map +1 -0
  192. package/dist/utils/video/format.js +64 -0
  193. package/dist/utils/video/format.js.map +1 -0
  194. package/dist/utils/video/index.d.ts +25 -0
  195. package/dist/utils/video/index.d.ts.map +1 -0
  196. package/dist/utils/video/index.js +104 -0
  197. package/dist/utils/video/index.js.map +1 -0
  198. package/dist/utils/video/param-registry.d.ts +64 -0
  199. package/dist/utils/video/param-registry.d.ts.map +1 -0
  200. package/dist/utils/video/param-registry.js +201 -0
  201. package/dist/utils/video/param-registry.js.map +1 -0
  202. package/dist/utils/video/quality.d.ts +12 -0
  203. package/dist/utils/video/quality.d.ts.map +1 -0
  204. package/dist/utils/video/quality.js +45 -0
  205. package/dist/utils/video/quality.js.map +1 -0
  206. package/dist/utils/video/queue-store.d.ts +51 -0
  207. package/dist/utils/video/queue-store.d.ts.map +1 -0
  208. package/dist/utils/video/queue-store.js +2 -0
  209. package/dist/utils/video/queue-store.js.map +1 -0
  210. package/dist/utils/video/resize.d.ts +7 -0
  211. package/dist/utils/video/resize.d.ts.map +1 -0
  212. package/dist/utils/video/resize.js +40 -0
  213. package/dist/utils/video/resize.js.map +1 -0
  214. package/dist/utils/video/sqlite-video-job-store.d.ts +29 -0
  215. package/dist/utils/video/sqlite-video-job-store.d.ts.map +1 -0
  216. package/dist/utils/video/sqlite-video-job-store.js +320 -0
  217. package/dist/utils/video/sqlite-video-job-store.js.map +1 -0
  218. package/dist/utils/video/thumbnail.d.ts +7 -0
  219. package/dist/utils/video/thumbnail.d.ts.map +1 -0
  220. package/dist/utils/video/thumbnail.js +22 -0
  221. package/dist/utils/video/thumbnail.js.map +1 -0
  222. package/dist/utils/video/trim.d.ts +7 -0
  223. package/dist/utils/video/trim.d.ts.map +1 -0
  224. package/dist/utils/video/trim.js +36 -0
  225. package/dist/utils/video/trim.js.map +1 -0
  226. package/dist/utils/video/types.d.ts +19 -0
  227. package/dist/utils/video/types.d.ts.map +1 -0
  228. package/dist/utils/video/types.js +2 -0
  229. package/dist/utils/video/types.js.map +1 -0
  230. package/dist/utils/video/video-info.d.ts +25 -0
  231. package/dist/utils/video/video-info.d.ts.map +1 -0
  232. package/dist/utils/video/video-info.js +58 -0
  233. package/dist/utils/video/video-info.js.map +1 -0
  234. package/dist/utils/video/video-worker.d.ts +52 -0
  235. package/dist/utils/video/video-worker.d.ts.map +1 -0
  236. package/dist/utils/video/video-worker.js +192 -0
  237. package/dist/utils/video/video-worker.js.map +1 -0
  238. package/dist/utils/video-job-queue.d.ts +62 -0
  239. package/dist/utils/video-job-queue.d.ts.map +1 -0
  240. package/dist/utils/video-job-queue.js +122 -0
  241. package/dist/utils/video-job-queue.js.map +1 -0
  242. 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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,6CAA6C;AAC7C,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAE7B;;;;GAIG;AACH,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAExE;;;GAGG;AACH,KAAK,UAAU,gBAAgB,CAC7B,MAAc,EACd,YAAoB,EACpB,OAAgB;IAEhB,MAAM,KAAK,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9C,MAAM,CAAC,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3D,QAAQ,YAAY,EAAE,CAAC;QACrB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvD,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvD,KAAK,KAAK;YACR,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QACxC,KAAK,KAAK;YACR,mEAAmE;YACnE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QACxC;YACE,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,SAAiB,EACjB,MAA4B,EACX,EAAE;IACnB,wCAAwC;IACxC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEvD,4BAA4B;IAC5B,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;IAE5D,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAClE,SAAS,EACT,MAAM,CAAC,MAAM,CACd,CAAC;IAEF,6EAA6E;IAC7E,0DAA0D;IAC1D,yEAAyE;IACzE,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IAClD,MAAM,oBAAoB,GACxB,WAAW;QACX,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC1C,uBAAuB,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IAEvD,wDAAwD;IACxD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,UAAU,EAAE,IAAI,YAAY,EAAE,CAAC,CAAC;IAEnE,uBAAuB;IACvB,MAAM,OAAO,GAAiB;QAC5B,SAAS;QACT,UAAU;QACV,MAAM;QACN,MAAM;QACN,aAAa;QACb,WAAW;KACZ,CAAC;IAEF,6CAA6C;IAC7C,8FAA8F;IAC9F,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAEjD,IAAI,MAAM,GAAG,MAAM,OAAO;SACvB,KAAK,CACJ,wBAAwB,EACxB,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,YAAY,CACb;SACA,OAAO,EAAE,CAAC;IAEb,gEAAgE;IAChE,IAAI,oBAAoB,EAAE,CAAC;QACzB,MAAM,YAAY,GAChB,MAAM,CAAC,OAAO,KAAK,SAAS;YAC1B,CAAC,CAAC,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;gBAClC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC9B,CAAC,CAAC,MAAM,CAAC,OAAO;YAClB,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,GAAG,MAAM,gBAAgB,CAC7B,MAAM,EACN,eAAe,CAAC,MAAM,CAAC,EACvB,YAAY,CACb,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
@@ -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"}