@mevdragon/vidfarm-devcli 0.2.13 → 0.3.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 (200) hide show
  1. package/README.md +11 -4
  2. package/SKILL.director.md +97 -0
  3. package/SKILL.platform.md +66 -6
  4. package/dist/src/cli.js +137 -1524
  5. package/dist/src/dev-serve.js +169 -0
  6. package/package.json +5 -24
  7. package/.env.example +0 -60
  8. package/auto-create-hyperframe-templates/AUTO_CREATE_HYPERFRAME_TEMPLATES.md +0 -275
  9. package/auto-create-hyperframe-templates/extractor-system-prompt.md +0 -128
  10. package/auto-create-hyperframe-templates/input-manifest.schema.json +0 -56
  11. package/auto-create-hyperframe-templates/lambda-memory-estimator.md +0 -118
  12. package/auto-create-hyperframe-templates/production-graph.schema.json +0 -314
  13. package/auto-create-hyperframe-templates/runbook.md +0 -198
  14. package/auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs +0 -368
  15. package/auto-create-hyperframe-templates/template-plan.schema.json +0 -311
  16. package/auto-create-hyperframe-templates/template-planner-prompt.md +0 -144
  17. package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +0 -599
  18. package/auto-create-templates/extractor-system-prompt.md +0 -119
  19. package/auto-create-templates/production-graph.schema.json +0 -276
  20. package/auto-create-templates/runbook.md +0 -140
  21. package/auto-create-templates/template-plan.schema.json +0 -301
  22. package/auto-create-templates/template-planner-prompt.md +0 -137
  23. package/dist/src/account-pages-legacy.js +0 -9336
  24. package/dist/src/account-pages-legacy.js.map +0 -1
  25. package/dist/src/account-pages.js +0 -61
  26. package/dist/src/account-pages.js.map +0 -1
  27. package/dist/src/app.js +0 -14023
  28. package/dist/src/app.js.map +0 -1
  29. package/dist/src/cli.js.map +0 -1
  30. package/dist/src/composition-runtime.js +0 -581
  31. package/dist/src/composition-runtime.js.map +0 -1
  32. package/dist/src/config.js +0 -140
  33. package/dist/src/config.js.map +0 -1
  34. package/dist/src/context.js +0 -492
  35. package/dist/src/context.js.map +0 -1
  36. package/dist/src/dev-app-legacy.js +0 -694
  37. package/dist/src/dev-app-legacy.js.map +0 -1
  38. package/dist/src/dev-app.js +0 -6
  39. package/dist/src/dev-app.js.map +0 -1
  40. package/dist/src/domain.js +0 -2
  41. package/dist/src/domain.js.map +0 -1
  42. package/dist/src/editor-chat-history.js +0 -82
  43. package/dist/src/editor-chat-history.js.map +0 -1
  44. package/dist/src/editor-chat.js +0 -311
  45. package/dist/src/editor-chat.js.map +0 -1
  46. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  47. package/dist/src/frontend/flockposter-cache-store.js.map +0 -1
  48. package/dist/src/frontend/homepage-client.js +0 -117
  49. package/dist/src/frontend/homepage-client.js.map +0 -1
  50. package/dist/src/frontend/homepage-shared.js +0 -4
  51. package/dist/src/frontend/homepage-shared.js.map +0 -1
  52. package/dist/src/frontend/homepage-store.js +0 -28
  53. package/dist/src/frontend/homepage-store.js.map +0 -1
  54. package/dist/src/frontend/homepage-view.js +0 -260
  55. package/dist/src/frontend/homepage-view.js.map +0 -1
  56. package/dist/src/frontend/page-runtime-client.js +0 -103
  57. package/dist/src/frontend/page-runtime-client.js.map +0 -1
  58. package/dist/src/frontend/page-runtime-store.js +0 -9
  59. package/dist/src/frontend/page-runtime-store.js.map +0 -1
  60. package/dist/src/frontend/sentry.js +0 -42
  61. package/dist/src/frontend/sentry.js.map +0 -1
  62. package/dist/src/frontend/template-editor-chat.js +0 -3767
  63. package/dist/src/frontend/template-editor-chat.js.map +0 -1
  64. package/dist/src/help-page.js +0 -333
  65. package/dist/src/help-page.js.map +0 -1
  66. package/dist/src/homepage.js +0 -919
  67. package/dist/src/homepage.js.map +0 -1
  68. package/dist/src/hyperframes/composition.js +0 -180
  69. package/dist/src/hyperframes/composition.js.map +0 -1
  70. package/dist/src/index.js +0 -16
  71. package/dist/src/index.js.map +0 -1
  72. package/dist/src/instrument.js +0 -30
  73. package/dist/src/instrument.js.map +0 -1
  74. package/dist/src/lib/crypto.js +0 -45
  75. package/dist/src/lib/crypto.js.map +0 -1
  76. package/dist/src/lib/dev-log.js +0 -54
  77. package/dist/src/lib/dev-log.js.map +0 -1
  78. package/dist/src/lib/display-name.js +0 -11
  79. package/dist/src/lib/display-name.js.map +0 -1
  80. package/dist/src/lib/ids.js +0 -24
  81. package/dist/src/lib/ids.js.map +0 -1
  82. package/dist/src/lib/images.js +0 -19
  83. package/dist/src/lib/images.js.map +0 -1
  84. package/dist/src/lib/json.js +0 -15
  85. package/dist/src/lib/json.js.map +0 -1
  86. package/dist/src/lib/template-dna.js +0 -543
  87. package/dist/src/lib/template-dna.js.map +0 -1
  88. package/dist/src/lib/template-paths.js +0 -28
  89. package/dist/src/lib/template-paths.js.map +0 -1
  90. package/dist/src/lib/template-style-options.js +0 -203
  91. package/dist/src/lib/template-style-options.js.map +0 -1
  92. package/dist/src/lib/time.js +0 -7
  93. package/dist/src/lib/time.js.map +0 -1
  94. package/dist/src/lib/video-quality-harness.js +0 -60
  95. package/dist/src/lib/video-quality-harness.js.map +0 -1
  96. package/dist/src/page-runtime.js +0 -2
  97. package/dist/src/page-runtime.js.map +0 -1
  98. package/dist/src/page-shell.js +0 -2393
  99. package/dist/src/page-shell.js.map +0 -1
  100. package/dist/src/primitive-context.js +0 -299
  101. package/dist/src/primitive-context.js.map +0 -1
  102. package/dist/src/primitive-registry.js +0 -2181
  103. package/dist/src/primitive-registry.js.map +0 -1
  104. package/dist/src/primitive-sdk.js +0 -4
  105. package/dist/src/primitive-sdk.js.map +0 -1
  106. package/dist/src/primitives/remotion/html-image.js +0 -30
  107. package/dist/src/primitives/remotion/html-image.js.map +0 -1
  108. package/dist/src/primitives/remotion/media-slideshow.js +0 -60
  109. package/dist/src/primitives/remotion/media-slideshow.js.map +0 -1
  110. package/dist/src/react-page-shell.js +0 -35
  111. package/dist/src/react-page-shell.js.map +0 -1
  112. package/dist/src/ready-post-schedule-component.js +0 -1515
  113. package/dist/src/ready-post-schedule-component.js.map +0 -1
  114. package/dist/src/registry.js +0 -296
  115. package/dist/src/registry.js.map +0 -1
  116. package/dist/src/runtime.js +0 -31
  117. package/dist/src/runtime.js.map +0 -1
  118. package/dist/src/services/api-call-history.js +0 -249
  119. package/dist/src/services/api-call-history.js.map +0 -1
  120. package/dist/src/services/auth.js +0 -152
  121. package/dist/src/services/auth.js.map +0 -1
  122. package/dist/src/services/billing.js +0 -231
  123. package/dist/src/services/billing.js.map +0 -1
  124. package/dist/src/services/chat-threads.js +0 -92
  125. package/dist/src/services/chat-threads.js.map +0 -1
  126. package/dist/src/services/fork-access.js +0 -94
  127. package/dist/src/services/fork-access.js.map +0 -1
  128. package/dist/src/services/ghostcut.js +0 -132
  129. package/dist/src/services/ghostcut.js.map +0 -1
  130. package/dist/src/services/hyperframes.js +0 -1014
  131. package/dist/src/services/hyperframes.js.map +0 -1
  132. package/dist/src/services/job-capacity.js +0 -14
  133. package/dist/src/services/job-capacity.js.map +0 -1
  134. package/dist/src/services/job-logs.js +0 -197
  135. package/dist/src/services/job-logs.js.map +0 -1
  136. package/dist/src/services/jobs.js +0 -136
  137. package/dist/src/services/jobs.js.map +0 -1
  138. package/dist/src/services/media-processing.js +0 -743
  139. package/dist/src/services/media-processing.js.map +0 -1
  140. package/dist/src/services/primitive-media-lambda.js +0 -280
  141. package/dist/src/services/primitive-media-lambda.js.map +0 -1
  142. package/dist/src/services/providers.js +0 -2870
  143. package/dist/src/services/providers.js.map +0 -1
  144. package/dist/src/services/rate-limits.js +0 -237
  145. package/dist/src/services/rate-limits.js.map +0 -1
  146. package/dist/src/services/remotion.js +0 -688
  147. package/dist/src/services/remotion.js.map +0 -1
  148. package/dist/src/services/serverless-auth.js +0 -374
  149. package/dist/src/services/serverless-auth.js.map +0 -1
  150. package/dist/src/services/serverless-jobs.js +0 -1074
  151. package/dist/src/services/serverless-jobs.js.map +0 -1
  152. package/dist/src/services/serverless-provider-keys.js +0 -401
  153. package/dist/src/services/serverless-provider-keys.js.map +0 -1
  154. package/dist/src/services/serverless-records.js +0 -1086
  155. package/dist/src/services/serverless-records.js.map +0 -1
  156. package/dist/src/services/serverless-template-configs.js +0 -67
  157. package/dist/src/services/serverless-template-configs.js.map +0 -1
  158. package/dist/src/services/storage.js +0 -382
  159. package/dist/src/services/storage.js.map +0 -1
  160. package/dist/src/services/template-certification.js +0 -475
  161. package/dist/src/services/template-certification.js.map +0 -1
  162. package/dist/src/services/template-loader.js +0 -99
  163. package/dist/src/services/template-loader.js.map +0 -1
  164. package/dist/src/services/template-runtime-bundles.js +0 -217
  165. package/dist/src/services/template-runtime-bundles.js.map +0 -1
  166. package/dist/src/services/template-sources.js +0 -1017
  167. package/dist/src/services/template-sources.js.map +0 -1
  168. package/dist/src/services/video-normalization.js +0 -2
  169. package/dist/src/services/video-normalization.js.map +0 -1
  170. package/dist/src/services/webhooks.js +0 -62
  171. package/dist/src/services/webhooks.js.map +0 -1
  172. package/dist/src/template-editor-pages.js +0 -6624
  173. package/dist/src/template-editor-pages.js.map +0 -1
  174. package/dist/src/template-editor-shell.js +0 -2713
  175. package/dist/src/template-editor-shell.js.map +0 -1
  176. package/dist/src/template-sdk.js +0 -4
  177. package/dist/src/template-sdk.js.map +0 -1
  178. package/dist/src/worker.js +0 -17
  179. package/dist/src/worker.js.map +0 -1
  180. package/public/assets/homepage-app.js +0 -54
  181. package/public/assets/homepage-client-app.js +0 -80
  182. package/public/assets/page-runtime-client-app.js +0 -94
  183. package/readme.secret.md +0 -89
  184. package/templates/vidfarm_template_0000/README.md +0 -106
  185. package/templates/vidfarm_template_0000/SKILL.md +0 -266
  186. package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
  187. package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
  188. package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
  189. package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
  190. package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
  191. package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
  192. package/templates/vidfarm_template_0000/composition.json +0 -11
  193. package/templates/vidfarm_template_0000/src/lib/images.js +0 -221
  194. package/templates/vidfarm_template_0000/src/remotion/Root.js +0 -33
  195. package/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
  196. package/templates/vidfarm_template_0000/src/sdk.js +0 -3
  197. package/templates/vidfarm_template_0000/src/style-options.js +0 -200
  198. package/templates/vidfarm_template_0000/src/template-dna.js +0 -9
  199. package/templates/vidfarm_template_0000/src/template.js +0 -1566
  200. package/templates/vidfarm_template_0000/template.config.json +0 -21
@@ -1,688 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import path from "node:path";
3
- import { bundle } from "@remotion/bundler";
4
- import { getRenderProgress, renderMediaOnLambda } from "@remotion/lambda";
5
- import { getCompositions, renderMedia, renderStill } from "@remotion/renderer";
6
- import { config } from "../config.js";
7
- import { createId } from "../lib/ids.js";
8
- const REQUIRED_VIDEO_FPS = 30;
9
- const RENDER_MARKUP_MULTIPLIER = 1.2;
10
- const RENDER_ASSET_PREFLIGHT_TIMEOUT_MS = 10_000;
11
- const RENDER_ASSET_PREFLIGHT_MAX_FAILURES = 5;
12
- const ABSOLUTE_MAX_REMOTE_VIDEO_DURATION_SECONDS = 10 * 60;
13
- const ABSOLUTE_MAX_DIRECT_REMOTE_VIDEO_BYTES = 50 * 1024 * 1024;
14
- const REMOTE_VIDEO_DOWNLOAD_BUDGET_BYTES = 1250 * 1024 * 1024;
15
- const DEFAULT_PARALLEL_LAMBDA_TARGET = 6;
16
- const MAX_PARALLEL_LAMBDA_TARGET = 24;
17
- const SMALL_REMOTE_VIDEO_BYTES = 12 * 1024 * 1024;
18
- const MEDIUM_REMOTE_VIDEO_BYTES = 25 * 1024 * 1024;
19
- export class RemotionService {
20
- async render(input) {
21
- const fps = input.fps ?? REQUIRED_VIDEO_FPS;
22
- if (fps !== REQUIRED_VIDEO_FPS) {
23
- throw new Error(`Vidfarm renders must be ${REQUIRED_VIDEO_FPS}fps. Received ${fps}fps.`);
24
- }
25
- const preflightAssets = await preflightRenderAssets(input.preflightUrls ?? collectPreflightUrls(input.inputProps), { requireRemoteHttpUrls: Boolean(input.preflightUrls) });
26
- const serveUrl = nonBlank(input.serveUrl) ??
27
- nonBlank(config.REMOTION_SERVE_URL) ??
28
- inferHostedServeUrl(input.compositionId);
29
- const hasHostedLambdaConfig = Boolean(nonBlank(config.REMOTION_FUNCTION_NAME) && serveUrl && nonBlank(config.REMOTION_BUCKET_NAME));
30
- if (config.isProduction && config.REMOTION_MODE !== "lambda") {
31
- throw new Error("Production video rendering must use lambda mode.");
32
- }
33
- if (input.entryPoint
34
- && (config.REMOTION_MODE === "local"
35
- || (config.REMOTION_MODE === "auto" && !hasHostedLambdaConfig)
36
- || (!config.isProduction && config.REMOTION_MODE === "auto"))) {
37
- if (config.isProduction) {
38
- throw new Error("Production video rendering cannot fall back to local mode.");
39
- }
40
- return this.renderLocal({
41
- compositionId: input.compositionId,
42
- entryPoint: input.entryPoint,
43
- outputKey: input.outputKey,
44
- fps,
45
- inputProps: input.inputProps
46
- });
47
- }
48
- if (config.REMOTION_MODE === "mock") {
49
- if (config.isProduction) {
50
- throw new Error("Production video rendering cannot use mock mode.");
51
- }
52
- return {
53
- renderId: createId("render"),
54
- outputUrl: null,
55
- metadata: {
56
- mode: "mock",
57
- compositionId: input.compositionId,
58
- fps,
59
- inputProps: input.inputProps
60
- }
61
- };
62
- }
63
- if (!hasHostedLambdaConfig) {
64
- throw new Error(config.isProduction
65
- ? "Cloud render is not configured."
66
- : "Cloud render is not configured. Set REMOTION_FUNCTION_NAME, REMOTION_SERVE_URL, and REMOTION_BUCKET_NAME, or provide a local entryPoint.");
67
- }
68
- const hostedServeUrl = serveUrl;
69
- const hostedFunctionName = nonBlank(config.REMOTION_FUNCTION_NAME);
70
- const hostedBucketName = nonBlank(config.REMOTION_BUCKET_NAME);
71
- const requestedFramesPerLambda = Number.isFinite(input.framesPerLambda)
72
- ? Math.max(1, Math.floor(input.framesPerLambda))
73
- : 40;
74
- const composition = await this.resolveComposition({
75
- compositionId: input.compositionId,
76
- source: hostedServeUrl,
77
- inputProps: input.inputProps,
78
- });
79
- this.assertExpectedFps({
80
- compositionId: input.compositionId,
81
- actualFps: composition.fps,
82
- expectedFps: fps
83
- });
84
- const renderPolicy = planLambdaRenderPolicy({
85
- inputProps: input.inputProps,
86
- preflightAssets,
87
- requestedFramesPerLambda,
88
- durationInFrames: composition.durationInFrames,
89
- fps
90
- });
91
- const framesPerLambda = renderPolicy.framesPerLambda;
92
- const render = await renderMediaOnLambda({
93
- region: config.REMOTION_REGION,
94
- functionName: hostedFunctionName,
95
- serveUrl: hostedServeUrl,
96
- composition: input.compositionId,
97
- inputProps: input.inputProps,
98
- codec: "h264",
99
- privacy: "public",
100
- forceBucketName: hostedBucketName,
101
- forceWidth: 1080,
102
- forceHeight: 1920,
103
- framesPerLambda,
104
- crf: 18,
105
- overwrite: true
106
- });
107
- const startedAt = Date.now();
108
- for (;;) {
109
- const progress = await getRenderProgress({
110
- region: config.REMOTION_REGION,
111
- functionName: hostedFunctionName,
112
- bucketName: hostedBucketName,
113
- renderId: render.renderId
114
- });
115
- const fatalError = summarizeFatalRenderError(progress);
116
- if (fatalError) {
117
- throw new Error(`Cloud render ${render.renderId} failed: ${fatalError}`);
118
- }
119
- if (progress.done) {
120
- if (!progress.outputFile) {
121
- throw new Error(`Cloud render ${render.renderId} completed without an output file.`);
122
- }
123
- const estimatedCostUsd = progress.costs.accruedSoFar;
124
- const chargeUsd = roundUsd(estimatedCostUsd * RENDER_MARKUP_MULTIPLIER);
125
- return {
126
- renderId: render.renderId,
127
- outputUrl: progress.outputFile,
128
- metadata: {
129
- mode: "lambda",
130
- bucketName: render.bucketName,
131
- compositionId: input.compositionId,
132
- fps,
133
- framesPerLambda,
134
- estimatedLambdaCount: renderPolicy.estimatedLambdaCount,
135
- mediaPolicy: renderPolicy,
136
- serveUrl,
137
- estimatedCostUsd,
138
- chargeUsd,
139
- costDisplay: progress.costs.displayCost,
140
- costCurrency: progress.costs.currency,
141
- costDisclaimer: progress.costs.disclaimer,
142
- estimatedBillingDurationInMilliseconds: progress.estimatedBillingDurationInMilliseconds,
143
- lambdasInvoked: progress.lambdasInvoked,
144
- overallProgress: progress.overallProgress,
145
- renderSize: progress.renderSize
146
- }
147
- };
148
- }
149
- if (Date.now() - startedAt > 10 * 60 * 1000) {
150
- throw new Error(`Timed out waiting for cloud render ${render.renderId} output.`);
151
- }
152
- await new Promise((resolve) => setTimeout(resolve, 5000));
153
- }
154
- }
155
- async renderLocal(input) {
156
- const renderId = createId("render");
157
- const bundled = await getOrCreateBundle(input.entryPoint);
158
- const composition = await this.resolveComposition({
159
- source: bundled,
160
- compositionId: input.compositionId,
161
- inputProps: input.inputProps
162
- });
163
- this.assertExpectedFps({
164
- compositionId: input.compositionId,
165
- actualFps: composition.fps,
166
- expectedFps: input.fps
167
- });
168
- const outputKey = input.outputKey ?? `renders/${renderId}.mp4`;
169
- const outputLocation = path.join(config.VIDFARM_DATA_DIR, "storage", outputKey);
170
- mkdirSync(path.dirname(outputLocation), { recursive: true });
171
- await renderMedia({
172
- serveUrl: bundled,
173
- composition,
174
- codec: "h264",
175
- inputProps: input.inputProps,
176
- outputLocation,
177
- overwrite: true
178
- });
179
- return {
180
- renderId,
181
- outputUrl: `${config.PUBLIC_BASE_URL}/storage/${encodeURIComponent(outputKey)}`,
182
- metadata: {
183
- mode: "local",
184
- compositionId: input.compositionId,
185
- entryPoint: input.entryPoint,
186
- fps: input.fps,
187
- outputKey
188
- }
189
- };
190
- }
191
- async renderStill(input) {
192
- const renderId = createId("render");
193
- const bundled = await getOrCreateBundle(input.entryPoint);
194
- const composition = await this.resolveComposition({
195
- source: bundled,
196
- compositionId: input.compositionId,
197
- inputProps: input.inputProps
198
- });
199
- const format = input.imageFormat ?? "png";
200
- const extension = format === "jpeg" ? "jpg" : format;
201
- const outputKey = input.outputKey ?? `renders/${renderId}.${extension}`;
202
- const outputLocation = path.join(config.VIDFARM_DATA_DIR, "storage", outputKey);
203
- mkdirSync(path.dirname(outputLocation), { recursive: true });
204
- await renderStill({
205
- serveUrl: bundled,
206
- composition,
207
- inputProps: input.inputProps,
208
- output: outputLocation,
209
- imageFormat: format,
210
- overwrite: true
211
- });
212
- return {
213
- renderId,
214
- outputUrl: `${config.PUBLIC_BASE_URL}/storage/${encodeURIComponent(outputKey)}`,
215
- metadata: {
216
- mode: "local_still",
217
- compositionId: input.compositionId,
218
- entryPoint: input.entryPoint,
219
- outputKey,
220
- imageFormat: format
221
- }
222
- };
223
- }
224
- async resolveComposition(input) {
225
- const compositions = await getCompositions(input.source, {
226
- inputProps: input.inputProps
227
- });
228
- const composition = compositions.find((item) => item.id === input.compositionId);
229
- if (!composition) {
230
- throw new Error(`Composition ${input.compositionId} not found in ${input.source}`);
231
- }
232
- return composition;
233
- }
234
- assertExpectedFps(input) {
235
- if (input.actualFps !== input.expectedFps) {
236
- throw new Error(`Composition ${input.compositionId} is ${input.actualFps}fps, but Vidfarm requires ${input.expectedFps}fps output.`);
237
- }
238
- }
239
- }
240
- const bundleCache = new Map();
241
- function getOrCreateBundle(entryPoint) {
242
- const existing = bundleCache.get(entryPoint);
243
- if (existing) {
244
- return existing;
245
- }
246
- const created = bundle({
247
- entryPoint,
248
- onProgress: () => undefined
249
- });
250
- bundleCache.set(entryPoint, created);
251
- return created;
252
- }
253
- function roundUsd(value) {
254
- return Number(value.toFixed(6));
255
- }
256
- function collectPreflightUrls(value, path = "inputProps") {
257
- if (typeof value === "string") {
258
- return isRemoteHttpUrl(value) ? [{ url: value, label: path }] : [];
259
- }
260
- if (Array.isArray(value)) {
261
- return value.flatMap((item, index) => collectPreflightUrls(item, `${path}[${index}]`));
262
- }
263
- if (!value || typeof value !== "object") {
264
- return [];
265
- }
266
- return Object.entries(value).flatMap(([key, item]) => (collectPreflightUrls(item, `${path}.${key}`)));
267
- }
268
- async function preflightRenderAssets(inputs, options = {}) {
269
- const normalized = inputs.map((input, index) => (typeof input === "string" ? { url: input, label: `preflightUrls[${index}]` } : input));
270
- if (options.requireRemoteHttpUrls) {
271
- const invalid = normalized
272
- .filter((input) => input.url.trim() && !isRemoteHttpUrl(input.url))
273
- .map((input) => `${input.label || "asset"} must be an absolute http(s) URL: ${input.url}`);
274
- if (invalid.length) {
275
- throw new Error(`Render asset preflight failed: ${invalid.join("; ")}`);
276
- }
277
- }
278
- const targets = dedupePreflightUrls(normalized.filter((input) => isRemoteHttpUrl(input.url)));
279
- if (!targets.length) {
280
- return [];
281
- }
282
- const failures = [];
283
- const assets = [];
284
- for (const target of targets) {
285
- const result = await preflightOneRenderAsset(target);
286
- assets.push(result.asset);
287
- const failure = result.failure;
288
- if (failure) {
289
- failures.push(failure.message);
290
- }
291
- }
292
- if (failures.length) {
293
- const shown = failures.slice(0, RENDER_ASSET_PREFLIGHT_MAX_FAILURES);
294
- const remaining = failures.length - shown.length;
295
- throw new Error([
296
- "Render asset preflight failed:",
297
- ...shown.map((failure) => `- ${failure}`),
298
- remaining > 0 ? `- ${remaining} more asset${remaining === 1 ? "" : "s"} failed preflight.` : null
299
- ].filter(Boolean).join("\n"));
300
- }
301
- return assets;
302
- }
303
- function dedupePreflightUrls(inputs) {
304
- const seen = new Set();
305
- const deduped = [];
306
- for (const input of inputs) {
307
- const key = `${input.label || ""}\n${input.url}`;
308
- if (seen.has(key)) {
309
- continue;
310
- }
311
- seen.add(key);
312
- deduped.push(input);
313
- }
314
- return deduped;
315
- }
316
- async function preflightOneRenderAsset(input) {
317
- const maxAttempts = isVidfarmTemplateMediaUrl(input.url) ? 4 : 1;
318
- let latest = null;
319
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
320
- latest = await preflightOneRenderAssetOnce(input);
321
- if (!latest.failure || !isRetryablePreflightFailure(latest.failure.status, input.url)) {
322
- return latest;
323
- }
324
- if (attempt < maxAttempts - 1) {
325
- await sleep(preflightRetryDelayMs(attempt));
326
- }
327
- }
328
- return latest ?? preflightOneRenderAssetOnce(input);
329
- }
330
- async function preflightOneRenderAssetOnce(input) {
331
- const headFailure = await requestRenderAsset(input, "HEAD");
332
- if (!headFailure.failure) {
333
- return headFailure;
334
- }
335
- if (!headFailure.failure.allowGetFallback) {
336
- return headFailure;
337
- }
338
- const getFailure = await requestRenderAsset(input, "GET");
339
- return {
340
- asset: mergeAssetMetadata(headFailure.asset, getFailure.asset),
341
- failure: getFailure.failure
342
- };
343
- }
344
- async function requestRenderAsset(input, method) {
345
- const controller = new AbortController();
346
- const timeout = setTimeout(() => controller.abort(), RENDER_ASSET_PREFLIGHT_TIMEOUT_MS);
347
- try {
348
- const response = await fetch(input.url, {
349
- method,
350
- signal: controller.signal,
351
- headers: method === "GET" ? { range: "bytes=0-0" } : undefined
352
- });
353
- const asset = extractPreflightAssetMetadata(input, response);
354
- if (response.ok) {
355
- return { asset, failure: null };
356
- }
357
- return {
358
- asset,
359
- failure: {
360
- allowGetFallback: method === "HEAD",
361
- status: response.status,
362
- message: `${input.label || "asset"} returned ${response.status} ${response.statusText || "HTTP error"} for ${formatUrlForError(input.url)}`
363
- }
364
- };
365
- }
366
- catch (error) {
367
- const message = error instanceof Error && error.name === "AbortError"
368
- ? `timed out after ${RENDER_ASSET_PREFLIGHT_TIMEOUT_MS / 1000}s`
369
- : error instanceof Error
370
- ? error.message
371
- : "unknown fetch error";
372
- return {
373
- asset: {
374
- url: input.url,
375
- label: input.label,
376
- contentType: null,
377
- contentLengthBytes: null
378
- },
379
- failure: {
380
- allowGetFallback: false,
381
- status: null,
382
- message: `${input.label || "asset"} could not be reached: ${message} (${formatUrlForError(input.url)})`
383
- }
384
- };
385
- }
386
- finally {
387
- clearTimeout(timeout);
388
- }
389
- }
390
- function isRetryablePreflightFailure(status, url) {
391
- return isVidfarmTemplateMediaUrl(url) && (status === 403 || status === 404 || status === 429 || status == null || status >= 500);
392
- }
393
- function preflightRetryDelayMs(attempt) {
394
- return Math.min(250 * (2 ** attempt), 2_000);
395
- }
396
- function sleep(ms) {
397
- return new Promise((resolve) => setTimeout(resolve, ms));
398
- }
399
- function isVidfarmTemplateMediaUrl(value) {
400
- try {
401
- const url = new URL(value);
402
- return url.pathname === "/template-media" && Boolean(url.searchParams.get("key"));
403
- }
404
- catch {
405
- return false;
406
- }
407
- }
408
- function isRemoteHttpUrl(value) {
409
- try {
410
- const url = new URL(value);
411
- return url.protocol === "http:" || url.protocol === "https:";
412
- }
413
- catch {
414
- return false;
415
- }
416
- }
417
- function formatUrlForError(url) {
418
- return url.length > 300 ? `${url.slice(0, 300)}...` : url;
419
- }
420
- function extractPreflightAssetMetadata(input, response) {
421
- return {
422
- url: input.url,
423
- label: input.label,
424
- contentType: normalizeHeaderValue(response.headers.get("content-type")),
425
- contentLengthBytes: parseContentLength(response.headers)
426
- };
427
- }
428
- function mergeAssetMetadata(primary, fallback) {
429
- return {
430
- ...primary,
431
- contentType: primary.contentType ?? fallback.contentType,
432
- contentLengthBytes: primary.contentLengthBytes ?? fallback.contentLengthBytes
433
- };
434
- }
435
- function normalizeHeaderValue(value) {
436
- const trimmed = value?.trim();
437
- return trimmed ? trimmed : null;
438
- }
439
- function parseContentLength(headers) {
440
- const contentRange = headers.get("content-range");
441
- if (contentRange) {
442
- const match = /\/(\d+)\s*$/u.exec(contentRange);
443
- if (match) {
444
- return Number.parseInt(match[1], 10);
445
- }
446
- }
447
- const contentLength = headers.get("content-length");
448
- if (!contentLength) {
449
- return null;
450
- }
451
- const parsed = Number.parseInt(contentLength, 10);
452
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
453
- }
454
- function planLambdaRenderPolicy(input) {
455
- const durationSeconds = input.durationInFrames / input.fps;
456
- const outputDimensions = inferOutputDimensions(input.inputProps);
457
- const outputPixels = outputDimensions.width != null && outputDimensions.height != null
458
- ? outputDimensions.width * outputDimensions.height
459
- : null;
460
- const remoteVideoAssets = collectRemoteVideoAssets(input.inputProps, input.preflightAssets);
461
- const sourceVideo = remoteVideoAssets.primary;
462
- const totalRemoteVideoBytes = remoteVideoAssets.totalBytes;
463
- if (durationSeconds > ABSOLUTE_MAX_REMOTE_VIDEO_DURATION_SECONDS) {
464
- throw new Error(`Render duration is ${formatDurationSeconds(durationSeconds)}, which exceeds the current Vidfarm remote-video platform limit of ${formatDurationSeconds(ABSOLUTE_MAX_REMOTE_VIDEO_DURATION_SECONDS)}. Trim the source video before rendering.`);
465
- }
466
- const sourceVideoBytes = sourceVideo?.contentLengthBytes ?? null;
467
- if (sourceVideoBytes != null && sourceVideoBytes > ABSOLUTE_MAX_DIRECT_REMOTE_VIDEO_BYTES) {
468
- throw new Error(`Remote video source is ${formatBytes(sourceVideoBytes)}, which exceeds the direct Vidfarm render limit of ${formatBytes(ABSOLUTE_MAX_DIRECT_REMOTE_VIDEO_BYTES)}. Run POST /api/v1/primitives/videos/normalize first, then pass the returned normalizedVideoUrl into the template render.`);
469
- }
470
- const maxSafeSegmentDurationSeconds = inferMaxSafeSegmentDurationSeconds({
471
- sourceVideoBytes,
472
- durationSeconds,
473
- outputPixels
474
- });
475
- const requiredLambdasByDuration = Math.max(1, Math.ceil(durationSeconds / maxSafeSegmentDurationSeconds));
476
- const maxLambdasByDownloadBudget = inferMaxLambdasByDownloadBudget(totalRemoteVideoBytes);
477
- const preferredLambdasByRequestedFrames = Math.max(1, Math.ceil(input.durationInFrames / Math.max(1, input.requestedFramesPerLambda)));
478
- if (requiredLambdasByDuration > maxLambdasByDownloadBudget) {
479
- const sourceDescriptor = sourceVideoBytes == null
480
- ? `${formatDurationSeconds(durationSeconds)} remote-video render`
481
- : `${formatBytes(sourceVideoBytes)} over ${formatDurationSeconds(durationSeconds)}`;
482
- throw new Error(`Remote-video render workload (${sourceDescriptor}) exceeds the current Vidfarm Lambda render envelope. The platform would need about ${requiredLambdasByDuration} chunks to keep per-chunk runtime safe, but remote media download policy caps this workload at ${maxLambdasByDownloadBudget} chunk${maxLambdasByDownloadBudget === 1 ? "" : "s"}. Trim, shorten, or pre-compress the source video before rendering.`);
483
- }
484
- const parallelLambdaTarget = inferParallelLambdaTarget({
485
- sourceVideoBytes,
486
- totalRemoteVideoBytes,
487
- outputPixels,
488
- preferredLambdasByRequestedFrames
489
- });
490
- const targetLambdas = Math.max(requiredLambdasByDuration, Math.min(preferredLambdasByRequestedFrames, maxLambdasByDownloadBudget, parallelLambdaTarget));
491
- const strategy = targetLambdas <= 1 ? "single" : targetLambdas <= 3 ? "few" : "parallel";
492
- const reason = describeLambdaPolicyReason({
493
- targetLambdas,
494
- preferredLambdasByRequestedFrames,
495
- maxSafeSegmentDurationSeconds,
496
- sourceVideoBytes,
497
- totalRemoteVideoBytes,
498
- durationSeconds
499
- });
500
- const adaptiveFramesPerLambda = Math.max(input.requestedFramesPerLambda, Math.ceil(input.durationInFrames / targetLambdas));
501
- return {
502
- framesPerLambda: Math.max(1, adaptiveFramesPerLambda),
503
- estimatedLambdaCount: Math.max(1, Math.ceil(input.durationInFrames / Math.max(1, adaptiveFramesPerLambda))),
504
- strategy,
505
- reason,
506
- sourceVideoUrl: sourceVideo?.url ?? null,
507
- sourceVideoBytes,
508
- totalRemoteVideoBytes,
509
- remoteVideoCount: remoteVideoAssets.assets.length,
510
- durationSeconds,
511
- outputWidth: outputDimensions.width,
512
- outputHeight: outputDimensions.height,
513
- outputPixels
514
- };
515
- }
516
- function collectRemoteVideoAssets(inputProps, assets) {
517
- const prioritizedLabels = new Set(collectRemoteVideoLabels(inputProps));
518
- const prioritized = assets.filter((asset) => prioritizedLabels.has(asset.label ?? ""));
519
- const candidates = prioritized.length ? prioritized : assets;
520
- const videoAssets = candidates.filter((asset) => isLikelyVideoAsset(asset));
521
- const totalBytes = videoAssets.reduce((sum, asset) => (sum == null || asset.contentLengthBytes == null ? null : sum + asset.contentLengthBytes), 0);
522
- return {
523
- primary: videoAssets[0] ?? null,
524
- assets: videoAssets,
525
- totalBytes
526
- };
527
- }
528
- function collectRemoteVideoLabels(value, path = "inputProps") {
529
- if (typeof value === "string") {
530
- return isRemoteVideoLabel(path) && isRemoteHttpUrl(value) ? [path] : [];
531
- }
532
- if (Array.isArray(value)) {
533
- return value.flatMap((item, index) => collectRemoteVideoLabels(item, `${path}[${index}]`));
534
- }
535
- if (!value || typeof value !== "object") {
536
- return [];
537
- }
538
- return Object.entries(value).flatMap(([key, item]) => (collectRemoteVideoLabels(item, `${path}.${key}`)));
539
- }
540
- function isRemoteVideoLabel(label) {
541
- return /(source_video_url|backgroundVideoUrl|background_video_url|video_url)$/u.test(label);
542
- }
543
- function isLikelyVideoAsset(asset) {
544
- if (asset.contentType?.toLowerCase().startsWith("video/")) {
545
- return true;
546
- }
547
- return /\.(mp4|mov|m4v|webm)(?:$|[?#])/iu.test(asset.url);
548
- }
549
- function inferMaxSafeSegmentDurationSeconds(input) {
550
- let maxSafeDurationSeconds = inferBaselineMaxSafeSegmentDurationSeconds({
551
- sourceVideoBytes: input.sourceVideoBytes,
552
- durationSeconds: input.durationSeconds
553
- });
554
- const outputPixels = input.outputPixels;
555
- if (outputPixels != null) {
556
- if (outputPixels >= 1920 * 1080) {
557
- maxSafeDurationSeconds = Math.min(maxSafeDurationSeconds, input.durationSeconds <= 30 ? 24 : 20);
558
- }
559
- else if (outputPixels >= 1280 * 720) {
560
- maxSafeDurationSeconds = Math.min(maxSafeDurationSeconds, input.durationSeconds <= 30 ? 36 : 30);
561
- }
562
- else if (outputPixels >= 854 * 480) {
563
- maxSafeDurationSeconds = Math.min(maxSafeDurationSeconds, 45);
564
- }
565
- }
566
- return maxSafeDurationSeconds;
567
- }
568
- function inferBaselineMaxSafeSegmentDurationSeconds(input) {
569
- const bytes = input.sourceVideoBytes;
570
- if (bytes == null) {
571
- return input.durationSeconds <= 30 ? 45 : 60;
572
- }
573
- if (bytes >= 400 * 1024 * 1024) {
574
- return 30;
575
- }
576
- if (bytes >= 250 * 1024 * 1024) {
577
- return input.durationSeconds <= 30 ? 30 : 25;
578
- }
579
- if (bytes >= 200 * 1024 * 1024) {
580
- return input.durationSeconds <= 30 ? 35 : 25;
581
- }
582
- if (bytes >= 150 * 1024 * 1024) {
583
- return input.durationSeconds <= 45 ? 45 : 30;
584
- }
585
- if (bytes >= 75 * 1024 * 1024) {
586
- return 90;
587
- }
588
- return 120;
589
- }
590
- function inferOutputDimensions(inputProps) {
591
- return {
592
- width: parsePositiveInteger(inputProps.output_width),
593
- height: parsePositiveInteger(inputProps.output_height)
594
- };
595
- }
596
- function parsePositiveInteger(value) {
597
- if (typeof value !== "number" || !Number.isFinite(value)) {
598
- return null;
599
- }
600
- const normalized = Math.floor(value);
601
- return normalized > 0 ? normalized : null;
602
- }
603
- function inferMaxLambdasByDownloadBudget(totalRemoteVideoBytes) {
604
- if (totalRemoteVideoBytes == null || totalRemoteVideoBytes <= 0) {
605
- return DEFAULT_PARALLEL_LAMBDA_TARGET;
606
- }
607
- return Math.max(1, Math.min(MAX_PARALLEL_LAMBDA_TARGET, Math.floor(REMOTE_VIDEO_DOWNLOAD_BUDGET_BYTES / totalRemoteVideoBytes)));
608
- }
609
- function inferParallelLambdaTarget(input) {
610
- const requestedTarget = Math.max(1, input.preferredLambdasByRequestedFrames);
611
- const totalRemoteBytes = input.totalRemoteVideoBytes ?? input.sourceVideoBytes;
612
- const outputPixels = input.outputPixels ?? 0;
613
- if (totalRemoteBytes != null && totalRemoteBytes <= SMALL_REMOTE_VIDEO_BYTES) {
614
- return Math.min(MAX_PARALLEL_LAMBDA_TARGET, Math.max(DEFAULT_PARALLEL_LAMBDA_TARGET, requestedTarget));
615
- }
616
- if (totalRemoteBytes != null
617
- && totalRemoteBytes <= MEDIUM_REMOTE_VIDEO_BYTES
618
- && outputPixels <= 1080 * 1920) {
619
- return Math.min(18, Math.max(DEFAULT_PARALLEL_LAMBDA_TARGET, requestedTarget));
620
- }
621
- if (totalRemoteBytes != null
622
- && totalRemoteBytes <= ABSOLUTE_MAX_DIRECT_REMOTE_VIDEO_BYTES
623
- && outputPixels <= 1080 * 1920) {
624
- return Math.min(12, Math.max(DEFAULT_PARALLEL_LAMBDA_TARGET, requestedTarget));
625
- }
626
- return DEFAULT_PARALLEL_LAMBDA_TARGET;
627
- }
628
- function describeLambdaPolicyReason(input) {
629
- const sourcePart = input.sourceVideoBytes == null ? "unknown source size" : `${formatBytes(input.sourceVideoBytes)} source`;
630
- const totalPart = input.totalRemoteVideoBytes == null ? "unknown total remote bytes" : `${formatBytes(input.totalRemoteVideoBytes)} total remote video`;
631
- return `${sourcePart}, ${formatDurationSeconds(input.durationSeconds)} duration, requested ~${input.preferredLambdasByRequestedFrames} chunk${input.preferredLambdasByRequestedFrames === 1 ? "" : "s"}, target <=${formatDurationSeconds(input.maxSafeSegmentDurationSeconds)} per chunk, ${totalPart}`;
632
- }
633
- function formatBytes(bytes) {
634
- if (bytes >= 1024 * 1024) {
635
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
636
- }
637
- if (bytes >= 1024) {
638
- return `${(bytes / 1024).toFixed(1)}KB`;
639
- }
640
- return `${bytes}B`;
641
- }
642
- function formatDurationSeconds(seconds) {
643
- return `${seconds.toFixed(seconds >= 10 ? 0 : 1)}s`;
644
- }
645
- function summarizeFatalRenderError(progress) {
646
- if (!progress || typeof progress !== "object") {
647
- return null;
648
- }
649
- const maybeProgress = progress;
650
- if (maybeProgress.fatalErrorEncountered !== true) {
651
- return null;
652
- }
653
- const errors = Array.isArray(maybeProgress.errors) ? maybeProgress.errors : [];
654
- const messages = errors
655
- .map((error) => summarizeRenderError(error))
656
- .filter(Boolean)
657
- .slice(0, 3);
658
- return messages.length ? messages.join(" | ") : "Remotion reported a fatal render error.";
659
- }
660
- function summarizeRenderError(error) {
661
- if (!error || typeof error !== "object") {
662
- return typeof error === "string" ? error : null;
663
- }
664
- const record = error;
665
- const message = firstString(record.message, record.name, record.stack, record.explanation)
666
- ?? firstString(record.error?.message);
667
- const chunk = typeof record.chunk === "number" || typeof record.chunk === "string"
668
- ? `chunk ${record.chunk}: `
669
- : "";
670
- return message ? `${chunk}${message}` : JSON.stringify(record).slice(0, 500);
671
- }
672
- function firstString(...values) {
673
- return values.find((value) => typeof value === "string" && value.trim().length > 0)?.trim() ?? null;
674
- }
675
- function nonBlank(value) {
676
- const trimmed = value?.trim();
677
- return trimmed ? trimmed : undefined;
678
- }
679
- function inferHostedServeUrl(compositionId) {
680
- const bucketName = nonBlank(config.REMOTION_BUCKET_NAME);
681
- if (!bucketName) {
682
- return undefined;
683
- }
684
- const siteName = nonBlank(config.REMOTION_SITE_NAME) ?? compositionId;
685
- const encodedSiteName = siteName.split("/").map(encodeURIComponent).join("/");
686
- return `https://${bucketName}.s3.${config.REMOTION_REGION}.amazonaws.com/sites/${encodedSiteName}/index.html`;
687
- }
688
- //# sourceMappingURL=remotion.js.map