@klikeai/cut-preview 1.0.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 +83 -0
- package/dist/index.d.ts +268 -0
- package/dist/index.js +1163 -0
- package/package.json +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
// ../src/SmartStudioComposition.tsx
|
|
2
|
+
import { AbsoluteFill as AbsoluteFill9 } from "remotion";
|
|
3
|
+
|
|
4
|
+
// ../src/themes.ts
|
|
5
|
+
var STYLE_PRESETS = [
|
|
6
|
+
"bold_impact",
|
|
7
|
+
"clean_saas",
|
|
8
|
+
"ugc_casual",
|
|
9
|
+
"premium_dark"
|
|
10
|
+
];
|
|
11
|
+
var DEFAULT_CAPTION_STYLE = {
|
|
12
|
+
fontFamily: "Montserrat",
|
|
13
|
+
fontSize: 64,
|
|
14
|
+
primaryColor: "#FFFFFF",
|
|
15
|
+
highlightColor: "#FFFF00",
|
|
16
|
+
outlineColor: "#000000",
|
|
17
|
+
outlineWidth: 3,
|
|
18
|
+
bold: true,
|
|
19
|
+
italic: false,
|
|
20
|
+
uppercase: false,
|
|
21
|
+
activeScale: 1.15,
|
|
22
|
+
animateIn: true,
|
|
23
|
+
template: "classic",
|
|
24
|
+
position: "bottom"
|
|
25
|
+
};
|
|
26
|
+
var DEFAULT_CAPTION_DISPLAY_MODE = "highlight";
|
|
27
|
+
var DEFAULT_CAPTION_COMBINE_MS = 1200;
|
|
28
|
+
var DEFAULT_OVERLAY_STYLE = {
|
|
29
|
+
fontFamily: "Poppins",
|
|
30
|
+
fontSize: 72,
|
|
31
|
+
color: "#FFFFFF",
|
|
32
|
+
outlineColor: "#000000",
|
|
33
|
+
outlineWidth: 3,
|
|
34
|
+
enterAnimation: "spring-pop",
|
|
35
|
+
bold: true
|
|
36
|
+
};
|
|
37
|
+
var DEFAULT_ENDCARD_STYLE = {
|
|
38
|
+
background: "#101010",
|
|
39
|
+
color: "#FFFFFF",
|
|
40
|
+
fontFamily: "Poppins",
|
|
41
|
+
fontSize: 88,
|
|
42
|
+
animateIn: true
|
|
43
|
+
};
|
|
44
|
+
var DEFAULT_PROGRESS_BAR = {
|
|
45
|
+
enabled: false,
|
|
46
|
+
color: "#FFFFFF",
|
|
47
|
+
heightPx: 10
|
|
48
|
+
};
|
|
49
|
+
var THEMES = {
|
|
50
|
+
// Promoção/varejo/urgência: tipografia pesada, caixa alta, box no token
|
|
51
|
+
// ativo, paleta amarelo-vibrante, progress bar de urgência.
|
|
52
|
+
bold_impact: {
|
|
53
|
+
captions: {
|
|
54
|
+
fontFamily: "Anton",
|
|
55
|
+
uppercase: true,
|
|
56
|
+
template: "box",
|
|
57
|
+
highlightColor: "#FFE600",
|
|
58
|
+
primaryColor: "#FFFFFF",
|
|
59
|
+
outlineWidth: 4,
|
|
60
|
+
activeScale: 1.1
|
|
61
|
+
},
|
|
62
|
+
overlays: {
|
|
63
|
+
fontFamily: "Anton",
|
|
64
|
+
enterAnimation: "zoom",
|
|
65
|
+
color: "#FFE600",
|
|
66
|
+
outlineWidth: 4
|
|
67
|
+
},
|
|
68
|
+
endcard: {
|
|
69
|
+
gradient: { from: "#1B1B1B", to: "#4A0E0E", angleDeg: 160 },
|
|
70
|
+
color: "#FFE600",
|
|
71
|
+
fontFamily: "Anton"
|
|
72
|
+
},
|
|
73
|
+
progressBar: { enabled: true, color: "#FFE600" },
|
|
74
|
+
cutTransition: { type: "slide", durationMs: 250 }
|
|
75
|
+
},
|
|
76
|
+
// Produto digital/B2B: limpo, pill translúcido, azul confiável, fades.
|
|
77
|
+
clean_saas: {
|
|
78
|
+
captions: {
|
|
79
|
+
fontFamily: "Inter",
|
|
80
|
+
template: "pill",
|
|
81
|
+
highlightColor: "#4D9FFF",
|
|
82
|
+
primaryColor: "#FFFFFF",
|
|
83
|
+
outlineWidth: 0,
|
|
84
|
+
activeScale: 1.06
|
|
85
|
+
},
|
|
86
|
+
overlays: {
|
|
87
|
+
fontFamily: "Inter",
|
|
88
|
+
enterAnimation: "slide-up",
|
|
89
|
+
color: "#FFFFFF",
|
|
90
|
+
outlineWidth: 2
|
|
91
|
+
},
|
|
92
|
+
endcard: {
|
|
93
|
+
gradient: { from: "#0F2027", to: "#2C5364", angleDeg: 150 },
|
|
94
|
+
color: "#FFFFFF",
|
|
95
|
+
fontFamily: "Inter"
|
|
96
|
+
},
|
|
97
|
+
progressBar: { enabled: true, color: "#4D9FFF", heightPx: 8 },
|
|
98
|
+
cutTransition: { type: "fade", durationMs: 300 }
|
|
99
|
+
},
|
|
100
|
+
// Creator falando pra câmera: informal, pop forte, cores quentes.
|
|
101
|
+
ugc_casual: {
|
|
102
|
+
captions: {
|
|
103
|
+
fontFamily: "Poppins",
|
|
104
|
+
template: "classic",
|
|
105
|
+
highlightColor: "#7CFF6B",
|
|
106
|
+
activeScale: 1.25
|
|
107
|
+
},
|
|
108
|
+
overlays: {
|
|
109
|
+
fontFamily: "Poppins",
|
|
110
|
+
enterAnimation: "spring-pop"
|
|
111
|
+
},
|
|
112
|
+
endcard: {
|
|
113
|
+
background: "#141414",
|
|
114
|
+
color: "#7CFF6B",
|
|
115
|
+
fontFamily: "Poppins"
|
|
116
|
+
},
|
|
117
|
+
cutTransition: { type: "fade", durationMs: 200 }
|
|
118
|
+
},
|
|
119
|
+
// Marca premium: glow dourado sutil, tipografia condensada, fades lentos.
|
|
120
|
+
premium_dark: {
|
|
121
|
+
captions: {
|
|
122
|
+
fontFamily: "Oswald",
|
|
123
|
+
template: "glow",
|
|
124
|
+
primaryColor: "#F4EBDC",
|
|
125
|
+
highlightColor: "#E8C46B",
|
|
126
|
+
outlineWidth: 0,
|
|
127
|
+
activeScale: 1.08,
|
|
128
|
+
bold: false
|
|
129
|
+
},
|
|
130
|
+
overlays: {
|
|
131
|
+
fontFamily: "Oswald",
|
|
132
|
+
enterAnimation: "fade",
|
|
133
|
+
color: "#E8C46B",
|
|
134
|
+
outlineWidth: 1,
|
|
135
|
+
bold: false
|
|
136
|
+
},
|
|
137
|
+
endcard: {
|
|
138
|
+
gradient: { from: "#0B0B0D", to: "#2A2118", angleDeg: 170 },
|
|
139
|
+
color: "#E8C46B",
|
|
140
|
+
fontFamily: "Oswald"
|
|
141
|
+
},
|
|
142
|
+
cutTransition: { type: "fade", durationMs: 500 }
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var resolveTheme = (preset) => {
|
|
146
|
+
if (preset && STYLE_PRESETS.includes(preset)) {
|
|
147
|
+
return THEMES[preset];
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
var merge = (...layers) => {
|
|
152
|
+
const out = {};
|
|
153
|
+
for (const layer of layers) {
|
|
154
|
+
if (!layer) continue;
|
|
155
|
+
for (const [k, v] of Object.entries(layer)) {
|
|
156
|
+
if (v !== void 0) out[k] = v;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
};
|
|
161
|
+
var resolveCaptionStyle = (theme, explicit) => merge(
|
|
162
|
+
DEFAULT_CAPTION_STYLE,
|
|
163
|
+
theme?.captions,
|
|
164
|
+
explicit
|
|
165
|
+
);
|
|
166
|
+
var resolveCaptionDisplayMode = (theme, explicit) => explicit ?? theme?.captions.displayMode ?? DEFAULT_CAPTION_DISPLAY_MODE;
|
|
167
|
+
var resolveCaptionCombineMs = (theme, explicit) => explicit ?? theme?.captions.combineTokensWithinMs ?? DEFAULT_CAPTION_COMBINE_MS;
|
|
168
|
+
var resolveOverlayItem = (theme, item) => merge(
|
|
169
|
+
DEFAULT_OVERLAY_STYLE,
|
|
170
|
+
theme?.overlays,
|
|
171
|
+
item
|
|
172
|
+
);
|
|
173
|
+
var resolveEndcard = (theme, endcard) => merge(
|
|
174
|
+
DEFAULT_ENDCARD_STYLE,
|
|
175
|
+
theme?.endcard,
|
|
176
|
+
endcard
|
|
177
|
+
);
|
|
178
|
+
var resolveProgressBar = (theme, explicit) => merge(DEFAULT_PROGRESS_BAR, theme?.progressBar, explicit);
|
|
179
|
+
var DEFAULT_CUT_TRANSITION_MS = 300;
|
|
180
|
+
var resolveCutTransition = (theme, explicitType, explicitMs) => {
|
|
181
|
+
const type = explicitType ?? theme?.cutTransition?.type ?? "fade";
|
|
182
|
+
const durationMs = explicitMs ?? theme?.cutTransition?.durationMs ?? DEFAULT_CUT_TRANSITION_MS;
|
|
183
|
+
return { type, durationMs };
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// ../src/layers/VideoBase.tsx
|
|
187
|
+
import { AbsoluteFill, OffthreadVideo } from "remotion";
|
|
188
|
+
import { jsx } from "react/jsx-runtime";
|
|
189
|
+
var VideoBase = ({ source, fit = "cover" }) => {
|
|
190
|
+
return /* @__PURE__ */ jsx(AbsoluteFill, { children: /* @__PURE__ */ jsx(
|
|
191
|
+
OffthreadVideo,
|
|
192
|
+
{
|
|
193
|
+
src: source.videoPath,
|
|
194
|
+
style: { width: "100%", height: "100%", objectFit: fit }
|
|
195
|
+
}
|
|
196
|
+
) });
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// ../src/layers/ReframeContainer.tsx
|
|
200
|
+
import { AbsoluteFill as AbsoluteFill2, OffthreadVideo as OffthreadVideo2 } from "remotion";
|
|
201
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
202
|
+
var ReframeContainer = ({ reframe, source, children }) => {
|
|
203
|
+
if (!reframe.enabled) {
|
|
204
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
205
|
+
}
|
|
206
|
+
return /* @__PURE__ */ jsxs(AbsoluteFill2, { children: [
|
|
207
|
+
/* @__PURE__ */ jsx2(AbsoluteFill2, { children: /* @__PURE__ */ jsx2(
|
|
208
|
+
OffthreadVideo2,
|
|
209
|
+
{
|
|
210
|
+
src: source.videoPath,
|
|
211
|
+
muted: true,
|
|
212
|
+
style: {
|
|
213
|
+
width: "100%",
|
|
214
|
+
height: "100%",
|
|
215
|
+
objectFit: "cover",
|
|
216
|
+
filter: "blur(30px)",
|
|
217
|
+
transform: "scale(1.1)"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
) }),
|
|
221
|
+
/* @__PURE__ */ jsx2(
|
|
222
|
+
AbsoluteFill2,
|
|
223
|
+
{
|
|
224
|
+
style: { alignItems: "center", justifyContent: "center" },
|
|
225
|
+
children
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] });
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// ../src/layers/CutsSequencer.tsx
|
|
232
|
+
import {
|
|
233
|
+
AbsoluteFill as AbsoluteFill3,
|
|
234
|
+
interpolate as interpolate2,
|
|
235
|
+
OffthreadVideo as OffthreadVideo3,
|
|
236
|
+
useVideoConfig
|
|
237
|
+
} from "remotion";
|
|
238
|
+
import {
|
|
239
|
+
linearTiming,
|
|
240
|
+
TransitionSeries
|
|
241
|
+
} from "@remotion/transitions";
|
|
242
|
+
import { fade } from "@remotion/transitions/fade";
|
|
243
|
+
import { slide } from "@remotion/transitions/slide";
|
|
244
|
+
import { wipe } from "@remotion/transitions/wipe";
|
|
245
|
+
|
|
246
|
+
// ../src/helpers.ts
|
|
247
|
+
import { interpolate, spring } from "remotion";
|
|
248
|
+
var msToFrames = (ms, fps) => Math.round(ms / 1e3 * fps);
|
|
249
|
+
var REFERENCE_MIN_SIDE = 1080;
|
|
250
|
+
var uiScale = (width, height) => Math.min(width, height) / REFERENCE_MIN_SIDE;
|
|
251
|
+
var springScale = (localFrame, fps, peak = 1.15, durationInFrames = 8) => {
|
|
252
|
+
const s = spring({
|
|
253
|
+
fps,
|
|
254
|
+
frame: localFrame,
|
|
255
|
+
config: { damping: 12, stiffness: 200, mass: 0.6 },
|
|
256
|
+
durationInFrames
|
|
257
|
+
});
|
|
258
|
+
return interpolate(s, [0, 1], [1, peak]);
|
|
259
|
+
};
|
|
260
|
+
var justifyForPosition = (position) => {
|
|
261
|
+
if (position === "top") return "flex-start";
|
|
262
|
+
if (position === "bottom") return "flex-end";
|
|
263
|
+
return "center";
|
|
264
|
+
};
|
|
265
|
+
var GRADE_TARGETS = {
|
|
266
|
+
warm: { sepia: 0.35, saturate: 1.25, brightness: 1.04 },
|
|
267
|
+
cold: { saturate: 1.1, hueDeg: -12, brightness: 1.02 },
|
|
268
|
+
vintage: { sepia: 0.5, contrast: 0.92, saturate: 0.85 },
|
|
269
|
+
bw: { grayscale: 1, contrast: 1.08 },
|
|
270
|
+
high_contrast: { contrast: 1.35, saturate: 1.1 },
|
|
271
|
+
vivid: { saturate: 1.45, contrast: 1.1 }
|
|
272
|
+
};
|
|
273
|
+
var gradeFilter = (preset, intensity) => {
|
|
274
|
+
const t = preset ? GRADE_TARGETS[preset] : void 0;
|
|
275
|
+
if (!t) return "none";
|
|
276
|
+
const k = Math.max(0, Math.min(intensity, 1));
|
|
277
|
+
const lerp = (from, to) => from + (to - from) * k;
|
|
278
|
+
const parts = [];
|
|
279
|
+
if (t.grayscale !== void 0) parts.push(`grayscale(${lerp(0, t.grayscale)})`);
|
|
280
|
+
if (t.sepia !== void 0) parts.push(`sepia(${lerp(0, t.sepia)})`);
|
|
281
|
+
if (t.saturate !== void 0) parts.push(`saturate(${lerp(1, t.saturate)})`);
|
|
282
|
+
if (t.contrast !== void 0) parts.push(`contrast(${lerp(1, t.contrast)})`);
|
|
283
|
+
if (t.brightness !== void 0) parts.push(`brightness(${lerp(1, t.brightness)})`);
|
|
284
|
+
if (t.hueDeg !== void 0) parts.push(`hue-rotate(${lerp(0, t.hueDeg)}deg)`);
|
|
285
|
+
return parts.length ? parts.join(" ") : "none";
|
|
286
|
+
};
|
|
287
|
+
var textOutline = (color, px = 3) => {
|
|
288
|
+
const o = [];
|
|
289
|
+
for (let dx = -px; dx <= px; dx++) {
|
|
290
|
+
for (let dy = -px; dy <= px; dy++) {
|
|
291
|
+
if (dx === 0 && dy === 0) continue;
|
|
292
|
+
o.push(`${dx}px ${dy}px 0 ${color}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return o.join(", ");
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// ../src/layers/CutsSequencer.tsx
|
|
299
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
300
|
+
var presentationFor = (type) => {
|
|
301
|
+
if (type === "wipe") return wipe();
|
|
302
|
+
if (type === "slide") return slide();
|
|
303
|
+
return fade();
|
|
304
|
+
};
|
|
305
|
+
var SegmentVideo = ({
|
|
306
|
+
source,
|
|
307
|
+
startFromFrames,
|
|
308
|
+
fadeInFrames,
|
|
309
|
+
fadeOutFrames,
|
|
310
|
+
durationInFrames,
|
|
311
|
+
reframe
|
|
312
|
+
}) => {
|
|
313
|
+
const volume = (f) => {
|
|
314
|
+
let v = 1;
|
|
315
|
+
if (fadeInFrames > 0) {
|
|
316
|
+
v *= interpolate2(f, [0, fadeInFrames], [0, 1], {
|
|
317
|
+
extrapolateLeft: "clamp",
|
|
318
|
+
extrapolateRight: "clamp"
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (fadeOutFrames > 0) {
|
|
322
|
+
v *= interpolate2(
|
|
323
|
+
f,
|
|
324
|
+
[durationInFrames - fadeOutFrames, durationInFrames],
|
|
325
|
+
[1, 0],
|
|
326
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
return v;
|
|
330
|
+
};
|
|
331
|
+
const fg = /* @__PURE__ */ jsx3(
|
|
332
|
+
OffthreadVideo3,
|
|
333
|
+
{
|
|
334
|
+
src: source.videoPath,
|
|
335
|
+
startFrom: startFromFrames,
|
|
336
|
+
volume,
|
|
337
|
+
style: {
|
|
338
|
+
width: "100%",
|
|
339
|
+
height: "100%",
|
|
340
|
+
objectFit: reframe.enabled ? "contain" : "cover"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
if (!reframe.enabled) {
|
|
345
|
+
return /* @__PURE__ */ jsx3(AbsoluteFill3, { children: fg });
|
|
346
|
+
}
|
|
347
|
+
return /* @__PURE__ */ jsxs2(AbsoluteFill3, { children: [
|
|
348
|
+
/* @__PURE__ */ jsx3(AbsoluteFill3, { children: /* @__PURE__ */ jsx3(
|
|
349
|
+
OffthreadVideo3,
|
|
350
|
+
{
|
|
351
|
+
src: source.videoPath,
|
|
352
|
+
startFrom: startFromFrames,
|
|
353
|
+
muted: true,
|
|
354
|
+
style: {
|
|
355
|
+
width: "100%",
|
|
356
|
+
height: "100%",
|
|
357
|
+
objectFit: "cover",
|
|
358
|
+
filter: "blur(30px)",
|
|
359
|
+
transform: "scale(1.1)"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
) }),
|
|
363
|
+
/* @__PURE__ */ jsx3(AbsoluteFill3, { style: { alignItems: "center", justifyContent: "center" }, children: fg })
|
|
364
|
+
] });
|
|
365
|
+
};
|
|
366
|
+
var CutsSequencer = ({ source, cuts, reframe, transition }) => {
|
|
367
|
+
const { fps } = useVideoConfig();
|
|
368
|
+
const segments = cuts.segmentsKeepMs.filter(([s, e]) => e > s);
|
|
369
|
+
if (segments.length === 0) return null;
|
|
370
|
+
const minSegMs = Math.min(...segments.map(([s, e]) => e - s));
|
|
371
|
+
const useTransition = transition.type !== "none" && transition.durationMs > 0 && segments.length > 1;
|
|
372
|
+
const transF = useTransition ? Math.max(
|
|
373
|
+
1,
|
|
374
|
+
msToFrames(Math.min(transition.durationMs, minSegMs / 2), fps)
|
|
375
|
+
) : 0;
|
|
376
|
+
const children = [];
|
|
377
|
+
segments.forEach(([startMs, endMs], i) => {
|
|
378
|
+
const durationInFrames = Math.max(1, msToFrames(endMs - startMs, fps));
|
|
379
|
+
const fadeInF = i > 0 ? transF : 0;
|
|
380
|
+
const fadeOutF = i < segments.length - 1 ? transF : 0;
|
|
381
|
+
children.push(
|
|
382
|
+
/* @__PURE__ */ jsx3(
|
|
383
|
+
TransitionSeries.Sequence,
|
|
384
|
+
{
|
|
385
|
+
durationInFrames,
|
|
386
|
+
children: /* @__PURE__ */ jsx3(
|
|
387
|
+
SegmentVideo,
|
|
388
|
+
{
|
|
389
|
+
source,
|
|
390
|
+
startFromFrames: msToFrames(startMs, fps),
|
|
391
|
+
fadeInFrames: fadeInF,
|
|
392
|
+
fadeOutFrames: fadeOutF,
|
|
393
|
+
durationInFrames,
|
|
394
|
+
reframe
|
|
395
|
+
}
|
|
396
|
+
)
|
|
397
|
+
},
|
|
398
|
+
`seg-${i}`
|
|
399
|
+
)
|
|
400
|
+
);
|
|
401
|
+
if (useTransition && i < segments.length - 1) {
|
|
402
|
+
children.push(
|
|
403
|
+
/* @__PURE__ */ jsx3(
|
|
404
|
+
TransitionSeries.Transition,
|
|
405
|
+
{
|
|
406
|
+
presentation: presentationFor(transition.type),
|
|
407
|
+
timing: linearTiming({ durationInFrames: transF })
|
|
408
|
+
},
|
|
409
|
+
`trans-${i}`
|
|
410
|
+
)
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
return /* @__PURE__ */ jsx3(TransitionSeries, { children });
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// ../src/layers/ZoomLayer.tsx
|
|
418
|
+
import { AbsoluteFill as AbsoluteFill4, spring as spring2, useCurrentFrame } from "remotion";
|
|
419
|
+
import { Fragment as Fragment2, jsx as jsx4 } from "react/jsx-runtime";
|
|
420
|
+
var momentScaleBoost = (frame, fps, moment) => {
|
|
421
|
+
const startF = msToFrames(moment.startMs, fps);
|
|
422
|
+
const endF = msToFrames(moment.endMs, fps);
|
|
423
|
+
const ramp = Math.max(1, Math.round(Math.min(fps * 0.3, (endF - startF) / 2)));
|
|
424
|
+
const enter = spring2({
|
|
425
|
+
fps,
|
|
426
|
+
frame: frame - startF,
|
|
427
|
+
config: { damping: 14, stiffness: 160, mass: 0.8 },
|
|
428
|
+
durationInFrames: ramp
|
|
429
|
+
});
|
|
430
|
+
const exit = spring2({
|
|
431
|
+
fps,
|
|
432
|
+
frame: frame - (endF - ramp),
|
|
433
|
+
config: { damping: 200 },
|
|
434
|
+
durationInFrames: ramp
|
|
435
|
+
});
|
|
436
|
+
return (moment.factor - 1) * Math.max(0, enter - exit);
|
|
437
|
+
};
|
|
438
|
+
var ZoomLayer = ({ zoom, fps, children }) => {
|
|
439
|
+
const frame = useCurrentFrame();
|
|
440
|
+
if (!zoom.enabled) {
|
|
441
|
+
return /* @__PURE__ */ jsx4(Fragment2, { children });
|
|
442
|
+
}
|
|
443
|
+
const moments = zoom.moments?.length ? zoom.moments : [{ startMs: zoom.startMs, endMs: zoom.endMs, factor: zoom.factor }];
|
|
444
|
+
let scale = 1;
|
|
445
|
+
if (zoom.animated) {
|
|
446
|
+
for (const moment of moments) {
|
|
447
|
+
scale += momentScaleBoost(frame, fps, moment);
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
for (const moment of moments) {
|
|
451
|
+
const startF = msToFrames(moment.startMs, fps);
|
|
452
|
+
const endF = msToFrames(moment.endMs, fps);
|
|
453
|
+
if (frame >= startF && frame <= endF) {
|
|
454
|
+
scale = moment.factor;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return /* @__PURE__ */ jsx4(AbsoluteFill4, { style: { transform: `scale(${scale})` }, children });
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// ../src/layers/CaptionsLayer.tsx
|
|
463
|
+
import {
|
|
464
|
+
AbsoluteFill as AbsoluteFill5,
|
|
465
|
+
Easing,
|
|
466
|
+
interpolate as interpolate3,
|
|
467
|
+
Sequence,
|
|
468
|
+
useCurrentFrame as useCurrentFrame2,
|
|
469
|
+
useVideoConfig as useVideoConfig2
|
|
470
|
+
} from "remotion";
|
|
471
|
+
import {
|
|
472
|
+
createTikTokStyleCaptions
|
|
473
|
+
} from "@remotion/captions";
|
|
474
|
+
|
|
475
|
+
// ../src/fonts.ts
|
|
476
|
+
import { loadFont as loadAnton } from "@remotion/google-fonts/Anton";
|
|
477
|
+
import { loadFont as loadBebasNeue } from "@remotion/google-fonts/BebasNeue";
|
|
478
|
+
import { loadFont as loadInter } from "@remotion/google-fonts/Inter";
|
|
479
|
+
import { loadFont as loadMontserrat } from "@remotion/google-fonts/Montserrat";
|
|
480
|
+
import { loadFont as loadOswald } from "@remotion/google-fonts/Oswald";
|
|
481
|
+
import { loadFont as loadPoppins } from "@remotion/google-fonts/Poppins";
|
|
482
|
+
import { loadFont as loadRoboto } from "@remotion/google-fonts/Roboto";
|
|
483
|
+
var FONTS = {
|
|
484
|
+
Montserrat: loadMontserrat().fontFamily,
|
|
485
|
+
Inter: loadInter().fontFamily,
|
|
486
|
+
Poppins: loadPoppins().fontFamily,
|
|
487
|
+
Oswald: loadOswald().fontFamily,
|
|
488
|
+
"Bebas Neue": loadBebasNeue().fontFamily,
|
|
489
|
+
BebasNeue: "",
|
|
490
|
+
Anton: loadAnton().fontFamily,
|
|
491
|
+
Roboto: loadRoboto().fontFamily
|
|
492
|
+
};
|
|
493
|
+
FONTS.BebasNeue = FONTS["Bebas Neue"];
|
|
494
|
+
var DEFAULT_CAPTION_FONT = "Montserrat";
|
|
495
|
+
var DEFAULT_OVERLAY_FONT = "Poppins";
|
|
496
|
+
function resolveFontFamily(name) {
|
|
497
|
+
if (name && FONTS[name]) {
|
|
498
|
+
return `${FONTS[name]}, Arial, sans-serif`;
|
|
499
|
+
}
|
|
500
|
+
if (name && name.trim()) {
|
|
501
|
+
return `${name}, Arial, sans-serif`;
|
|
502
|
+
}
|
|
503
|
+
return `${FONTS[DEFAULT_CAPTION_FONT]}, Arial, sans-serif`;
|
|
504
|
+
}
|
|
505
|
+
var AVAILABLE_FONTS = Object.keys(FONTS).filter((k) => FONTS[k]);
|
|
506
|
+
|
|
507
|
+
// ../src/layers/CaptionsLayer.tsx
|
|
508
|
+
import { Fragment as Fragment3, jsx as jsx5 } from "react/jsx-runtime";
|
|
509
|
+
var glowShadow = (color, scale = 1) => `0 0 ${8 * scale}px ${color}, 0 0 ${18 * scale}px ${color}, 0 0 ${36 * scale}px ${color}66`;
|
|
510
|
+
var Page = ({ page, style, displayMode, safeZones }) => {
|
|
511
|
+
const frame = useCurrentFrame2();
|
|
512
|
+
const { fps, width, height } = useVideoConfig2();
|
|
513
|
+
const nowMs = frame / fps * 1e3 + page.startMs;
|
|
514
|
+
const template = style.template;
|
|
515
|
+
const fontWeight = style.bold ? 800 : 500;
|
|
516
|
+
const ui = uiScale(width, height);
|
|
517
|
+
const enter = style.animateIn ? interpolate3(frame, [0, 5], [0, 1], {
|
|
518
|
+
extrapolateLeft: "clamp",
|
|
519
|
+
extrapolateRight: "clamp",
|
|
520
|
+
easing: Easing.out(Easing.cubic)
|
|
521
|
+
}) : 1;
|
|
522
|
+
const textShadow = template === "glow" ? glowShadow(style.highlightColor, ui) : template === "pill" || style.outlineWidth <= 0 ? "none" : textOutline(
|
|
523
|
+
style.outlineColor,
|
|
524
|
+
Math.max(1, Math.round(style.outlineWidth * ui))
|
|
525
|
+
);
|
|
526
|
+
const baseStyle = {
|
|
527
|
+
fontFamily: resolveFontFamily(style.fontFamily),
|
|
528
|
+
fontWeight,
|
|
529
|
+
fontStyle: style.italic ? "italic" : "normal",
|
|
530
|
+
fontSize: style.fontSize * ui,
|
|
531
|
+
color: style.primaryColor,
|
|
532
|
+
textShadow,
|
|
533
|
+
textTransform: style.uppercase ? "uppercase" : "none"
|
|
534
|
+
};
|
|
535
|
+
const renderToken = (tokenText, fromMs, toMs, key) => {
|
|
536
|
+
const active = nowMs >= fromMs && nowMs < toMs;
|
|
537
|
+
if (displayMode === "word_by_word" && !active) return null;
|
|
538
|
+
const canHighlight = displayMode === "highlight" || displayMode === "karaoke" || displayMode === "word_by_word";
|
|
539
|
+
const highlight = canHighlight && active;
|
|
540
|
+
let scale = 1;
|
|
541
|
+
if (highlight && style.activeScale > 1) {
|
|
542
|
+
const tokenStartFrame = msToFrames(fromMs - page.startMs, fps);
|
|
543
|
+
scale = springScale(frame - tokenStartFrame, fps, style.activeScale);
|
|
544
|
+
}
|
|
545
|
+
const boxed = template === "box" && highlight;
|
|
546
|
+
return /* @__PURE__ */ jsx5(
|
|
547
|
+
"span",
|
|
548
|
+
{
|
|
549
|
+
style: {
|
|
550
|
+
color: boxed ? "#111111" : highlight ? style.highlightColor : style.primaryColor,
|
|
551
|
+
display: "inline-block",
|
|
552
|
+
transform: `scale(${scale})`,
|
|
553
|
+
whiteSpace: "pre",
|
|
554
|
+
...boxed ? {
|
|
555
|
+
backgroundColor: style.highlightColor,
|
|
556
|
+
borderRadius: "0.18em",
|
|
557
|
+
padding: "0 0.12em",
|
|
558
|
+
textShadow: "none"
|
|
559
|
+
} : {},
|
|
560
|
+
...template === "glow" && highlight ? { textShadow: glowShadow(style.highlightColor, ui) } : {}
|
|
561
|
+
},
|
|
562
|
+
children: tokenText
|
|
563
|
+
},
|
|
564
|
+
key
|
|
565
|
+
);
|
|
566
|
+
};
|
|
567
|
+
const position = style.position;
|
|
568
|
+
return /* @__PURE__ */ jsx5(
|
|
569
|
+
AbsoluteFill5,
|
|
570
|
+
{
|
|
571
|
+
style: {
|
|
572
|
+
justifyContent: position === "top" ? "flex-start" : position === "center" ? "center" : "flex-end",
|
|
573
|
+
alignItems: "center",
|
|
574
|
+
paddingTop: height * safeZones.marginTopPercent,
|
|
575
|
+
paddingBottom: height * safeZones.marginBottomPercent,
|
|
576
|
+
paddingLeft: width * safeZones.marginSidePercent,
|
|
577
|
+
paddingRight: width * safeZones.marginSidePercent
|
|
578
|
+
},
|
|
579
|
+
children: /* @__PURE__ */ jsx5(
|
|
580
|
+
"div",
|
|
581
|
+
{
|
|
582
|
+
style: {
|
|
583
|
+
...baseStyle,
|
|
584
|
+
textAlign: "center",
|
|
585
|
+
lineHeight: 1.25,
|
|
586
|
+
opacity: enter,
|
|
587
|
+
transform: `scale(${interpolate3(enter, [0, 1], [0.92, 1])})`,
|
|
588
|
+
// Template pill: bloco da página sobre fundo arredondado translúcido.
|
|
589
|
+
...template === "pill" ? {
|
|
590
|
+
backgroundColor: "rgba(12, 12, 14, 0.72)",
|
|
591
|
+
borderRadius: 22 * ui,
|
|
592
|
+
padding: "0.35em 0.6em"
|
|
593
|
+
} : {}
|
|
594
|
+
},
|
|
595
|
+
children: page.tokens.map((t, i) => renderToken(t.text, t.fromMs, t.toMs, i))
|
|
596
|
+
}
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
};
|
|
601
|
+
var CaptionsLayer = ({ captions, style, displayMode, combineTokensWithinMs, safeZones, fps }) => {
|
|
602
|
+
if (!captions.enabled || !captions.tokens || captions.tokens.length === 0) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
const remotionCaptions = captions.tokens.map((t, i) => ({
|
|
606
|
+
text: i === 0 ? t.text : ` ${t.text}`,
|
|
607
|
+
startMs: t.startMs,
|
|
608
|
+
endMs: t.endMs,
|
|
609
|
+
timestampMs: (t.startMs + t.endMs) / 2,
|
|
610
|
+
confidence: t.confidence ?? null
|
|
611
|
+
}));
|
|
612
|
+
const sentenceChunks = [];
|
|
613
|
+
let currentChunk = [];
|
|
614
|
+
for (const caption of remotionCaptions) {
|
|
615
|
+
currentChunk.push(caption);
|
|
616
|
+
if (/[.!?…]["')\]]?$/.test(caption.text.trim())) {
|
|
617
|
+
sentenceChunks.push(currentChunk);
|
|
618
|
+
currentChunk = [];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (currentChunk.length > 0) sentenceChunks.push(currentChunk);
|
|
622
|
+
const pages = sentenceChunks.flatMap((chunk) => {
|
|
623
|
+
const normalized = chunk.map(
|
|
624
|
+
(c, i) => i === 0 ? { ...c, text: c.text.trimStart() } : c
|
|
625
|
+
);
|
|
626
|
+
return createTikTokStyleCaptions({
|
|
627
|
+
captions: normalized,
|
|
628
|
+
combineTokensWithinMilliseconds: combineTokensWithinMs
|
|
629
|
+
}).pages;
|
|
630
|
+
});
|
|
631
|
+
return /* @__PURE__ */ jsx5(Fragment3, { children: pages.map((page, i) => {
|
|
632
|
+
const from = msToFrames(page.startMs, fps);
|
|
633
|
+
const durationInFrames = Math.max(1, msToFrames(page.durationMs, fps));
|
|
634
|
+
return /* @__PURE__ */ jsx5(
|
|
635
|
+
Sequence,
|
|
636
|
+
{
|
|
637
|
+
from,
|
|
638
|
+
durationInFrames,
|
|
639
|
+
name: `caption-page-${i}`,
|
|
640
|
+
children: /* @__PURE__ */ jsx5(
|
|
641
|
+
Page,
|
|
642
|
+
{
|
|
643
|
+
page,
|
|
644
|
+
style,
|
|
645
|
+
displayMode,
|
|
646
|
+
safeZones
|
|
647
|
+
}
|
|
648
|
+
)
|
|
649
|
+
},
|
|
650
|
+
i
|
|
651
|
+
);
|
|
652
|
+
}) });
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// ../src/layers/OverlayLayer.tsx
|
|
656
|
+
import {
|
|
657
|
+
AbsoluteFill as AbsoluteFill6,
|
|
658
|
+
interpolate as interpolate4,
|
|
659
|
+
Sequence as Sequence2,
|
|
660
|
+
spring as spring3,
|
|
661
|
+
useCurrentFrame as useCurrentFrame3,
|
|
662
|
+
useVideoConfig as useVideoConfig3
|
|
663
|
+
} from "remotion";
|
|
664
|
+
import { fitText } from "@remotion/layout-utils";
|
|
665
|
+
import { Fragment as Fragment4, jsx as jsx6 } from "react/jsx-runtime";
|
|
666
|
+
var DEFAULT_FADE_MS = 300;
|
|
667
|
+
var DEFAULT_FONT_SIZE = 72;
|
|
668
|
+
var DEFAULT_COLOR = "#FFFFFF";
|
|
669
|
+
var DEFAULT_OUTLINE = "#000000";
|
|
670
|
+
var MIN_FONT_SIZE = 40;
|
|
671
|
+
var fitFontSize = (text, desired, withinWidth, fontFamily, fontWeight, uppercase, minSize = MIN_FONT_SIZE) => {
|
|
672
|
+
try {
|
|
673
|
+
const oneLine = fitText({
|
|
674
|
+
text,
|
|
675
|
+
withinWidth,
|
|
676
|
+
fontFamily,
|
|
677
|
+
fontWeight,
|
|
678
|
+
textTransform: uppercase ? "uppercase" : void 0
|
|
679
|
+
}).fontSize;
|
|
680
|
+
if (oneLine >= desired * 0.7) return Math.min(desired, oneLine);
|
|
681
|
+
const twoLines = fitText({
|
|
682
|
+
text,
|
|
683
|
+
withinWidth: withinWidth * 1.9,
|
|
684
|
+
fontFamily,
|
|
685
|
+
fontWeight,
|
|
686
|
+
textTransform: uppercase ? "uppercase" : void 0
|
|
687
|
+
}).fontSize;
|
|
688
|
+
return Math.max(minSize, Math.min(desired, twoLines));
|
|
689
|
+
} catch {
|
|
690
|
+
return desired;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
var CAPTION_BAND_PCT = 0.13;
|
|
694
|
+
var OverlayText = ({ item, durationInFrames, fps, safeZones, captionsPosition }) => {
|
|
695
|
+
const frame = useCurrentFrame3();
|
|
696
|
+
const { width, height } = useVideoConfig3();
|
|
697
|
+
const fadeInF = Math.max(1, msToFrames(item.fadeMs ?? DEFAULT_FADE_MS, fps));
|
|
698
|
+
const fadeOutF = Math.max(
|
|
699
|
+
1,
|
|
700
|
+
msToFrames(item.fadeOutMs ?? item.fadeMs ?? DEFAULT_FADE_MS, fps)
|
|
701
|
+
);
|
|
702
|
+
const enterAnim = item.enterAnimation ?? "spring-pop";
|
|
703
|
+
const enterSpring = spring3({
|
|
704
|
+
fps,
|
|
705
|
+
frame,
|
|
706
|
+
config: { damping: 14, stiffness: 200, mass: 0.7 },
|
|
707
|
+
durationInFrames: fadeInF
|
|
708
|
+
});
|
|
709
|
+
const enterLinear = interpolate4(frame, [0, fadeInF], [0, 1], {
|
|
710
|
+
extrapolateLeft: "clamp",
|
|
711
|
+
extrapolateRight: "clamp"
|
|
712
|
+
});
|
|
713
|
+
const opacity = interpolate4(
|
|
714
|
+
frame,
|
|
715
|
+
[0, fadeInF, Math.max(fadeInF, durationInFrames - fadeOutF), durationInFrames],
|
|
716
|
+
[0, 1, 1, 0],
|
|
717
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
718
|
+
);
|
|
719
|
+
let transform = "none";
|
|
720
|
+
if (enterAnim === "slide-up") {
|
|
721
|
+
transform = `translateY(${interpolate4(enterLinear, [0, 1], [40, 0])}px)`;
|
|
722
|
+
} else if (enterAnim === "slide-down") {
|
|
723
|
+
transform = `translateY(${interpolate4(enterLinear, [0, 1], [-40, 0])}px)`;
|
|
724
|
+
} else if (enterAnim === "zoom") {
|
|
725
|
+
transform = `scale(${interpolate4(enterLinear, [0, 1], [0.6, 1])})`;
|
|
726
|
+
} else if (enterAnim === "spring-pop") {
|
|
727
|
+
transform = `scale(${interpolate4(enterSpring, [0, 1], [0.7, 1])})`;
|
|
728
|
+
}
|
|
729
|
+
const fontFamily = resolveFontFamily(item.fontFamily ?? DEFAULT_OVERLAY_FONT);
|
|
730
|
+
const fontWeight = item.bold === false ? 500 : 700;
|
|
731
|
+
const ui = uiScale(width, height);
|
|
732
|
+
const outlineWidth = Math.max(
|
|
733
|
+
1,
|
|
734
|
+
Math.round((item.outlineWidth ?? 3) * ui)
|
|
735
|
+
);
|
|
736
|
+
const withinWidth = width * (1 - 2 * safeZones.marginSidePercent);
|
|
737
|
+
const fontSize = fitFontSize(
|
|
738
|
+
item.text,
|
|
739
|
+
(item.fontSize ?? DEFAULT_FONT_SIZE) * ui,
|
|
740
|
+
withinWidth,
|
|
741
|
+
fontFamily,
|
|
742
|
+
fontWeight,
|
|
743
|
+
false,
|
|
744
|
+
MIN_FONT_SIZE * ui
|
|
745
|
+
);
|
|
746
|
+
const overlayPos = item.position ?? "center";
|
|
747
|
+
const bottomClearance = safeZones.marginBottomPercent + (captionsPosition === "bottom" && overlayPos === "bottom" ? CAPTION_BAND_PCT : 0);
|
|
748
|
+
const topClearance = safeZones.marginTopPercent + (captionsPosition === "top" && overlayPos === "top" ? CAPTION_BAND_PCT : 0);
|
|
749
|
+
return /* @__PURE__ */ jsx6(
|
|
750
|
+
AbsoluteFill6,
|
|
751
|
+
{
|
|
752
|
+
style: {
|
|
753
|
+
justifyContent: justifyForPosition(item.position),
|
|
754
|
+
alignItems: "center",
|
|
755
|
+
paddingTop: height * topClearance,
|
|
756
|
+
paddingBottom: height * bottomClearance,
|
|
757
|
+
paddingLeft: width * safeZones.marginSidePercent,
|
|
758
|
+
paddingRight: width * safeZones.marginSidePercent
|
|
759
|
+
},
|
|
760
|
+
children: /* @__PURE__ */ jsx6(
|
|
761
|
+
"div",
|
|
762
|
+
{
|
|
763
|
+
style: {
|
|
764
|
+
opacity,
|
|
765
|
+
transform,
|
|
766
|
+
textAlign: "center",
|
|
767
|
+
fontFamily,
|
|
768
|
+
fontWeight,
|
|
769
|
+
fontStyle: item.italic ? "italic" : "normal",
|
|
770
|
+
fontSize,
|
|
771
|
+
color: item.color ?? DEFAULT_COLOR,
|
|
772
|
+
textShadow: outlineWidth > 0 ? textOutline(item.outlineColor ?? DEFAULT_OUTLINE, outlineWidth) : "none",
|
|
773
|
+
lineHeight: 1.15,
|
|
774
|
+
whiteSpace: "pre-wrap"
|
|
775
|
+
},
|
|
776
|
+
children: item.text
|
|
777
|
+
}
|
|
778
|
+
)
|
|
779
|
+
}
|
|
780
|
+
);
|
|
781
|
+
};
|
|
782
|
+
var OverlayLayer = ({ overlays, safeZones, fps, captionsPosition = null }) => {
|
|
783
|
+
if (!overlays || overlays.length === 0) return null;
|
|
784
|
+
return /* @__PURE__ */ jsx6(Fragment4, { children: overlays.map((item, i) => {
|
|
785
|
+
const from = msToFrames(item.startMs, fps);
|
|
786
|
+
const durationInFrames = Math.max(
|
|
787
|
+
1,
|
|
788
|
+
msToFrames(item.endMs, fps) - from
|
|
789
|
+
);
|
|
790
|
+
return /* @__PURE__ */ jsx6(
|
|
791
|
+
Sequence2,
|
|
792
|
+
{
|
|
793
|
+
from,
|
|
794
|
+
durationInFrames,
|
|
795
|
+
name: `overlay-${i}`,
|
|
796
|
+
children: /* @__PURE__ */ jsx6(
|
|
797
|
+
OverlayText,
|
|
798
|
+
{
|
|
799
|
+
item,
|
|
800
|
+
durationInFrames,
|
|
801
|
+
fps,
|
|
802
|
+
safeZones,
|
|
803
|
+
captionsPosition
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
},
|
|
807
|
+
i
|
|
808
|
+
);
|
|
809
|
+
}) });
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
// ../src/layers/ImageOverlayLayer.tsx
|
|
813
|
+
import {
|
|
814
|
+
AbsoluteFill as AbsoluteFill7,
|
|
815
|
+
Img,
|
|
816
|
+
interpolate as interpolate5,
|
|
817
|
+
Sequence as Sequence3,
|
|
818
|
+
useCurrentFrame as useCurrentFrame4,
|
|
819
|
+
useVideoConfig as useVideoConfig4
|
|
820
|
+
} from "remotion";
|
|
821
|
+
import { Fragment as Fragment5, jsx as jsx7 } from "react/jsx-runtime";
|
|
822
|
+
var FADE_MS = 300;
|
|
823
|
+
var alignFor = (corner) => {
|
|
824
|
+
const vertical = corner.startsWith("top") ? "flex-start" : corner.startsWith("bottom") ? "flex-end" : "center";
|
|
825
|
+
const horizontal = corner.endsWith("left") ? "flex-start" : corner.endsWith("right") ? "flex-end" : "center";
|
|
826
|
+
return { vertical, horizontal };
|
|
827
|
+
};
|
|
828
|
+
var ImageOverlay = ({ item, durationInFrames, fps, safeZones }) => {
|
|
829
|
+
const frame = useCurrentFrame4();
|
|
830
|
+
const { width, height } = useVideoConfig4();
|
|
831
|
+
const fadeF = Math.max(1, msToFrames(FADE_MS, fps));
|
|
832
|
+
const opacity = interpolate5(
|
|
833
|
+
frame,
|
|
834
|
+
[0, fadeF, Math.max(fadeF, durationInFrames - fadeF), durationInFrames],
|
|
835
|
+
[0, 1, 1, 0],
|
|
836
|
+
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
|
|
837
|
+
) * item.opacity;
|
|
838
|
+
const { vertical, horizontal } = alignFor(item.corner);
|
|
839
|
+
return /* @__PURE__ */ jsx7(
|
|
840
|
+
AbsoluteFill7,
|
|
841
|
+
{
|
|
842
|
+
style: {
|
|
843
|
+
justifyContent: vertical,
|
|
844
|
+
alignItems: horizontal,
|
|
845
|
+
paddingTop: height * safeZones.marginTopPercent,
|
|
846
|
+
paddingBottom: height * safeZones.marginBottomPercent,
|
|
847
|
+
paddingLeft: width * safeZones.marginSidePercent,
|
|
848
|
+
paddingRight: width * safeZones.marginSidePercent
|
|
849
|
+
},
|
|
850
|
+
children: /* @__PURE__ */ jsx7(
|
|
851
|
+
Img,
|
|
852
|
+
{
|
|
853
|
+
src: item.src,
|
|
854
|
+
style: {
|
|
855
|
+
height: height * item.heightPct,
|
|
856
|
+
width: "auto",
|
|
857
|
+
maxWidth: width * 0.8,
|
|
858
|
+
objectFit: "contain",
|
|
859
|
+
opacity
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
)
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
};
|
|
866
|
+
var ImageOverlayLayer = ({ imageOverlays, safeZones, fps }) => {
|
|
867
|
+
if (!imageOverlays || imageOverlays.length === 0) return null;
|
|
868
|
+
return /* @__PURE__ */ jsx7(Fragment5, { children: imageOverlays.map((item, i) => {
|
|
869
|
+
const from = msToFrames(item.startMs, fps);
|
|
870
|
+
const durationInFrames = Math.max(
|
|
871
|
+
1,
|
|
872
|
+
msToFrames(item.endMs, fps) - from
|
|
873
|
+
);
|
|
874
|
+
return /* @__PURE__ */ jsx7(
|
|
875
|
+
Sequence3,
|
|
876
|
+
{
|
|
877
|
+
from,
|
|
878
|
+
durationInFrames,
|
|
879
|
+
name: `image-overlay-${i}`,
|
|
880
|
+
children: /* @__PURE__ */ jsx7(
|
|
881
|
+
ImageOverlay,
|
|
882
|
+
{
|
|
883
|
+
item,
|
|
884
|
+
durationInFrames,
|
|
885
|
+
fps,
|
|
886
|
+
safeZones
|
|
887
|
+
}
|
|
888
|
+
)
|
|
889
|
+
},
|
|
890
|
+
i
|
|
891
|
+
);
|
|
892
|
+
}) });
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
// ../src/layers/ProgressBarLayer.tsx
|
|
896
|
+
import { useCurrentFrame as useCurrentFrame5, useVideoConfig as useVideoConfig5 } from "remotion";
|
|
897
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
898
|
+
var ProgressBarLayer = ({ progressBar, sourceDurationInSeconds, fps }) => {
|
|
899
|
+
const frame = useCurrentFrame5();
|
|
900
|
+
const { width, height } = useVideoConfig5();
|
|
901
|
+
if (!progressBar.enabled || sourceDurationInSeconds <= 0) return null;
|
|
902
|
+
const totalFrames = Math.max(1, Math.round(sourceDurationInSeconds * fps));
|
|
903
|
+
const progress = Math.min(1, frame / totalFrames);
|
|
904
|
+
return /* @__PURE__ */ jsx8(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
style: {
|
|
908
|
+
position: "absolute",
|
|
909
|
+
top: 0,
|
|
910
|
+
left: 0,
|
|
911
|
+
width: `${progress * 100}%`,
|
|
912
|
+
// heightPx calibrado p/ lado menor 1080 → escala p/ o canvas real.
|
|
913
|
+
height: Math.max(2, progressBar.heightPx * uiScale(width, height)),
|
|
914
|
+
backgroundColor: progressBar.color,
|
|
915
|
+
opacity: 0.9
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
);
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
// ../src/layers/EndcardSequence.tsx
|
|
922
|
+
import {
|
|
923
|
+
AbsoluteFill as AbsoluteFill8,
|
|
924
|
+
Img as Img2,
|
|
925
|
+
interpolate as interpolate6,
|
|
926
|
+
Sequence as Sequence4,
|
|
927
|
+
spring as spring4,
|
|
928
|
+
useCurrentFrame as useCurrentFrame6,
|
|
929
|
+
useVideoConfig as useVideoConfig6
|
|
930
|
+
} from "remotion";
|
|
931
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
932
|
+
var DEFAULT_GRADIENT_ANGLE_DEG = 160;
|
|
933
|
+
var DEFAULT_LOGO_HEIGHT_PX = 140;
|
|
934
|
+
var backgroundStyle = (endcard) => {
|
|
935
|
+
if (endcard.gradient?.from && endcard.gradient?.to) {
|
|
936
|
+
const angle = endcard.gradient.angleDeg ?? DEFAULT_GRADIENT_ANGLE_DEG;
|
|
937
|
+
return {
|
|
938
|
+
background: `linear-gradient(${angle}deg, ${endcard.gradient.from}, ${endcard.gradient.to})`
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
return { backgroundColor: endcard.background ?? "#101010" };
|
|
942
|
+
};
|
|
943
|
+
var EndcardCard = ({
|
|
944
|
+
endcard,
|
|
945
|
+
fps
|
|
946
|
+
}) => {
|
|
947
|
+
const frame = useCurrentFrame6();
|
|
948
|
+
const { width, height } = useVideoConfig6();
|
|
949
|
+
const animate = endcard.animateIn ?? true;
|
|
950
|
+
const ui = uiScale(width, height);
|
|
951
|
+
const logoIn = animate ? spring4({
|
|
952
|
+
fps,
|
|
953
|
+
frame,
|
|
954
|
+
config: { damping: 12, stiffness: 170, mass: 0.7 },
|
|
955
|
+
durationInFrames: Math.round(fps * 0.5)
|
|
956
|
+
}) : 1;
|
|
957
|
+
const textDelay = animate ? Math.round(fps * 0.15) : 0;
|
|
958
|
+
const textIn = animate ? spring4({
|
|
959
|
+
fps,
|
|
960
|
+
frame,
|
|
961
|
+
delay: textDelay,
|
|
962
|
+
config: { damping: 200 },
|
|
963
|
+
durationInFrames: Math.round(fps * 0.5)
|
|
964
|
+
}) : 1;
|
|
965
|
+
const textY = interpolate6(textIn, [0, 1], [40 * ui, 0]);
|
|
966
|
+
return /* @__PURE__ */ jsxs3(
|
|
967
|
+
AbsoluteFill8,
|
|
968
|
+
{
|
|
969
|
+
style: {
|
|
970
|
+
...backgroundStyle(endcard),
|
|
971
|
+
alignItems: "center",
|
|
972
|
+
justifyContent: "center",
|
|
973
|
+
padding: "8%"
|
|
974
|
+
},
|
|
975
|
+
children: [
|
|
976
|
+
endcard.logoUrl ? /* @__PURE__ */ jsx9(
|
|
977
|
+
Img2,
|
|
978
|
+
{
|
|
979
|
+
src: endcard.logoUrl,
|
|
980
|
+
style: {
|
|
981
|
+
height: (endcard.logoHeightPx ?? DEFAULT_LOGO_HEIGHT_PX) * ui,
|
|
982
|
+
objectFit: "contain",
|
|
983
|
+
marginBottom: 48 * ui,
|
|
984
|
+
opacity: logoIn,
|
|
985
|
+
transform: `scale(${interpolate6(logoIn, [0, 1], [0.6, 1])})`
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
) : null,
|
|
989
|
+
/* @__PURE__ */ jsx9(
|
|
990
|
+
"div",
|
|
991
|
+
{
|
|
992
|
+
style: {
|
|
993
|
+
opacity: textIn,
|
|
994
|
+
transform: `translateY(${textY}px)`,
|
|
995
|
+
textAlign: "center",
|
|
996
|
+
fontFamily: resolveFontFamily(endcard.fontFamily ?? DEFAULT_OVERLAY_FONT),
|
|
997
|
+
fontWeight: 700,
|
|
998
|
+
fontSize: (endcard.fontSize ?? 88) * ui,
|
|
999
|
+
color: endcard.color ?? "#FFFFFF",
|
|
1000
|
+
textShadow: textOutline("#000000", Math.max(1, Math.round(2 * ui))),
|
|
1001
|
+
lineHeight: 1.2,
|
|
1002
|
+
whiteSpace: "pre-wrap"
|
|
1003
|
+
},
|
|
1004
|
+
children: endcard.text
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
]
|
|
1008
|
+
}
|
|
1009
|
+
);
|
|
1010
|
+
};
|
|
1011
|
+
var EndcardSequence = ({ endcard, sourceDurationInSeconds, fps }) => {
|
|
1012
|
+
if (!endcard.enabled || !endcard.text) return null;
|
|
1013
|
+
const from = Math.round(sourceDurationInSeconds * fps);
|
|
1014
|
+
const durationInFrames = Math.max(1, msToFrames(endcard.durationMs, fps));
|
|
1015
|
+
return /* @__PURE__ */ jsx9(Sequence4, { from, durationInFrames, name: "endcard", children: /* @__PURE__ */ jsx9(EndcardCard, { endcard, fps }) });
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
// ../src/SmartStudioComposition.tsx
|
|
1019
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1020
|
+
var SmartStudioComposition = (props) => {
|
|
1021
|
+
const { source, output, captions, overlays, endcard, safeZones, reframe, zoom, cuts } = props;
|
|
1022
|
+
const theme = resolveTheme(props.stylePreset);
|
|
1023
|
+
const captionStyle = resolveCaptionStyle(theme, captions.style);
|
|
1024
|
+
const displayMode = resolveCaptionDisplayMode(theme, captions.displayMode);
|
|
1025
|
+
const combineMs = resolveCaptionCombineMs(theme, captions.combineTokensWithinMs);
|
|
1026
|
+
const themedOverlays = overlays.map((o) => resolveOverlayItem(theme, o));
|
|
1027
|
+
const themedEndcard = resolveEndcard(theme, endcard);
|
|
1028
|
+
const progressBar = resolveProgressBar(theme, props.progressBar);
|
|
1029
|
+
const cutTransition = resolveCutTransition(
|
|
1030
|
+
theme,
|
|
1031
|
+
cuts.transitionType,
|
|
1032
|
+
cuts.transitionMs
|
|
1033
|
+
);
|
|
1034
|
+
const videoFilter = props.colorGrade ? gradeFilter(props.colorGrade.preset, props.colorGrade.intensity) : "none";
|
|
1035
|
+
return /* @__PURE__ */ jsxs4(AbsoluteFill9, { style: { backgroundColor: "#000000" }, children: [
|
|
1036
|
+
/* @__PURE__ */ jsx10(AbsoluteFill9, { style: videoFilter !== "none" ? { filter: videoFilter } : void 0, children: cuts.enabled ? (
|
|
1037
|
+
// Cortes DENTRO da composição: CutsSequencer é reframe-aware por
|
|
1038
|
+
// segmento; o ZoomLayer envolve tudo (janelas já remapeadas p/ a
|
|
1039
|
+
// timeline pós-corte pelo Python).
|
|
1040
|
+
/* @__PURE__ */ jsx10(ZoomLayer, { zoom, fps: output.fps, children: /* @__PURE__ */ jsx10(
|
|
1041
|
+
CutsSequencer,
|
|
1042
|
+
{
|
|
1043
|
+
source,
|
|
1044
|
+
cuts,
|
|
1045
|
+
reframe,
|
|
1046
|
+
transition: cutTransition
|
|
1047
|
+
}
|
|
1048
|
+
) })
|
|
1049
|
+
) : /* @__PURE__ */ jsx10(ReframeContainer, { reframe, output, source, children: /* @__PURE__ */ jsx10(ZoomLayer, { zoom, fps: output.fps, children: /* @__PURE__ */ jsx10(
|
|
1050
|
+
VideoBase,
|
|
1051
|
+
{
|
|
1052
|
+
source,
|
|
1053
|
+
fit: reframe.enabled ? "contain" : "cover"
|
|
1054
|
+
}
|
|
1055
|
+
) }) }) }),
|
|
1056
|
+
/* @__PURE__ */ jsx10(
|
|
1057
|
+
CaptionsLayer,
|
|
1058
|
+
{
|
|
1059
|
+
captions,
|
|
1060
|
+
style: captionStyle,
|
|
1061
|
+
displayMode,
|
|
1062
|
+
combineTokensWithinMs: combineMs,
|
|
1063
|
+
safeZones,
|
|
1064
|
+
fps: output.fps
|
|
1065
|
+
}
|
|
1066
|
+
),
|
|
1067
|
+
/* @__PURE__ */ jsx10(
|
|
1068
|
+
OverlayLayer,
|
|
1069
|
+
{
|
|
1070
|
+
overlays: themedOverlays,
|
|
1071
|
+
safeZones,
|
|
1072
|
+
fps: output.fps,
|
|
1073
|
+
captionsPosition: captions.enabled && captions.tokens.length > 0 ? captionStyle.position : null
|
|
1074
|
+
}
|
|
1075
|
+
),
|
|
1076
|
+
/* @__PURE__ */ jsx10(
|
|
1077
|
+
ImageOverlayLayer,
|
|
1078
|
+
{
|
|
1079
|
+
imageOverlays: props.imageOverlays,
|
|
1080
|
+
safeZones,
|
|
1081
|
+
fps: output.fps
|
|
1082
|
+
}
|
|
1083
|
+
),
|
|
1084
|
+
/* @__PURE__ */ jsx10(
|
|
1085
|
+
ProgressBarLayer,
|
|
1086
|
+
{
|
|
1087
|
+
progressBar,
|
|
1088
|
+
sourceDurationInSeconds: source.durationInSeconds,
|
|
1089
|
+
fps: output.fps
|
|
1090
|
+
}
|
|
1091
|
+
),
|
|
1092
|
+
/* @__PURE__ */ jsx10(
|
|
1093
|
+
EndcardSequence,
|
|
1094
|
+
{
|
|
1095
|
+
endcard: themedEndcard,
|
|
1096
|
+
sourceDurationInSeconds: source.durationInSeconds,
|
|
1097
|
+
fps: output.fps
|
|
1098
|
+
}
|
|
1099
|
+
)
|
|
1100
|
+
] });
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
// ../src/metadata.ts
|
|
1104
|
+
var FALLBACK_DURATION_IN_FRAMES = 300;
|
|
1105
|
+
var calculateSmartStudioMetadata = ({
|
|
1106
|
+
props
|
|
1107
|
+
}) => {
|
|
1108
|
+
const fps = props.output?.fps || props.source?.fps || 30;
|
|
1109
|
+
const sourceSeconds = props.source?.durationInSeconds || 0;
|
|
1110
|
+
const endcardSeconds = props.endcard?.enabled && props.endcard?.durationMs ? props.endcard.durationMs / 1e3 : 0;
|
|
1111
|
+
const totalSeconds = sourceSeconds + endcardSeconds;
|
|
1112
|
+
const durationInFrames = Math.max(
|
|
1113
|
+
1,
|
|
1114
|
+
Math.round(totalSeconds * fps) || FALLBACK_DURATION_IN_FRAMES
|
|
1115
|
+
);
|
|
1116
|
+
return {
|
|
1117
|
+
durationInFrames,
|
|
1118
|
+
fps,
|
|
1119
|
+
width: props.output?.width || props.source?.width || 1080,
|
|
1120
|
+
height: props.output?.height || props.source?.height || 1920
|
|
1121
|
+
};
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// ../src/types.ts
|
|
1125
|
+
var DEFAULT_INPUT_PROPS = {
|
|
1126
|
+
source: {
|
|
1127
|
+
videoPath: "",
|
|
1128
|
+
width: 1080,
|
|
1129
|
+
height: 1920,
|
|
1130
|
+
durationInSeconds: 0,
|
|
1131
|
+
fps: 30
|
|
1132
|
+
},
|
|
1133
|
+
output: { width: 1080, height: 1920, fps: 30 },
|
|
1134
|
+
captions: {
|
|
1135
|
+
enabled: false,
|
|
1136
|
+
tokens: [],
|
|
1137
|
+
// style/displayMode/combineTokensWithinMs = overrides explícitos; os
|
|
1138
|
+
// defaults e o tema (stylePreset) são resolvidos em themes.ts.
|
|
1139
|
+
style: {}
|
|
1140
|
+
},
|
|
1141
|
+
overlays: [],
|
|
1142
|
+
endcard: {
|
|
1143
|
+
enabled: false,
|
|
1144
|
+
text: "",
|
|
1145
|
+
durationMs: 3e3
|
|
1146
|
+
},
|
|
1147
|
+
safeZones: {
|
|
1148
|
+
enabled: true,
|
|
1149
|
+
marginTopPercent: 0.08,
|
|
1150
|
+
marginBottomPercent: 0.25,
|
|
1151
|
+
marginSidePercent: 0.03
|
|
1152
|
+
},
|
|
1153
|
+
reframe: { enabled: false, mode: "blur_fill" },
|
|
1154
|
+
zoom: { enabled: false, startMs: 0, endMs: 0, factor: 1.15, animated: true },
|
|
1155
|
+
cuts: { enabled: false, segmentsKeepMs: [] }
|
|
1156
|
+
};
|
|
1157
|
+
export {
|
|
1158
|
+
DEFAULT_INPUT_PROPS,
|
|
1159
|
+
FALLBACK_DURATION_IN_FRAMES,
|
|
1160
|
+
STYLE_PRESETS,
|
|
1161
|
+
SmartStudioComposition,
|
|
1162
|
+
calculateSmartStudioMetadata
|
|
1163
|
+
};
|