@mevdragon/vidfarm-devcli 0.5.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/demo/README.md +28 -0
- package/demo/dist/app.css +1 -0
- package/demo/dist/app.js +1184 -0
- package/demo/dist/chunks/chunk-DXB73IDG.js +1 -0
- package/demo/dist/chunks/chunk-S7OWAJDS.js +36 -0
- package/demo/dist/chunks/chunk-VTIBZ6AN.js +1 -0
- package/demo/dist/chunks/dist-ADSJKBVE.js +332 -0
- package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +1 -0
- package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +425 -0
- package/demo/dist/chunks/lib-XAQ37YOE.js +1 -0
- package/demo/dist/chunks/src-TJ2QYA4U.js +207 -0
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +7 -0
- package/demo/dist/icons/timeline/captions.svg +5 -0
- package/demo/dist/icons/timeline/composition.svg +12 -0
- package/demo/dist/icons/timeline/image.svg +18 -0
- package/demo/dist/icons/timeline/music.svg +10 -0
- package/demo/dist/icons/timeline/text.svg +3 -0
- package/demo/dist/index.html +15 -0
- package/dist/src/account-pages-legacy.js +9396 -0
- package/dist/src/account-pages.js +61 -0
- package/dist/src/app.js +14378 -0
- package/dist/src/cli.js +1 -1
- package/dist/src/composition-runtime.js +613 -0
- package/dist/src/config.js +166 -0
- package/dist/src/context.js +447 -0
- package/dist/src/dev-app-legacy.js +739 -0
- package/dist/src/dev-app.js +6 -0
- package/dist/src/domain.js +2 -0
- package/dist/src/editor-chat-history.js +82 -0
- package/dist/src/editor-chat.js +449 -0
- package/dist/src/editor-dark-theme.js +1128 -0
- package/dist/src/frontend/debug.js +71 -0
- package/dist/src/frontend/flockposter-cache-store.js +124 -0
- package/dist/src/frontend/homepage-client.js +182 -0
- package/dist/src/frontend/homepage-shared.js +4 -0
- package/dist/src/frontend/homepage-store.js +28 -0
- package/dist/src/frontend/homepage-view.js +547 -0
- package/dist/src/frontend/page-runtime-client.js +132 -0
- package/dist/src/frontend/page-runtime-store.js +9 -0
- package/dist/src/frontend/sentry.js +42 -0
- package/dist/src/frontend/template-editor-chat.js +3960 -0
- package/dist/src/help-page.js +346 -0
- package/dist/src/homepage.js +1235 -0
- package/dist/src/hyperframes/composition.js +180 -0
- package/dist/src/index.js +16 -0
- package/dist/src/instrument.js +30 -0
- package/dist/src/lib/crypto.js +45 -0
- package/dist/src/lib/dev-log.js +54 -0
- package/dist/src/lib/display-name.js +11 -0
- package/dist/src/lib/ids.js +24 -0
- package/dist/src/lib/images.js +19 -0
- package/dist/src/lib/json.js +15 -0
- package/dist/src/lib/package-root.js +47 -0
- package/dist/src/lib/template-paths.js +28 -0
- package/dist/src/lib/time.js +7 -0
- package/dist/src/lib/url-clean.js +85 -0
- package/dist/src/page-runtime.js +2 -0
- package/dist/src/page-shell.js +1381 -0
- package/dist/src/primitive-context.js +357 -0
- package/dist/src/primitive-registry.js +2436 -0
- package/dist/src/primitive-sdk.js +4 -0
- package/dist/src/primitives/hyperframes-media.js +108 -0
- package/dist/src/react-page-shell.js +35 -0
- package/dist/src/ready-post-schedule-component.js +1540 -0
- package/dist/src/registry.js +296 -0
- package/dist/src/runtime.js +35 -0
- package/dist/src/services/api-call-history.js +249 -0
- package/dist/src/services/auth.js +152 -0
- package/dist/src/services/billing-pricing.js +39 -0
- package/dist/src/services/billing.js +228 -0
- package/dist/src/services/cast.js +127 -0
- package/dist/src/services/chat-threads.js +92 -0
- package/dist/src/services/composition-sanitize.js +124 -0
- package/dist/src/services/composition-watch.js +79 -0
- package/dist/src/services/fork-access.js +93 -0
- package/dist/src/services/fork-manifest.js +42 -0
- package/dist/src/services/ghostcut.js +179 -0
- package/dist/src/services/hyperframes.js +2307 -0
- package/dist/src/services/job-capacity.js +14 -0
- package/dist/src/services/job-logs.js +197 -0
- package/dist/src/services/jobs.js +136 -0
- package/dist/src/services/local-dynamo.js +0 -0
- package/dist/src/services/media-processing.js +766 -0
- package/dist/src/services/primitive-media-lambda.js +280 -0
- package/dist/src/services/providers.js +2926 -0
- package/dist/src/services/rate-limits.js +262 -0
- package/dist/src/services/serverless-auth.js +382 -0
- package/dist/src/services/serverless-jobs.js +1082 -0
- package/dist/src/services/serverless-provider-keys.js +409 -0
- package/dist/src/services/serverless-records.js +1385 -0
- package/dist/src/services/serverless-template-configs.js +75 -0
- package/dist/src/services/storage.js +383 -0
- package/dist/src/services/template-certification.js +413 -0
- package/dist/src/services/template-loader.js +99 -0
- package/dist/src/services/template-runtime-bundles.js +217 -0
- package/dist/src/services/template-sources.js +1017 -0
- package/dist/src/services/video-normalization.js +2 -0
- package/dist/src/services/webhooks.js +62 -0
- package/dist/src/template-editor-pages.js +2576 -0
- package/dist/src/template-editor-shell.js +2840 -0
- package/dist/src/template-sdk.js +4 -0
- package/dist/src/worker.js +17 -0
- package/package.json +6 -4
- package/public/assets/homepage-app.js +54 -0
- package/public/assets/homepage-client-app.js +80 -0
- package/public/assets/page-runtime-client-app.js +94 -0
- package/src/assets/SELLING_AWARENESS_STAGES.md +579 -0
- package/src/assets/SELLING_WITH_HOOKS.md +377 -0
- package/src/assets/SELLING_WITH_VSLS.md +606 -0
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo-vidfarm.png +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { concatAudioAssets, concatVideoAssets, estimateNormalizationCostUsd, extractAudioFromVideoAsset, extractVideoFrameAsset, muteVideoAsset, normalizeAudioAsset, normalizeVideoAsset, probeMediaAsset, renderSlidesVideoAsset, replaceVideoAudioAsset, trimAudioAsset, trimVideoAsset } from "./media-processing.js";
|
|
2
|
+
import { config } from "../config.js";
|
|
3
|
+
export class PrimitiveMediaLambdaService {
|
|
4
|
+
async execute(input) {
|
|
5
|
+
if (config.VIDFARM_PRIMITIVE_MEDIA_LAMBDA_URL && config.VIDFARM_PRIMITIVE_MEDIA_LAMBDA_SECRET) {
|
|
6
|
+
return this.executeRemote(input);
|
|
7
|
+
}
|
|
8
|
+
return this.executeLocal(input);
|
|
9
|
+
}
|
|
10
|
+
async executeRemote(input) {
|
|
11
|
+
const response = await fetch(config.VIDFARM_PRIMITIVE_MEDIA_LAMBDA_URL, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: {
|
|
14
|
+
"content-type": "application/json",
|
|
15
|
+
"x-vidfarm-media-secret": config.VIDFARM_PRIMITIVE_MEDIA_LAMBDA_SECRET
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
output_key: input.outputKey,
|
|
19
|
+
operation: input.operation,
|
|
20
|
+
payload: input.payload
|
|
21
|
+
})
|
|
22
|
+
});
|
|
23
|
+
const payload = await response.json().catch(() => null);
|
|
24
|
+
if (!response.ok || !payload?.storage_key || !payload.content_type) {
|
|
25
|
+
throw new Error(payload?.error || `Primitive media Lambda failed with HTTP ${response.status}.`);
|
|
26
|
+
}
|
|
27
|
+
const billingCostCenterSlug = input.operation === "video_normalize"
|
|
28
|
+
? "video_normalization"
|
|
29
|
+
: "primitive_media_lambda";
|
|
30
|
+
return {
|
|
31
|
+
storageKey: payload.storage_key,
|
|
32
|
+
publicUrl: payload.public_url ?? null,
|
|
33
|
+
contentType: payload.content_type,
|
|
34
|
+
bytes: Number(payload.bytes ?? 0),
|
|
35
|
+
metadata: payload.metadata ?? {},
|
|
36
|
+
billingCostUsd: Number(payload.billing?.cost_usd ?? 0),
|
|
37
|
+
billingCostCenterSlug,
|
|
38
|
+
billingMetadata: {
|
|
39
|
+
operation: input.operation,
|
|
40
|
+
mode: "lambda",
|
|
41
|
+
duration_ms: Number(payload.billing?.duration_ms ?? 0)
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async executeLocal(input) {
|
|
46
|
+
const startedAt = Date.now();
|
|
47
|
+
const result = await runLocalPrimitiveMediaOperation(input.operation, input.payload);
|
|
48
|
+
const stored = await input.storeLocal(input.localOutputKey, result.bytes, result.contentType);
|
|
49
|
+
return {
|
|
50
|
+
storageKey: stored.key,
|
|
51
|
+
publicUrl: stored.url,
|
|
52
|
+
contentType: result.contentType,
|
|
53
|
+
bytes: result.bytes.byteLength,
|
|
54
|
+
metadata: result.metadata,
|
|
55
|
+
billingCostUsd: result.billingCostUsd ?? estimateLocalLambdaEquivalentUsd(Date.now() - startedAt),
|
|
56
|
+
billingCostCenterSlug: input.operation === "video_normalize" ? "video_normalization" : "primitive_media_lambda",
|
|
57
|
+
billingMetadata: {
|
|
58
|
+
operation: input.operation,
|
|
59
|
+
mode: "local_fallback",
|
|
60
|
+
duration_ms: Date.now() - startedAt
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function runLocalPrimitiveMediaOperation(operation, payload) {
|
|
66
|
+
switch (operation) {
|
|
67
|
+
case "video_normalize": {
|
|
68
|
+
const result = await normalizeVideoAsset({
|
|
69
|
+
sourceUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceUrl),
|
|
70
|
+
maxWidth: readOptionalInt(payload.max_width ?? payload.maxWidth),
|
|
71
|
+
maxHeight: readOptionalInt(payload.max_height ?? payload.maxHeight),
|
|
72
|
+
crf: readOptionalInt(payload.crf),
|
|
73
|
+
preset: readOptionalPreset(payload.preset),
|
|
74
|
+
maxVideoBitrateKbps: readOptionalInt(payload.max_video_bitrate_kbps ?? payload.maxVideoBitrateKbps),
|
|
75
|
+
audioBitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
bytes: result.bytes,
|
|
79
|
+
contentType: result.contentType,
|
|
80
|
+
metadata: result.metadata,
|
|
81
|
+
billingCostUsd: estimateNormalizationCostUsd(result.metadata)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
case "video_render_slides": {
|
|
85
|
+
const result = await renderSlidesVideoAsset({
|
|
86
|
+
slides: readSlides(payload.slides),
|
|
87
|
+
width: Math.max(16, readOptionalInt(payload.width) ?? 1080),
|
|
88
|
+
height: Math.max(16, readOptionalInt(payload.height) ?? 1920),
|
|
89
|
+
backgroundColor: readString(payload.background_color ?? payload.backgroundColor) || "#000000",
|
|
90
|
+
defaultFit: readOptionalFit(payload.default_fit ?? payload.defaultFit) ?? "cover"
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
bytes: result.bytes,
|
|
94
|
+
contentType: result.contentType,
|
|
95
|
+
metadata: result.metadata
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
case "video_trim": {
|
|
99
|
+
const result = await trimVideoAsset({
|
|
100
|
+
sourceUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceUrl),
|
|
101
|
+
startMs: readOptionalInt(payload.start_ms ?? payload.startMs),
|
|
102
|
+
endMs: readOptionalInt(payload.end_ms ?? payload.endMs),
|
|
103
|
+
durationMs: readOptionalInt(payload.duration_ms ?? payload.durationMs),
|
|
104
|
+
maxWidth: readOptionalInt(payload.max_width ?? payload.maxWidth),
|
|
105
|
+
maxHeight: readOptionalInt(payload.max_height ?? payload.maxHeight),
|
|
106
|
+
crf: readOptionalInt(payload.crf),
|
|
107
|
+
preset: readOptionalPreset(payload.preset),
|
|
108
|
+
audioBitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
109
|
+
});
|
|
110
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
111
|
+
}
|
|
112
|
+
case "video_extract_audio": {
|
|
113
|
+
const result = await extractAudioFromVideoAsset({
|
|
114
|
+
sourceUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceUrl),
|
|
115
|
+
format: readOptionalAudioFormat(payload.output_format ?? payload.outputFormat),
|
|
116
|
+
bitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
117
|
+
});
|
|
118
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
119
|
+
}
|
|
120
|
+
case "audio_trim": {
|
|
121
|
+
const result = await trimAudioAsset({
|
|
122
|
+
sourceUrl: readRequiredUrl(payload.source_audio_url ?? payload.sourceUrl),
|
|
123
|
+
startMs: readOptionalInt(payload.start_ms ?? payload.startMs),
|
|
124
|
+
endMs: readOptionalInt(payload.end_ms ?? payload.endMs),
|
|
125
|
+
durationMs: readOptionalInt(payload.duration_ms ?? payload.durationMs),
|
|
126
|
+
format: readOptionalAudioFormat(payload.output_format ?? payload.outputFormat),
|
|
127
|
+
bitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
128
|
+
});
|
|
129
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
130
|
+
}
|
|
131
|
+
case "video_probe":
|
|
132
|
+
case "audio_probe": {
|
|
133
|
+
const result = await probeMediaAsset({
|
|
134
|
+
sourceUrl: readRequiredUrl(payload.source_video_url
|
|
135
|
+
?? payload.source_audio_url
|
|
136
|
+
?? payload.source_url
|
|
137
|
+
?? payload.sourceUrl)
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
bytes: Buffer.from(JSON.stringify(result.metadata, null, 2), "utf8"),
|
|
141
|
+
contentType: "application/json",
|
|
142
|
+
metadata: result.metadata
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
case "video_extract_frame": {
|
|
146
|
+
const result = await extractVideoFrameAsset({
|
|
147
|
+
sourceUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceUrl),
|
|
148
|
+
atMs: readOptionalInt(payload.at_ms ?? payload.atMs),
|
|
149
|
+
format: readOptionalImageFormat(payload.output_format ?? payload.outputFormat)
|
|
150
|
+
});
|
|
151
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
152
|
+
}
|
|
153
|
+
case "video_mute": {
|
|
154
|
+
const result = await muteVideoAsset({ sourceUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceUrl) });
|
|
155
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
156
|
+
}
|
|
157
|
+
case "video_replace_audio": {
|
|
158
|
+
const result = await replaceVideoAudioAsset({
|
|
159
|
+
sourceVideoUrl: readRequiredUrl(payload.source_video_url ?? payload.sourceVideoUrl),
|
|
160
|
+
audioUrl: readRequiredUrl(payload.source_audio_url ?? payload.audio_url ?? payload.audioUrl),
|
|
161
|
+
audioBitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
162
|
+
});
|
|
163
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
164
|
+
}
|
|
165
|
+
case "video_concat": {
|
|
166
|
+
const result = await concatVideoAssets({
|
|
167
|
+
sourceUrls: readRequiredUrlArray(payload.source_video_urls ?? payload.sourceUrls),
|
|
168
|
+
width: readOptionalInt(payload.width),
|
|
169
|
+
height: readOptionalInt(payload.height),
|
|
170
|
+
fit: readOptionalFit(payload.fit),
|
|
171
|
+
backgroundColor: readString(payload.background_color ?? payload.backgroundColor) || undefined
|
|
172
|
+
});
|
|
173
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
174
|
+
}
|
|
175
|
+
case "audio_concat": {
|
|
176
|
+
const result = await concatAudioAssets({
|
|
177
|
+
sourceUrls: readRequiredUrlArray(payload.source_audio_urls ?? payload.sourceUrls),
|
|
178
|
+
format: readOptionalAudioFormat(payload.output_format ?? payload.outputFormat),
|
|
179
|
+
bitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps)
|
|
180
|
+
});
|
|
181
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
182
|
+
}
|
|
183
|
+
case "audio_normalize": {
|
|
184
|
+
const result = await normalizeAudioAsset({
|
|
185
|
+
sourceUrl: readRequiredUrl(payload.source_audio_url ?? payload.sourceUrl),
|
|
186
|
+
format: readOptionalAudioFormat(payload.output_format ?? payload.outputFormat),
|
|
187
|
+
bitrateKbps: readOptionalInt(payload.audio_bitrate_kbps ?? payload.audioBitrateKbps),
|
|
188
|
+
sampleRateHz: readOptionalInt(payload.sample_rate_hz ?? payload.sampleRateHz)
|
|
189
|
+
});
|
|
190
|
+
return { bytes: result.bytes, contentType: result.contentType, metadata: result.metadata };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function estimateLocalLambdaEquivalentUsd(durationMs) {
|
|
195
|
+
const gbSeconds = 2 * (Math.max(1, durationMs) / 1000);
|
|
196
|
+
const computeCost = gbSeconds * 0.0000166667;
|
|
197
|
+
const requestCost = 0.20 / 1_000_000;
|
|
198
|
+
return Number((computeCost + requestCost).toFixed(6));
|
|
199
|
+
}
|
|
200
|
+
function readRequiredUrl(value) {
|
|
201
|
+
const text = readString(value);
|
|
202
|
+
if (!text) {
|
|
203
|
+
throw new Error("A source URL is required.");
|
|
204
|
+
}
|
|
205
|
+
new URL(text);
|
|
206
|
+
return text;
|
|
207
|
+
}
|
|
208
|
+
function readRequiredUrlArray(value) {
|
|
209
|
+
if (!Array.isArray(value) || !value.length) {
|
|
210
|
+
throw new Error("At least one source URL is required.");
|
|
211
|
+
}
|
|
212
|
+
return value.map((entry) => readRequiredUrl(entry));
|
|
213
|
+
}
|
|
214
|
+
function readOptionalInt(value) {
|
|
215
|
+
if (value == null || value === "")
|
|
216
|
+
return undefined;
|
|
217
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
218
|
+
if (!Number.isFinite(parsed)) {
|
|
219
|
+
throw new Error(`Invalid integer value "${String(value)}".`);
|
|
220
|
+
}
|
|
221
|
+
return parsed;
|
|
222
|
+
}
|
|
223
|
+
function readOptionalPreset(value) {
|
|
224
|
+
const text = readString(value);
|
|
225
|
+
if (!text)
|
|
226
|
+
return undefined;
|
|
227
|
+
if (text === "veryfast" || text === "faster" || text === "fast" || text === "medium") {
|
|
228
|
+
return text;
|
|
229
|
+
}
|
|
230
|
+
throw new Error(`Unsupported preset "${text}".`);
|
|
231
|
+
}
|
|
232
|
+
function readOptionalFit(value) {
|
|
233
|
+
const text = readString(value);
|
|
234
|
+
if (!text)
|
|
235
|
+
return undefined;
|
|
236
|
+
if (text === "cover" || text === "contain" || text === "fill") {
|
|
237
|
+
return text;
|
|
238
|
+
}
|
|
239
|
+
throw new Error(`Unsupported fit "${text}".`);
|
|
240
|
+
}
|
|
241
|
+
function readOptionalAudioFormat(value) {
|
|
242
|
+
const text = readString(value);
|
|
243
|
+
if (!text)
|
|
244
|
+
return undefined;
|
|
245
|
+
if (text === "mp3" || text === "wav" || text === "m4a") {
|
|
246
|
+
return text;
|
|
247
|
+
}
|
|
248
|
+
throw new Error(`Unsupported audio format "${text}".`);
|
|
249
|
+
}
|
|
250
|
+
function readOptionalImageFormat(value) {
|
|
251
|
+
const text = readString(value);
|
|
252
|
+
if (!text)
|
|
253
|
+
return undefined;
|
|
254
|
+
if (text === "png" || text === "jpeg") {
|
|
255
|
+
return text;
|
|
256
|
+
}
|
|
257
|
+
throw new Error(`Unsupported image format "${text}".`);
|
|
258
|
+
}
|
|
259
|
+
function readSlides(value) {
|
|
260
|
+
if (!Array.isArray(value) || !value.length) {
|
|
261
|
+
throw new Error("At least one slide is required.");
|
|
262
|
+
}
|
|
263
|
+
return value.map((slide, index) => {
|
|
264
|
+
if (!slide || typeof slide !== "object" || Array.isArray(slide)) {
|
|
265
|
+
throw new Error(`Invalid slide at index ${index}.`);
|
|
266
|
+
}
|
|
267
|
+
const entry = slide;
|
|
268
|
+
return {
|
|
269
|
+
mediaUrl: readRequiredUrl(entry.media_url ?? entry.mediaUrl ?? entry.image_url ?? entry.imageUrl ?? entry.video_url ?? entry.videoUrl),
|
|
270
|
+
mediaType: readString(entry.media_type ?? entry.mediaType) === "video" ? "video" : "image",
|
|
271
|
+
durationMs: Math.max(100, readOptionalInt(entry.duration_ms ?? entry.durationMs) ?? 1000),
|
|
272
|
+
fit: readOptionalFit(entry.fit) ?? "cover",
|
|
273
|
+
backgroundColor: readString(entry.background_color ?? entry.backgroundColor) || "#000000"
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function readString(value) {
|
|
278
|
+
return typeof value === "string" ? value.trim() : "";
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=primitive-media-lambda.js.map
|