@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,66 @@
1
+ export declare const IMAGE_EXTENSIONS: string[];
2
+ export declare const VIDEO_EXTENSIONS: string[];
3
+ export type MediaType = "image" | "video";
4
+ export type FolderPreviewItem = {
5
+ path: string;
6
+ type: MediaType;
7
+ };
8
+ export type LevelFile = {
9
+ name: string;
10
+ path: string;
11
+ size?: number;
12
+ mtime?: string;
13
+ };
14
+ export type LevelFolder = {
15
+ name: string;
16
+ path: string;
17
+ };
18
+ export type FolderSummary = {
19
+ itemCount: number;
20
+ truncated: boolean;
21
+ previewItems: FolderPreviewItem[];
22
+ };
23
+ type ListedObject = {
24
+ key: string;
25
+ size?: number;
26
+ lastModified?: Date;
27
+ };
28
+ export declare const FOLDER_SUMMARY_MAX_KEYS = 100;
29
+ export declare const FOLDER_PREVIEW_LIMIT = 4;
30
+ export declare function getMediaType(name: string): MediaType | null;
31
+ /**
32
+ * Normalizes a user-provided folder path.
33
+ * Returns "" for the root, null when the path is invalid (empty segments,
34
+ * "." or "..", critical to prevent traversal in the local-FS fallback).
35
+ */
36
+ export declare function normalizeLevelPath(raw: string): string | null;
37
+ /**
38
+ * Shapes one delimiter-listed directory level into folder names and files.
39
+ * `storagePrefix` is the raw listing prefix (e.g. "public/cows/"), `folderPath`
40
+ * the relative path of the listed folder ("" for root). The folder marker
41
+ * object (key === storagePrefix) is excluded.
42
+ */
43
+ export declare function shapeLevel(storagePrefix: string, folderPath: string, delimited: {
44
+ prefixes: string[];
45
+ objects: ListedObject[];
46
+ }): {
47
+ folderNames: string[];
48
+ files: LevelFile[];
49
+ };
50
+ /**
51
+ * Shapes a bounded single-page listing of a folder into its summary:
52
+ * direct child count (capped by the page size), truncation flag and up to
53
+ * FOLDER_PREVIEW_LIMIT media preview items.
54
+ */
55
+ export declare function shapeFolderSummary(storagePrefix: string, folderPath: string, page: {
56
+ prefixes: string[];
57
+ objects: ListedObject[];
58
+ isTruncated: boolean;
59
+ }, previewLimit?: number): FolderSummary;
60
+ /**
61
+ * Derives every folder path (including intermediate ones) from a flat list of
62
+ * relative keys. Marker keys ("a/b/") count as folders themselves.
63
+ */
64
+ export declare function deriveFolderPaths(keys: string[]): string[];
65
+ export {};
66
+ //# sourceMappingURL=storage-level.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-level.d.ts","sourceRoot":"","sources":["../../src/utils/storage-level.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,UAQ5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,UAA4B,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE1C,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAElE,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,iBAAiB,EAAE,CAAC;CACnC,CAAC;AAEF,KAAK,YAAY,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAExE,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAK3D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CACxB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAA;CAAE,GACzD;IAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAqB/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,EAC3E,YAAY,SAAuB,GAClC,aAAa,CAqBf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAc1D"}
@@ -0,0 +1,106 @@
1
+ export const IMAGE_EXTENSIONS = [
2
+ ".jpg",
3
+ ".jpeg",
4
+ ".png",
5
+ ".webp",
6
+ ".gif",
7
+ ".avif",
8
+ ".psd",
9
+ ];
10
+ export const VIDEO_EXTENSIONS = [".mp4", ".mov", ".webm"];
11
+ export const FOLDER_SUMMARY_MAX_KEYS = 100;
12
+ export const FOLDER_PREVIEW_LIMIT = 4;
13
+ export function getMediaType(name) {
14
+ const lower = name.toLowerCase();
15
+ if (IMAGE_EXTENSIONS.some((ext) => lower.endsWith(ext)))
16
+ return "image";
17
+ if (VIDEO_EXTENSIONS.some((ext) => lower.endsWith(ext)))
18
+ return "video";
19
+ return null;
20
+ }
21
+ /**
22
+ * Normalizes a user-provided folder path.
23
+ * Returns "" for the root, null when the path is invalid (empty segments,
24
+ * "." or "..", critical to prevent traversal in the local-FS fallback).
25
+ */
26
+ export function normalizeLevelPath(raw) {
27
+ const trimmed = raw.replace(/^\/+|\/+$/g, "");
28
+ if (!trimmed)
29
+ return "";
30
+ const segments = trimmed.split("/");
31
+ for (const segment of segments) {
32
+ if (!segment || segment === "." || segment === "..") {
33
+ return null;
34
+ }
35
+ }
36
+ return segments.join("/");
37
+ }
38
+ /**
39
+ * Shapes one delimiter-listed directory level into folder names and files.
40
+ * `storagePrefix` is the raw listing prefix (e.g. "public/cows/"), `folderPath`
41
+ * the relative path of the listed folder ("" for root). The folder marker
42
+ * object (key === storagePrefix) is excluded.
43
+ */
44
+ export function shapeLevel(storagePrefix, folderPath, delimited) {
45
+ const folderNames = delimited.prefixes
46
+ .map((prefix) => prefix.slice(storagePrefix.length).replace(/\/+$/, ""))
47
+ .filter(Boolean)
48
+ .sort((a, b) => a.localeCompare(b));
49
+ const files = delimited.objects
50
+ .filter((obj) => obj.key !== storagePrefix)
51
+ .map((obj) => {
52
+ const name = obj.key.slice(storagePrefix.length);
53
+ return {
54
+ name,
55
+ path: folderPath ? `${folderPath}/${name}` : name,
56
+ size: obj.size,
57
+ mtime: obj.lastModified?.toISOString(),
58
+ };
59
+ })
60
+ .filter((file) => file.name)
61
+ .sort((a, b) => a.name.localeCompare(b.name));
62
+ return { folderNames, files };
63
+ }
64
+ /**
65
+ * Shapes a bounded single-page listing of a folder into its summary:
66
+ * direct child count (capped by the page size), truncation flag and up to
67
+ * FOLDER_PREVIEW_LIMIT media preview items.
68
+ */
69
+ export function shapeFolderSummary(storagePrefix, folderPath, page, previewLimit = FOLDER_PREVIEW_LIMIT) {
70
+ const children = page.objects.filter((obj) => obj.key !== storagePrefix);
71
+ const previewItems = [];
72
+ for (const obj of children) {
73
+ if (previewItems.length >= previewLimit)
74
+ break;
75
+ const name = obj.key.slice(storagePrefix.length);
76
+ const type = getMediaType(name);
77
+ if (type) {
78
+ previewItems.push({
79
+ path: folderPath ? `${folderPath}/${name}` : name,
80
+ type,
81
+ });
82
+ }
83
+ }
84
+ return {
85
+ itemCount: page.prefixes.length + children.length,
86
+ truncated: page.isTruncated,
87
+ previewItems,
88
+ };
89
+ }
90
+ /**
91
+ * Derives every folder path (including intermediate ones) from a flat list of
92
+ * relative keys. Marker keys ("a/b/") count as folders themselves.
93
+ */
94
+ export function deriveFolderPaths(keys) {
95
+ const folders = new Set();
96
+ for (const key of keys) {
97
+ const normalized = key.replace(/^\/+/, "");
98
+ const parts = normalized.split("/").filter(Boolean);
99
+ const dirDepth = normalized.endsWith("/") ? parts.length : parts.length - 1;
100
+ for (let i = 1; i <= dirDepth; i++) {
101
+ folders.add(parts.slice(0, i).join("/"));
102
+ }
103
+ }
104
+ return [...folders].sort((a, b) => a.localeCompare(b));
105
+ }
106
+ //# sourceMappingURL=storage-level.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-level.js","sourceRoot":"","sources":["../../src/utils/storage-level.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export type StorageNode = {
2
+ name: string;
3
+ path: string;
4
+ type: "file" | "directory";
5
+ size?: number;
6
+ mtime?: string;
7
+ children?: StorageNode[];
8
+ };
9
+ export type TreeDataItem = {
10
+ id: string;
11
+ name: string;
12
+ size?: number;
13
+ mtime?: string;
14
+ children?: TreeDataItem[];
15
+ draggable?: boolean;
16
+ droppable?: boolean;
17
+ disabled?: boolean;
18
+ };
19
+ export type ListedObject = {
20
+ key: string;
21
+ size?: number;
22
+ lastModified?: Date;
23
+ };
24
+ export declare function buildTreeFromKeys(keys: ListedObject[]): StorageNode;
25
+ export declare function storageTreeToTreeData(root: StorageNode): TreeDataItem[];
26
+ export declare function sumTreeSize(node: StorageNode): {
27
+ size: number;
28
+ fileCount: number;
29
+ };
30
+ //# sourceMappingURL=storage-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-tree.d.ts","sourceRoot":"","sources":["../../src/utils/storage-tree.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,WAAW,CAkEnE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,EAAE,CAcvE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAWA"}
@@ -0,0 +1,81 @@
1
+ export function buildTreeFromKeys(keys) {
2
+ const root = {
3
+ name: "storage",
4
+ path: "",
5
+ type: "directory",
6
+ children: [],
7
+ };
8
+ // Path-indexed lookups keep tree construction linear in the number of keys
9
+ const dirByPath = new Map([["", root]]);
10
+ const filePaths = new Set();
11
+ const ensureDir = (dirPath) => {
12
+ const existing = dirByPath.get(dirPath);
13
+ if (existing) {
14
+ return existing;
15
+ }
16
+ const slashIndex = dirPath.lastIndexOf("/");
17
+ const parent = ensureDir(slashIndex === -1 ? "" : dirPath.slice(0, slashIndex));
18
+ const dirNode = {
19
+ name: dirPath.slice(slashIndex + 1),
20
+ path: dirPath,
21
+ type: "directory",
22
+ children: [],
23
+ };
24
+ parent.children = parent.children || [];
25
+ parent.children.push(dirNode);
26
+ dirByPath.set(dirPath, dirNode);
27
+ return dirNode;
28
+ };
29
+ for (const { key, size, lastModified } of keys) {
30
+ const normalizedKey = key.replace(/^\/+/, "");
31
+ const isFolderMarker = normalizedKey.endsWith("/");
32
+ const parts = normalizedKey.split("/").filter(Boolean);
33
+ if (parts.length === 0) {
34
+ continue;
35
+ }
36
+ const fullPath = parts.join("/");
37
+ if (isFolderMarker) {
38
+ ensureDir(fullPath);
39
+ continue;
40
+ }
41
+ const parent = ensureDir(parts.slice(0, -1).join("/"));
42
+ if (filePaths.has(fullPath)) {
43
+ continue;
44
+ }
45
+ filePaths.add(fullPath);
46
+ parent.children = parent.children || [];
47
+ parent.children.push({
48
+ name: parts[parts.length - 1],
49
+ path: fullPath,
50
+ type: "file",
51
+ size,
52
+ mtime: lastModified?.toISOString(),
53
+ });
54
+ }
55
+ return root;
56
+ }
57
+ export function storageTreeToTreeData(root) {
58
+ if (!root.children)
59
+ return [];
60
+ const mapNode = (node) => {
61
+ return {
62
+ id: node.path || node.name,
63
+ name: node.name || node.path,
64
+ size: node.size,
65
+ mtime: node.mtime,
66
+ children: node.children?.map(mapNode),
67
+ };
68
+ };
69
+ return root.children.map(mapNode);
70
+ }
71
+ export function sumTreeSize(node) {
72
+ let size = node.size ?? 0;
73
+ let fileCount = node.type === "file" ? 1 : 0;
74
+ for (const child of node.children ?? []) {
75
+ const childStats = sumTreeSize(child);
76
+ size += childStats.size;
77
+ fileCount += childStats.fileCount;
78
+ }
79
+ return { size, fileCount };
80
+ }
81
+ //# sourceMappingURL=storage-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-tree.js","sourceRoot":"","sources":["../../src/utils/storage-tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Signature utility for presigned client-side uploads.
3
+ * Mirrors utils/signature.ts (used for /authenticated delivery URLs) but the
4
+ * signed payload includes an expiry, since presigned uploads are meant to be
5
+ * handed to a browser and should stop working after a short window.
6
+ */
7
+ export declare const UPLOAD_SIGNATURE_LENGTH = 16;
8
+ /**
9
+ * Generates a cryptographically secure signature for a presigned upload.
10
+ * @param folder - The destination folder the upload is scoped to (empty string for root)
11
+ * @param expires - Unix timestamp (seconds) after which the signature is no longer valid
12
+ * @param secret - The API secret key
13
+ * @returns The signature (first UPLOAD_SIGNATURE_LENGTH characters of HMAC-SHA256)
14
+ */
15
+ export declare function generateUploadSignature(folder: string, expires: number, secret: string): string;
16
+ /**
17
+ * Verifies a presigned upload signature using timing-safe comparison.
18
+ * @param providedSignature - The signature submitted with the upload
19
+ * @param folder - The destination folder submitted with the upload
20
+ * @param expires - The expiry timestamp submitted with the upload
21
+ * @param secret - The API secret key
22
+ * @returns True if the signature is valid, false otherwise
23
+ */
24
+ export declare function verifyUploadSignature(providedSignature: string, folder: string, expires: number, secret: string): boolean;
25
+ //# sourceMappingURL=upload-signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-signature.d.ts","sourceRoot":"","sources":["../../src/utils/upload-signature.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,CAUR;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CA2BT"}
@@ -0,0 +1,56 @@
1
+ import crypto from 'crypto';
2
+ /**
3
+ * Signature utility for presigned client-side uploads.
4
+ * Mirrors utils/signature.ts (used for /authenticated delivery URLs) but the
5
+ * signed payload includes an expiry, since presigned uploads are meant to be
6
+ * handed to a browser and should stop working after a short window.
7
+ */
8
+ export const UPLOAD_SIGNATURE_LENGTH = 16; // 64 bits of entropy
9
+ /**
10
+ * Generates a cryptographically secure signature for a presigned upload.
11
+ * @param folder - The destination folder the upload is scoped to (empty string for root)
12
+ * @param expires - Unix timestamp (seconds) after which the signature is no longer valid
13
+ * @param secret - The API secret key
14
+ * @returns The signature (first UPLOAD_SIGNATURE_LENGTH characters of HMAC-SHA256)
15
+ */
16
+ export function generateUploadSignature(folder, expires, secret) {
17
+ if (!secret) {
18
+ throw new Error('API_SECRET is required for signature generation');
19
+ }
20
+ const stringToSign = `${folder}:${expires}`;
21
+ const hmac = crypto.createHmac('sha256', secret);
22
+ hmac.update(stringToSign);
23
+ return hmac.digest('hex').substring(0, UPLOAD_SIGNATURE_LENGTH);
24
+ }
25
+ /**
26
+ * Verifies a presigned upload signature using timing-safe comparison.
27
+ * @param providedSignature - The signature submitted with the upload
28
+ * @param folder - The destination folder submitted with the upload
29
+ * @param expires - The expiry timestamp submitted with the upload
30
+ * @param secret - The API secret key
31
+ * @returns True if the signature is valid, false otherwise
32
+ */
33
+ export function verifyUploadSignature(providedSignature, folder, expires, secret) {
34
+ if (!secret || !providedSignature) {
35
+ return false;
36
+ }
37
+ if (providedSignature.length !== UPLOAD_SIGNATURE_LENGTH) {
38
+ return false;
39
+ }
40
+ if (!Number.isFinite(expires)) {
41
+ return false;
42
+ }
43
+ try {
44
+ const expectedSignature = generateUploadSignature(folder, expires, secret);
45
+ const providedBuffer = Buffer.from(providedSignature, 'utf8');
46
+ const expectedBuffer = Buffer.from(expectedSignature, 'utf8');
47
+ if (providedBuffer.length !== expectedBuffer.length) {
48
+ return false;
49
+ }
50
+ return crypto.timingSafeEqual(providedBuffer, expectedBuffer);
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ //# sourceMappingURL=upload-signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-signature.js","sourceRoot":"","sources":["../../src/utils/upload-signature.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC,CAAC,qBAAqB;AAEhE;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAc,EACd,OAAe,EACf,MAAc;IAEd,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC;IAE5C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,uBAAuB,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,iBAAyB,EACzB,MAAc,EACd,OAAe,EACf,MAAc;IAEd,IAAI,CAAC,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,iBAAiB,CAAC,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAE3E,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAE9D,IAAI,cAAc,CAAC,MAAM,KAAK,cAAc,CAAC,MAAM,EAAE,CAAC;YACpD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,MAAM,CAAC,eAAe,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { TransformFunction } from './types';
2
+ /**
3
+ * Automatically downscale videos larger than 720p to reduce processing load
4
+ * This prevents high-resolution videos (4K, 5K, 8K) from overwhelming the system
5
+ * Only applies if no explicit resize parameters are provided
6
+ *
7
+ * Uses 720p instead of 1080p for faster processing of very large videos (8K, 5K)
8
+ * - 8K (7680x4320) -> 720p = 98% pixel reduction (vs 94% to 1080p)
9
+ * - Processing time reduced by ~50% compared to 1080p output
10
+ */
11
+ export declare const applyAutoDownscale: TransformFunction;
12
+ //# sourceMappingURL=auto-downscale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-downscale.d.ts","sourceRoot":"","sources":["../../../src/utils/video/auto-downscale.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,iBAyBhC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Automatically downscale videos larger than 720p to reduce processing load
3
+ * This prevents high-resolution videos (4K, 5K, 8K) from overwhelming the system
4
+ * Only applies if no explicit resize parameters are provided
5
+ *
6
+ * Uses 720p instead of 1080p for faster processing of very large videos (8K, 5K)
7
+ * - 8K (7680x4320) -> 720p = 98% pixel reduction (vs 94% to 1080p)
8
+ * - Processing time reduced by ~50% compared to 1080p output
9
+ */
10
+ export const applyAutoDownscale = (command, context) => {
11
+ // Skip for thumbnail extraction
12
+ if (context.isThumbnail) {
13
+ return command;
14
+ }
15
+ const { resize, width, height } = context.params;
16
+ // Skip if user has explicitly specified dimensions
17
+ if (resize || width !== undefined || height !== undefined) {
18
+ return command;
19
+ }
20
+ // Auto-downscale to 720p max to reduce CPU/memory usage
21
+ // Using 720p instead of 1080p significantly speeds up processing of 8K/5K videos
22
+ // scale="min(1280\,iw):min(720\,ih)" means:
23
+ // - Width: minimum of 1280 or original width
24
+ // - Height: minimum of 720 or original height
25
+ // - Force even dimensions (required for h264 encoding)
26
+ const filter = "scale='min(1280,iw)':'min(720,ih)':force_original_aspect_ratio=decrease,scale=trunc(iw/2)*2:trunc(ih/2)*2";
27
+ return command.videoFilters(filter);
28
+ };
29
+ //# sourceMappingURL=auto-downscale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-downscale.js","sourceRoot":"","sources":["../../../src/utils/video/auto-downscale.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAsB,CACnD,OAAO,EACP,OAAO,EACP,EAAE;IACF,gCAAgC;IAChC,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjD,mDAAmD;IACnD,IAAI,MAAM,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1D,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,wDAAwD;IACxD,iFAAiF;IACjF,4CAA4C;IAC5C,6CAA6C;IAC7C,8CAA8C;IAC9C,uDAAuD;IACvD,MAAM,MAAM,GAAG,2GAA2G,CAAC;IAE3H,OAAO,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { VideoContext, TransformFunction } from './types';
2
+ /**
3
+ * Builder class for constructing and executing ffmpeg commands
4
+ * Provides a fluent interface for applying multiple transformations
5
+ */
6
+ export declare class VideoCommandBuilder {
7
+ private command;
8
+ private context;
9
+ constructor(context: VideoContext);
10
+ /**
11
+ * Apply one or more transformation functions to the ffmpeg command
12
+ * Returns this for method chaining
13
+ */
14
+ apply(...transforms: TransformFunction[]): this;
15
+ /**
16
+ * Execute the ffmpeg command and return the output buffer
17
+ * Handles cleanup of temporary files
18
+ * Includes a 5-minute timeout to handle large videos (4K, 8K)
19
+ */
20
+ execute(): Promise<Buffer>;
21
+ }
22
+ //# sourceMappingURL=command-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-builder.d.ts","sourceRoot":"","sources":["../../../src/utils/video/command-builder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAG/D;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,EAAE,YAAY;IAmBjC;;;OAGG;IACH,KAAK,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAO/C;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;CAqCjC"}
@@ -0,0 +1,79 @@
1
+ import ffmpeg from 'fluent-ffmpeg';
2
+ import { readFile, unlink, rmdir } from 'fs/promises';
3
+ import { FFMPEG_THREADS, FFMPEG_NICENESS } from './config.js';
4
+ /**
5
+ * Builder class for constructing and executing ffmpeg commands
6
+ * Provides a fluent interface for applying multiple transformations
7
+ */
8
+ export class VideoCommandBuilder {
9
+ command;
10
+ context;
11
+ constructor(context) {
12
+ this.context = context;
13
+ // niceness lowers ffmpeg's scheduling priority so encoding never starves
14
+ // the HTTP event loop; threads are capped to the container's effective
15
+ // CPUs (leaving one for serving) instead of a hardcoded value
16
+ this.command = ffmpeg(context.inputPath, { niceness: FFMPEG_NICENESS })
17
+ .output(context.outputPath)
18
+ .addOption('-threads', String(FFMPEG_THREADS));
19
+ // -movflags and -max_muxing_queue_size are MOV/MP4 container options and are
20
+ // incompatible with image output formats (image2 muxer used for thumbnails).
21
+ // Only apply them for video output.
22
+ if (!context.isImageOutput) {
23
+ this.command = this.command
24
+ .addOption('-movflags', '+faststart') // Optimize for web streaming
25
+ .addOption('-max_muxing_queue_size', '1024'); // Prevent buffer issues
26
+ }
27
+ }
28
+ /**
29
+ * Apply one or more transformation functions to the ffmpeg command
30
+ * Returns this for method chaining
31
+ */
32
+ apply(...transforms) {
33
+ for (const transform of transforms) {
34
+ this.command = transform(this.command, this.context);
35
+ }
36
+ return this;
37
+ }
38
+ /**
39
+ * Execute the ffmpeg command and return the output buffer
40
+ * Handles cleanup of temporary files
41
+ * Includes a 5-minute timeout to handle large videos (4K, 8K)
42
+ */
43
+ async execute() {
44
+ const TIMEOUT_MS = 300000; // 5 minutes (increased for 8K videos)
45
+ return new Promise((resolve, reject) => {
46
+ // Set timeout to kill ffmpeg if it takes too long
47
+ const timeoutId = setTimeout(() => {
48
+ this.command.kill('SIGKILL');
49
+ reject(new Error('Video processing timeout: exceeded 5 minutes. Try reducing video resolution or duration.'));
50
+ }, TIMEOUT_MS);
51
+ this.command
52
+ .on('end', async () => {
53
+ clearTimeout(timeoutId);
54
+ try {
55
+ // Read the output file
56
+ const buffer = await readFile(this.context.outputPath);
57
+ // Cleanup: remove output file and temp directory
58
+ await unlink(this.context.outputPath);
59
+ try {
60
+ await rmdir(this.context.tmpDir);
61
+ }
62
+ catch {
63
+ // Ignore if directory is not empty or already removed
64
+ }
65
+ resolve(buffer);
66
+ }
67
+ catch (error) {
68
+ reject(error);
69
+ }
70
+ })
71
+ .on('error', (error) => {
72
+ clearTimeout(timeoutId);
73
+ reject(new Error(`Video processing failed: ${error.message}`));
74
+ })
75
+ .run();
76
+ });
77
+ }
78
+ }
79
+ //# sourceMappingURL=command-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-builder.js","sourceRoot":"","sources":["../../../src/utils/video/command-builder.ts"],"names":[],"mappings":"AAAA,OAAO,MAAyB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3D;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACtB,OAAO,CAAgB;IACvB,OAAO,CAAe;IAE9B,YAAY,OAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,yEAAyE;QACzE,uEAAuE;QACvE,8DAA8D;QAC9D,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;aACpE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;aAC1B,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;QAEjD,6EAA6E;QAC7E,6EAA6E;QAC7E,oCAAoC;QACpC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO;iBACxB,SAAS,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,6BAA6B;iBAClE,SAAS,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC,CAAC,wBAAwB;QAC1E,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,GAAG,UAA+B;QACtC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,sCAAsC;QAEjE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,kDAAkD;YAClD,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBAChC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7B,MAAM,CAAC,IAAI,KAAK,CAAC,0FAA0F,CAAC,CAAC,CAAC;YAChH,CAAC,EAAE,UAAU,CAAC,CAAC;YAEf,IAAI,CAAC,OAAO;iBACT,EAAE,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;gBACpB,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,IAAI,CAAC;oBACH,uBAAuB;oBACvB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBAEvD,iDAAiD;oBACjD,MAAM,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBACtC,IAAI,CAAC;wBACH,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;oBACnC,CAAC;oBAAC,MAAM,CAAC;wBACP,sDAAsD;oBACxD,CAAC;oBAED,OAAO,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC;iBACD,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACrB,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACjE,CAAC,CAAC;iBACD,GAAG,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Video processing queue configuration
3
+ * These values can be overridden via environment variables
4
+ */
5
+ /**
6
+ * Effective CPU count (host CPUs or cgroup quota, whichever is lower)
7
+ */
8
+ export declare const EFFECTIVE_CPU_COUNT: number;
9
+ /**
10
+ * Number of threads each ffmpeg process may use.
11
+ * Leaves one CPU free for the HTTP serving event loop so video jobs never
12
+ * starve the API, or configured via VIDEO_FFMPEG_THREADS env variable.
13
+ */
14
+ export declare const FFMPEG_THREADS: number;
15
+ /**
16
+ * Niceness applied to spawned ffmpeg processes (0-19, higher = lower priority).
17
+ * Keeps the Node event loop responsive even when ffmpeg saturates the CPU.
18
+ * Configurable via VIDEO_FFMPEG_NICENESS env variable.
19
+ */
20
+ export declare const FFMPEG_NICENESS: number;
21
+ /**
22
+ * Maximum number of concurrent video jobs
23
+ * Auto-detected based on available memory and CPU (host or cgroup limit,
24
+ * whichever is lower), or configured via VIDEO_MAX_CONCURRENT env variable
25
+ */
26
+ export declare const MAX_CONCURRENT_JOBS: number;
27
+ /**
28
+ * Maximum number of retry attempts for failed jobs
29
+ */
30
+ export declare const JOB_RETRY_MAX: number;
31
+ /**
32
+ * Hours after which completed/error jobs are cleaned up
33
+ */
34
+ export declare const JOB_CLEANUP_HOURS: number;
35
+ /**
36
+ * Worker polling interval in milliseconds
37
+ */
38
+ export declare const WORKER_POLL_INTERVAL_MS: number;
39
+ /**
40
+ * Priority for thumbnail extraction jobs (lower = higher priority)
41
+ */
42
+ export declare const THUMBNAIL_PRIORITY = 1;
43
+ /**
44
+ * Priority for video transformation jobs
45
+ */
46
+ export declare const TRANSFORMATION_PRIORITY = 2;
47
+ /**
48
+ * Priority for other video jobs
49
+ */
50
+ export declare const LOW_PRIORITY = 3;
51
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/utils/video/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqHH;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAAoB,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAEW,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,eAAe,QAEtB,CAAC;AAEP;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAEqC,CAAC;AAOtE;;GAEG;AACH,eAAO,MAAM,aAAa,QAGzB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAG7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAGnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;GAEG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;GAEG;AACH,eAAO,MAAM,YAAY,IAAI,CAAC"}