@mevdragon/vidfarm-devcli 0.20.6 → 0.20.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/editor-capabilities/SKILL.md +15 -1
- package/.agents/skills/vidfarm-director/SKILL.md +106 -0
- package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
- package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +111 -0
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +243 -0
- package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
- package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
- package/.agents/skills/vidfarm-director/references/primitives.md +265 -0
- package/SKILL.director.md +476 -260
- package/dist/src/cli.js +362 -96
- package/dist/src/devcli/clips.js +15 -10
- package/dist/src/devcli/doctor.js +2 -2
- package/dist/src/devcli/local-backend.js +46 -2
- package/dist/src/services/clip-curation/cost.js +5 -1
- package/dist/src/services/clip-curation/gemini.js +10 -0
- package/package.json +30 -10
- package/SKILL.platform.md +0 -432
- package/demo/README.md +0 -28
- package/demo/dist/app.css +0 -1
- package/demo/dist/app.js +0 -1850
- package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
- package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
- package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
- package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
- package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
- package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
- package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
- package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +0 -7
- package/demo/dist/icons/timeline/captions.svg +0 -5
- package/demo/dist/icons/timeline/composition.svg +0 -12
- package/demo/dist/icons/timeline/image.svg +0 -18
- package/demo/dist/icons/timeline/music.svg +0 -10
- package/demo/dist/icons/timeline/text.svg +0 -3
- package/demo/dist/index.html +0 -15
- package/dist/src/account-pages-legacy.js +0 -9396
- package/dist/src/account-pages.js +0 -61
- package/dist/src/app.js +0 -21603
- package/dist/src/composition-runtime.js +0 -1053
- package/dist/src/config.js +0 -217
- package/dist/src/context.js +0 -447
- package/dist/src/dev-app-legacy.js +0 -739
- package/dist/src/dev-app.js +0 -6
- package/dist/src/devcli/migrate-local.js +0 -140
- package/dist/src/devcli/sync.js +0 -368
- package/dist/src/domain.js +0 -5
- package/dist/src/editor-chat-history.js +0 -82
- package/dist/src/editor-chat.js +0 -828
- package/dist/src/editor-dark-theme.js +0 -1128
- package/dist/src/frontend/debug.js +0 -71
- package/dist/src/frontend/discover-client.js +0 -130
- package/dist/src/frontend/discover-store.js +0 -23
- package/dist/src/frontend/file-directory.js +0 -1018
- package/dist/src/frontend/flockposter-cache-store.js +0 -124
- package/dist/src/frontend/homepage-client.js +0 -446
- package/dist/src/frontend/homepage-shared.js +0 -201
- package/dist/src/frontend/homepage-store.js +0 -66
- package/dist/src/frontend/homepage-view.js +0 -705
- package/dist/src/frontend/page-runtime-client.js +0 -132
- package/dist/src/frontend/page-runtime-store.js +0 -9
- package/dist/src/frontend/sentry.js +0 -42
- package/dist/src/frontend/template-editor-chat.js +0 -4181
- package/dist/src/help-page.js +0 -346
- package/dist/src/homepage.js +0 -1458
- package/dist/src/index.js +0 -16
- package/dist/src/instrument.js +0 -30
- package/dist/src/landing-page.js +0 -384
- package/dist/src/page-runtime.js +0 -2
- package/dist/src/page-shell.js +0 -1452
- package/dist/src/primitive-context.js +0 -416
- package/dist/src/primitive-registry.js +0 -3940
- package/dist/src/primitive-sdk.js +0 -4
- package/dist/src/primitives/hyperframes-media.js +0 -108
- package/dist/src/react-page-shell.js +0 -35
- package/dist/src/ready-post-schedule-component.js +0 -1540
- package/dist/src/registry.js +0 -296
- package/dist/src/reskin/agency-page.js +0 -299
- package/dist/src/reskin/calendar-page.js +0 -568
- package/dist/src/reskin/chat-page.js +0 -942
- package/dist/src/reskin/discover-page.js +0 -1788
- package/dist/src/reskin/document.js +0 -1587
- package/dist/src/reskin/help-page.js +0 -357
- package/dist/src/reskin/index-page.js +0 -62
- package/dist/src/reskin/inpaint-clipper-page.js +0 -890
- package/dist/src/reskin/inpaint-page.js +0 -2554
- package/dist/src/reskin/inpaint-video-page.js +0 -1339
- package/dist/src/reskin/job-runs-page.js +0 -477
- package/dist/src/reskin/library-page.js +0 -1634
- package/dist/src/reskin/login-page.js +0 -262
- package/dist/src/reskin/portfolio-page.js +0 -687
- package/dist/src/reskin/pricing-page.js +0 -390
- package/dist/src/reskin/settings-page.js +0 -732
- package/dist/src/reskin/theme.js +0 -711
- package/dist/src/runtime.js +0 -35
- package/dist/src/services/api-call-history.js +0 -249
- package/dist/src/services/auth.js +0 -152
- package/dist/src/services/billing-pricing.js +0 -39
- package/dist/src/services/billing.js +0 -241
- package/dist/src/services/cast.js +0 -127
- package/dist/src/services/chat-threads.js +0 -92
- package/dist/src/services/clip-records.js +0 -250
- package/dist/src/services/clip-search.js +0 -77
- package/dist/src/services/clip-vectors.js +0 -125
- package/dist/src/services/composition-sanitize.js +0 -124
- package/dist/src/services/composition-watch.js +0 -79
- package/dist/src/services/elevenlabs.js +0 -222
- package/dist/src/services/file-directory.js +0 -117
- package/dist/src/services/fork-access.js +0 -93
- package/dist/src/services/fork-manifest.js +0 -42
- package/dist/src/services/ghostcut.js +0 -179
- package/dist/src/services/hyperframes.js +0 -3654
- package/dist/src/services/job-capacity.js +0 -14
- package/dist/src/services/job-logs.js +0 -197
- package/dist/src/services/jobs.js +0 -136
- package/dist/src/services/local-dynamo.js +0 -0
- package/dist/src/services/media-processing.js +0 -766
- package/dist/src/services/primitive-media-lambda.js +0 -280
- package/dist/src/services/providers.js +0 -2748
- package/dist/src/services/rate-limits.js +0 -262
- package/dist/src/services/scene-annotations.js +0 -32
- package/dist/src/services/serverless-auth.js +0 -382
- package/dist/src/services/serverless-jobs.js +0 -1084
- package/dist/src/services/serverless-provider-keys.js +0 -409
- package/dist/src/services/serverless-records.js +0 -1515
- package/dist/src/services/serverless-template-configs.js +0 -75
- package/dist/src/services/storage.js +0 -461
- package/dist/src/services/swipe-customize.js +0 -437
- package/dist/src/services/template-certification.js +0 -413
- package/dist/src/services/template-loader.js +0 -99
- package/dist/src/services/template-runtime-bundles.js +0 -217
- package/dist/src/services/template-sources.js +0 -1017
- package/dist/src/services/upstream.js +0 -248
- package/dist/src/services/video-normalization.js +0 -2
- package/dist/src/services/webhooks.js +0 -62
- package/dist/src/template-editor-pages.js +0 -2576
- package/dist/src/template-editor-shell.js +0 -2893
- package/dist/src/template-sdk.js +0 -4
- package/dist/src/worker.js +0 -17
- package/public/assets/discover-client-app.js +0 -1
- package/public/assets/file-directory-app.js +0 -3
- package/public/assets/homepage-app.js +0 -54
- package/public/assets/homepage-client-app.js +0 -80
- package/public/assets/page-runtime-client-app.js +0 -94
- package/public/assets/placeholders/scene-placeholder.png +0 -0
- package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
- package/src/assets/SELLING_WITH_HOOKS.md +0 -377
- package/src/assets/SELLING_WITH_VSLS.md +0 -606
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo-vidfarm.png +0 -0
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import * as Sentry from "@sentry/node";
|
|
3
|
-
import { config } from "./config.js";
|
|
4
|
-
import { devErrorFields, devLog } from "./lib/dev-log.js";
|
|
5
|
-
import { createId } from "./lib/ids.js";
|
|
6
|
-
import { jobLogStore } from "./services/job-logs.js";
|
|
7
|
-
import { RenderInProgressError } from "./services/hyperframes.js";
|
|
8
|
-
import { estimateNormalizationCostUsd, normalizeVideoAsset } from "./services/video-normalization.js";
|
|
9
|
-
export function createPrimitiveJobContext(input) {
|
|
10
|
-
const prefix = input.storage.primitiveJobPrefix(input.primitive.id, input.customer.id, input.job.id);
|
|
11
|
-
const buildArtifactUrl = (storageKey, fallbackUrl) => fallbackUrl ?? input.storage.getPublicUrl(storageKey) ?? buildPlatformMediaUrl(storageKey);
|
|
12
|
-
// Billing records are written after the billable work (render, normalize,
|
|
13
|
-
// etc.) has already succeeded. A transient billing failure must never fail
|
|
14
|
-
// the job — the customer would retry and pay for the expensive work twice.
|
|
15
|
-
const recordBillingNonFatal = async (record) => {
|
|
16
|
-
try {
|
|
17
|
-
await input.billing.record(record);
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
Sentry.captureException(error);
|
|
21
|
-
devLog("billing.record_failed_nonfatal", {
|
|
22
|
-
job_id: input.job.id,
|
|
23
|
-
customer_id: input.customer.id,
|
|
24
|
-
template_id: input.primitive.id,
|
|
25
|
-
cost_center_slug: record.costCenterSlug ?? null,
|
|
26
|
-
idempotency_key: record.idempotencyKey ?? null,
|
|
27
|
-
...devErrorFields(error)
|
|
28
|
-
}, "warn");
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
return {
|
|
32
|
-
env: config.isProduction ? "production" : "development",
|
|
33
|
-
customer: input.customer,
|
|
34
|
-
logger: {
|
|
35
|
-
debug(message, metadata = {}) {
|
|
36
|
-
jobLogStore.addEvent({
|
|
37
|
-
id: createId("evt"),
|
|
38
|
-
jobId: input.job.id,
|
|
39
|
-
level: "debug",
|
|
40
|
-
message,
|
|
41
|
-
metadata,
|
|
42
|
-
progress: null,
|
|
43
|
-
artifactKey: null
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
info(message, metadata = {}) {
|
|
47
|
-
jobLogStore.addEvent({
|
|
48
|
-
id: createId("evt"),
|
|
49
|
-
jobId: input.job.id,
|
|
50
|
-
level: "info",
|
|
51
|
-
message,
|
|
52
|
-
metadata,
|
|
53
|
-
progress: null,
|
|
54
|
-
artifactKey: null
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
warn(message, metadata = {}) {
|
|
58
|
-
jobLogStore.addEvent({
|
|
59
|
-
id: createId("evt"),
|
|
60
|
-
jobId: input.job.id,
|
|
61
|
-
level: "warn",
|
|
62
|
-
message,
|
|
63
|
-
metadata,
|
|
64
|
-
progress: null,
|
|
65
|
-
artifactKey: null
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
error(message, metadata = {}) {
|
|
69
|
-
jobLogStore.addEvent({
|
|
70
|
-
id: createId("evt"),
|
|
71
|
-
jobId: input.job.id,
|
|
72
|
-
level: "error",
|
|
73
|
-
message,
|
|
74
|
-
metadata,
|
|
75
|
-
progress: null,
|
|
76
|
-
artifactKey: null
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
progress(progress, message, metadata = {}) {
|
|
80
|
-
jobLogStore.addEvent({
|
|
81
|
-
id: createId("evt"),
|
|
82
|
-
jobId: input.job.id,
|
|
83
|
-
level: "info",
|
|
84
|
-
message,
|
|
85
|
-
metadata,
|
|
86
|
-
progress,
|
|
87
|
-
artifactKey: typeof metadata.artifactKey === "string" ? metadata.artifactKey : null
|
|
88
|
-
});
|
|
89
|
-
void input.jobs.updateJobStatus({
|
|
90
|
-
id: input.job.id,
|
|
91
|
-
status: "running",
|
|
92
|
-
progress
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
storage: {
|
|
97
|
-
async putJson(key, value) {
|
|
98
|
-
const stored = await input.storage.putJson(`${prefix}/${key}`, value);
|
|
99
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
100
|
-
await insertArtifact("json", stored.key, artifactUrl, {});
|
|
101
|
-
return { ...stored, url: artifactUrl };
|
|
102
|
-
},
|
|
103
|
-
async putText(key, value, contentType = "text/plain; charset=utf-8") {
|
|
104
|
-
const stored = await input.storage.putText(`${prefix}/${key}`, value, contentType);
|
|
105
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
106
|
-
await insertArtifact("text", stored.key, artifactUrl, { contentType });
|
|
107
|
-
return { ...stored, url: artifactUrl };
|
|
108
|
-
},
|
|
109
|
-
async putBuffer(key, value, options = {}) {
|
|
110
|
-
const stored = await input.storage.putBuffer(`${prefix}/${key}`, value, options.contentType);
|
|
111
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
112
|
-
await insertArtifact(options.kind ?? "binary", stored.key, artifactUrl, {
|
|
113
|
-
...(options.metadata ?? {}),
|
|
114
|
-
contentType: options.contentType ?? null
|
|
115
|
-
});
|
|
116
|
-
return { ...stored, url: artifactUrl };
|
|
117
|
-
},
|
|
118
|
-
getPublicUrl(key) {
|
|
119
|
-
return input.storage.getPublicUrl(`${prefix}/${key}`);
|
|
120
|
-
},
|
|
121
|
-
resolveKey(key) {
|
|
122
|
-
return `${prefix}/${key}`;
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
billing: {
|
|
126
|
-
async record(record) {
|
|
127
|
-
await recordBillingNonFatal({
|
|
128
|
-
customerId: input.customer.id,
|
|
129
|
-
jobId: input.job.id,
|
|
130
|
-
tracer: input.job.tracer,
|
|
131
|
-
templateId: input.primitive.id,
|
|
132
|
-
type: record.type,
|
|
133
|
-
costUsd: record.costUsd,
|
|
134
|
-
chargeUsd: record.chargeUsd ?? Number((record.costUsd * 1.2).toFixed(4)),
|
|
135
|
-
costCenterSlug: record.costCenterSlug,
|
|
136
|
-
idempotencyKey: record.idempotencyKey,
|
|
137
|
-
occurredAtMs: record.occurredAtMs,
|
|
138
|
-
metadata: record.metadata
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
providers: {
|
|
143
|
-
listAvailableProviders() {
|
|
144
|
-
return input.providers.listAvailableProviders(input.customer.id);
|
|
145
|
-
},
|
|
146
|
-
async generateText(request) {
|
|
147
|
-
return input.providers.generateText({
|
|
148
|
-
customerId: input.customer.id,
|
|
149
|
-
jobId: input.job.id,
|
|
150
|
-
workerId: input.workerId,
|
|
151
|
-
...request
|
|
152
|
-
});
|
|
153
|
-
},
|
|
154
|
-
async generateImage(request) {
|
|
155
|
-
return input.providers.generateImage({
|
|
156
|
-
customerId: input.customer.id,
|
|
157
|
-
jobId: input.job.id,
|
|
158
|
-
workerId: input.workerId,
|
|
159
|
-
...request
|
|
160
|
-
});
|
|
161
|
-
},
|
|
162
|
-
async editImage(request) {
|
|
163
|
-
return input.providers.editImage({
|
|
164
|
-
customerId: input.customer.id,
|
|
165
|
-
jobId: input.job.id,
|
|
166
|
-
workerId: input.workerId,
|
|
167
|
-
...request
|
|
168
|
-
});
|
|
169
|
-
},
|
|
170
|
-
async inpaintImage(request) {
|
|
171
|
-
return input.providers.inpaintImage({
|
|
172
|
-
customerId: input.customer.id,
|
|
173
|
-
jobId: input.job.id,
|
|
174
|
-
workerId: input.workerId,
|
|
175
|
-
...request
|
|
176
|
-
});
|
|
177
|
-
},
|
|
178
|
-
async generateVideo(request) {
|
|
179
|
-
return input.providers.generateVideo({
|
|
180
|
-
customerId: input.customer.id,
|
|
181
|
-
jobId: input.job.id,
|
|
182
|
-
workerId: input.workerId,
|
|
183
|
-
...request
|
|
184
|
-
});
|
|
185
|
-
},
|
|
186
|
-
async generateSpeech(request) {
|
|
187
|
-
return input.providers.generateSpeech({
|
|
188
|
-
customerId: input.customer.id,
|
|
189
|
-
jobId: input.job.id,
|
|
190
|
-
workerId: input.workerId,
|
|
191
|
-
...request
|
|
192
|
-
});
|
|
193
|
-
},
|
|
194
|
-
async transcribeSpeech(request) {
|
|
195
|
-
return input.providers.transcribeSpeech({
|
|
196
|
-
customerId: input.customer.id,
|
|
197
|
-
jobId: input.job.id,
|
|
198
|
-
workerId: input.workerId,
|
|
199
|
-
...request
|
|
200
|
-
});
|
|
201
|
-
},
|
|
202
|
-
async generateMusic(request) {
|
|
203
|
-
return input.providers.generateMusic({
|
|
204
|
-
customerId: input.customer.id,
|
|
205
|
-
jobId: input.job.id,
|
|
206
|
-
workerId: input.workerId,
|
|
207
|
-
...request
|
|
208
|
-
});
|
|
209
|
-
},
|
|
210
|
-
async listElevenLabsVoices(request) {
|
|
211
|
-
return input.providers.listElevenLabsVoices({
|
|
212
|
-
customerId: input.customer.id,
|
|
213
|
-
...request
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
hyperframes: {
|
|
218
|
-
async render(request) {
|
|
219
|
-
const hyperframesOutputKey = `renders/vidfarm/primitives/${input.primitive.id}/${input.customer.id}/${input.job.id}/output.mp4`;
|
|
220
|
-
// Re-entrant, non-blocking render: the first invocation dispatches the
|
|
221
|
-
// render to its own Step Functions state machine and throws
|
|
222
|
-
// RenderInProgressError so the job runner parks as `waiting_for_render`;
|
|
223
|
-
// Step Functions waits + re-invokes, and a later invocation resumes the
|
|
224
|
-
// SAME render (keyed by the deterministic execution name) and finalizes
|
|
225
|
-
// once it terminates. No single Lambda holds the render open for its
|
|
226
|
-
// 15-minute lifetime.
|
|
227
|
-
const step = await input.hyperframes.renderStep({
|
|
228
|
-
...request,
|
|
229
|
-
outputKey: hyperframesOutputKey,
|
|
230
|
-
executionName: renderExecutionName(input.job.id)
|
|
231
|
-
});
|
|
232
|
-
if (step.state === "running") {
|
|
233
|
-
throw new RenderInProgressError({
|
|
234
|
-
executionArn: step.executionArn,
|
|
235
|
-
renderId: step.renderId,
|
|
236
|
-
waitSeconds: config.RENDER_POLL_WAIT_SECONDS,
|
|
237
|
-
progressFraction: step.progressFraction
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
if (step.state === "failed") {
|
|
241
|
-
throw new Error(step.message);
|
|
242
|
-
}
|
|
243
|
-
const render = step;
|
|
244
|
-
let outputUrl = render.outputUrl;
|
|
245
|
-
let platformOutputKey = null;
|
|
246
|
-
const localOutputLocation = typeof render.metadata.outputLocation === "string"
|
|
247
|
-
? render.metadata.outputLocation
|
|
248
|
-
: null;
|
|
249
|
-
if (render.outputS3Uri) {
|
|
250
|
-
const output = await input.hyperframes.readRenderOutput(render.outputS3Uri);
|
|
251
|
-
platformOutputKey = `${prefix}/${request.outputKey || "output.mp4"}`;
|
|
252
|
-
const stored = await input.storage.putBuffer(platformOutputKey, output.bytes, output.contentType);
|
|
253
|
-
outputUrl = buildArtifactUrl(stored.key, stored.url);
|
|
254
|
-
await insertArtifact("video", stored.key, outputUrl, {
|
|
255
|
-
operation: "hyperframes-render",
|
|
256
|
-
contentType: output.contentType,
|
|
257
|
-
renderId: render.renderId,
|
|
258
|
-
outputS3Uri: render.outputS3Uri
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
else if (localOutputLocation) {
|
|
262
|
-
// In-process (local) render writes the MP4 to ephemeral local disk and
|
|
263
|
-
// returns no S3 URI. On serverless boxes that disk is per-invocation
|
|
264
|
-
// and the `renders/` prefix is not publicly served, so the raw
|
|
265
|
-
// outputUrl 404s ("Artifact not found"). Persist the bytes into the
|
|
266
|
-
// public job-scoped prefix — S3 on cloud, local disk on serve boxes —
|
|
267
|
-
// exactly like the cloud branch above.
|
|
268
|
-
const bytes = await readFile(localOutputLocation);
|
|
269
|
-
platformOutputKey = `${prefix}/${request.outputKey || "output.mp4"}`;
|
|
270
|
-
const stored = await input.storage.putBuffer(platformOutputKey, bytes, "video/mp4");
|
|
271
|
-
outputUrl = buildArtifactUrl(stored.key, stored.url);
|
|
272
|
-
await insertArtifact("video", stored.key, outputUrl, {
|
|
273
|
-
operation: "hyperframes-render-local",
|
|
274
|
-
contentType: "video/mp4",
|
|
275
|
-
renderId: render.renderId,
|
|
276
|
-
outputLocation: localOutputLocation
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
const estimatedCostUsd = typeof render.metadata.estimatedCostUsd === "number" ? render.metadata.estimatedCostUsd : null;
|
|
280
|
-
const chargeUsd = typeof render.metadata.chargeUsd === "number" ? render.metadata.chargeUsd : null;
|
|
281
|
-
if (estimatedCostUsd !== null && estimatedCostUsd > 0) {
|
|
282
|
-
await recordBillingNonFatal({
|
|
283
|
-
customerId: input.customer.id,
|
|
284
|
-
jobId: input.job.id,
|
|
285
|
-
tracer: input.job.tracer,
|
|
286
|
-
templateId: input.primitive.id,
|
|
287
|
-
type: "render",
|
|
288
|
-
costUsd: estimatedCostUsd,
|
|
289
|
-
chargeUsd: typeof chargeUsd === "number" ? chargeUsd : undefined,
|
|
290
|
-
costCenterSlug: "hyperframes_lambda",
|
|
291
|
-
idempotencyKey: `hyperframes_lambda:${render.renderId}`,
|
|
292
|
-
occurredAtMs: Date.now(),
|
|
293
|
-
metadata: {
|
|
294
|
-
...render.metadata,
|
|
295
|
-
render_id: render.renderId,
|
|
296
|
-
output_url: outputUrl,
|
|
297
|
-
signed_output_url: render.outputUrl,
|
|
298
|
-
output_s3_uri: render.outputS3Uri,
|
|
299
|
-
platform_output_key: platformOutputKey
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return {
|
|
304
|
-
renderId: render.renderId,
|
|
305
|
-
outputUrl,
|
|
306
|
-
outputS3Uri: render.outputS3Uri,
|
|
307
|
-
metadata: {
|
|
308
|
-
...render.metadata,
|
|
309
|
-
outputUrl,
|
|
310
|
-
signedOutputUrl: render.outputUrl,
|
|
311
|
-
platformOutputKey
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
},
|
|
315
|
-
async renderStill(request) {
|
|
316
|
-
const extension = (request.imageFormat ?? "png") === "jpeg" ? "jpg" : (request.imageFormat ?? "png");
|
|
317
|
-
const localOutputKey = `renders/vidfarm/primitives/${input.primitive.id}/${input.customer.id}/${input.job.id}/${request.outputKey || `output.${extension}`}`;
|
|
318
|
-
const rendered = await input.hyperframes.renderStill({
|
|
319
|
-
compositionHtml: request.compositionHtml,
|
|
320
|
-
compositionId: request.compositionId,
|
|
321
|
-
imageFormat: request.imageFormat,
|
|
322
|
-
outputKey: localOutputKey
|
|
323
|
-
});
|
|
324
|
-
// renderStill writes to ephemeral local disk under the non-public
|
|
325
|
-
// `renders/` prefix. Persist it into the public job-scoped prefix (S3 on
|
|
326
|
-
// cloud, local disk on serve boxes) so the artifact URL resolves — same
|
|
327
|
-
// fix as render() above.
|
|
328
|
-
const platformKey = `${prefix}/${request.outputKey || `output.${extension}`}`;
|
|
329
|
-
const contentType = extension === "jpg" ? "image/jpeg" : extension === "webp" ? "image/webp" : "image/png";
|
|
330
|
-
const bytes = await readFile(rendered.outputLocation);
|
|
331
|
-
const stored = await input.storage.putBuffer(platformKey, bytes, contentType);
|
|
332
|
-
const outputUrl = buildArtifactUrl(stored.key, stored.url);
|
|
333
|
-
await insertArtifact("image", stored.key, outputUrl, {
|
|
334
|
-
operation: "hyperframes-render-still",
|
|
335
|
-
renderId: rendered.renderId,
|
|
336
|
-
imageFormat: request.imageFormat ?? "png"
|
|
337
|
-
});
|
|
338
|
-
return {
|
|
339
|
-
renderId: rendered.renderId,
|
|
340
|
-
outputUrl,
|
|
341
|
-
metadata: rendered.metadata
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
media: {
|
|
346
|
-
async normalizeVideo(request) {
|
|
347
|
-
const normalized = await normalizeVideoAsset({
|
|
348
|
-
sourceUrl: request.sourceUrl,
|
|
349
|
-
maxWidth: request.maxWidth,
|
|
350
|
-
maxHeight: request.maxHeight,
|
|
351
|
-
crf: request.crf,
|
|
352
|
-
preset: request.preset,
|
|
353
|
-
maxVideoBitrateKbps: request.maxVideoBitrateKbps,
|
|
354
|
-
audioBitrateKbps: request.audioBitrateKbps
|
|
355
|
-
});
|
|
356
|
-
const stored = await input.storage.putBuffer(`${prefix}/${request.outputKey}`, normalized.bytes, normalized.contentType);
|
|
357
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
358
|
-
await insertArtifact("video", stored.key, artifactUrl, {
|
|
359
|
-
...(request.metadata ?? {}),
|
|
360
|
-
operation: "normalize-video",
|
|
361
|
-
contentType: normalized.contentType,
|
|
362
|
-
normalization: normalized.metadata
|
|
363
|
-
});
|
|
364
|
-
await recordBillingNonFatal({
|
|
365
|
-
customerId: input.customer.id,
|
|
366
|
-
jobId: input.job.id,
|
|
367
|
-
tracer: input.job.tracer,
|
|
368
|
-
templateId: input.primitive.id,
|
|
369
|
-
type: "cpu_estimate",
|
|
370
|
-
costUsd: estimateNormalizationCostUsd(normalized.metadata),
|
|
371
|
-
costCenterSlug: "video_normalization",
|
|
372
|
-
idempotencyKey: `video_normalization:${input.job.id}:${request.outputKey}`,
|
|
373
|
-
occurredAtMs: Date.now(),
|
|
374
|
-
metadata: {
|
|
375
|
-
...(request.metadata ?? {}),
|
|
376
|
-
operation: "normalize-video",
|
|
377
|
-
source_url: request.sourceUrl,
|
|
378
|
-
output_key: `${prefix}/${request.outputKey}`,
|
|
379
|
-
normalization: normalized.metadata
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
return {
|
|
383
|
-
...stored,
|
|
384
|
-
url: artifactUrl,
|
|
385
|
-
contentType: normalized.contentType,
|
|
386
|
-
normalization: normalized.metadata
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
async function insertArtifact(kind, storageKey, publicUrl, metadata) {
|
|
392
|
-
await input.jobs.insertArtifact({
|
|
393
|
-
id: createId("art"),
|
|
394
|
-
jobId: input.job.id,
|
|
395
|
-
customerId: input.customer.id,
|
|
396
|
-
templateId: input.primitive.id,
|
|
397
|
-
kind,
|
|
398
|
-
storageKey,
|
|
399
|
-
publicUrl,
|
|
400
|
-
metadata
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
function buildPlatformMediaUrl(storageKey) {
|
|
405
|
-
return `${config.PUBLIC_BASE_URL.replace(/\/$/, "")}/template-media?key=${encodeURIComponent(storageKey)}`;
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Deterministic Step Functions execution name for a job's render. Stable across
|
|
409
|
-
* job-runner re-invocations so each poll resumes the same render execution
|
|
410
|
-
* rather than starting a new one. Step Functions execution names allow
|
|
411
|
-
* [A-Za-z0-9-_] and cap at 80 chars.
|
|
412
|
-
*/
|
|
413
|
-
function renderExecutionName(jobId) {
|
|
414
|
-
return `vfr-${jobId.replace(/[^A-Za-z0-9_-]/g, "-")}`.slice(0, 80);
|
|
415
|
-
}
|
|
416
|
-
//# sourceMappingURL=primitive-context.js.map
|