tommy24 commited on
Commit
40e0429
·
verified ·
1 Parent(s): f6bd605

Upload 5 files

Browse files
remotion/package.json ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "remotion-renderer",
3
+ "version": "1.0.0",
4
+ "description": "Remotion video renderer for HF Spaces",
5
+ "scripts": {
6
+ "build": "remotion bundle"
7
+ },
8
+ "dependencies": {
9
+ "@remotion/bundler": "^4.0.0",
10
+ "@remotion/cli": "^4.0.0",
11
+ "@remotion/player": "^4.0.0",
12
+ "react": "^18.2.0",
13
+ "react-dom": "^18.2.0",
14
+ "remotion": "^4.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/react": "^18.2.0",
18
+ "typescript": "^5.0.0"
19
+ }
20
+ }
remotion/remotion.config.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import { Config } from '@remotion/cli/config';
2
+
3
+ Config.setVideoImageFormat('jpeg');
4
+ Config.setOverwriteOutput(true);
remotion/src/Composition.tsx ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React from 'react';
4
+ import { AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig, interpolate, spring, random, Audio, OffthreadVideo } from 'remotion';
5
+ import { VideoScript, ScriptScene } from '@/lib/types';
6
+
7
+ // --- VISUAL UTILS ---
8
+
9
+ const GlitchText: React.FC<{
10
+ text: string;
11
+ color: string;
12
+ fontSize: number;
13
+ fontFamily?: string;
14
+ frame: number;
15
+ }> = ({ text, color, fontSize, fontFamily = 'Inter, sans-serif', frame }) => {
16
+ // Jitter calculation
17
+ const shakeX = Math.sin(frame * 0.8) * 4;
18
+ const shakeY = Math.cos(frame * 1.1) * 2;
19
+ const rgbOffset = fontSize * 0.03;
20
+
21
+ return (
22
+ <div style={{ position: 'relative', display: 'inline-block' }}>
23
+ {/* Cyan Channel */}
24
+ <div style={{
25
+ position: 'absolute',
26
+ top: 0,
27
+ left: -rgbOffset,
28
+ color: 'cyan',
29
+ fontSize,
30
+ fontFamily,
31
+ opacity: 0.7,
32
+ mixBlendMode: 'screen',
33
+ transform: `translate(${-shakeX}px, ${shakeY}px)`,
34
+ clipPath: 'polygon(0 0, 100% 0, 100% 45%, 0 45%)',
35
+ }}>
36
+ {text}
37
+ </div>
38
+
39
+ {/* Magenta Channel */}
40
+ <div style={{
41
+ position: 'absolute',
42
+ top: 0,
43
+ left: rgbOffset,
44
+ color: 'magenta',
45
+ fontSize,
46
+ fontFamily,
47
+ opacity: 0.7,
48
+ mixBlendMode: 'screen',
49
+ transform: `translate(${shakeX}px, ${-shakeY}px)`,
50
+ clipPath: 'polygon(0 55%, 100% 55%, 100% 100%, 0 100%)',
51
+ }}>
52
+ {text}
53
+ </div>
54
+
55
+ {/* Main Text */}
56
+ <div style={{ position: 'relative', color, fontSize, fontFamily, zIndex: 10 }}>
57
+ {text}
58
+ </div>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ const GridBackground: React.FC<{ color: string; velocity?: number }> = ({ color, velocity = 2 }) => {
64
+ const frame = useCurrentFrame();
65
+ const offset = (frame * velocity) % 100;
66
+
67
+ return (
68
+ <AbsoluteFill style={{
69
+ backgroundImage: `linear-gradient(${color} 1px, transparent 1px), linear-gradient(90deg, ${color} 1px, transparent 1px)`,
70
+ backgroundSize: '100px 100px',
71
+ backgroundPosition: `center ${offset}px`,
72
+ opacity: 0.15,
73
+ }} />
74
+ );
75
+ };
76
+
77
+ const Scanlines: React.FC = () => {
78
+ return (
79
+ <AbsoluteFill style={{
80
+ background: 'linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06))',
81
+ backgroundSize: '100% 4px, 6px 100%',
82
+ pointerEvents: 'none',
83
+ zIndex: 100,
84
+ }} />
85
+ );
86
+ };
87
+
88
+ // --- SCENE LAYOUTS ---
89
+
90
+ // 1. STANDARD STOMP: Huge centered text with glitch
91
+ const LayoutStandard: React.FC<{ scene: ScriptScene; scale: number; opacity: number; color: string }> = ({ scene, scale, opacity, color }) => {
92
+ const frame = useCurrentFrame();
93
+ const { width } = useVideoConfig();
94
+ const fontSize = width * 0.09;
95
+
96
+ return (
97
+ <div style={{
98
+ transform: `scale(${scale})`,
99
+ opacity: opacity,
100
+ display: 'flex',
101
+ flexDirection: 'column',
102
+ alignItems: 'center',
103
+ justifyContent: 'center',
104
+ textAlign: 'center',
105
+ width: '100%',
106
+ }}>
107
+ <div className="font-[900] tracking-tighter leading-[0.8] uppercase break-words max-w-[90%]">
108
+ <GlitchText text={scene.title} color={color} fontSize={fontSize} frame={frame} />
109
+ </div>
110
+ </div>
111
+ );
112
+ };
113
+
114
+ // 2. RAPID FIRE: Flash words one by one
115
+ const LayoutRapidFire: React.FC<{ scene: ScriptScene; durationInFrames: number; color: string }> = ({ scene, durationInFrames, color }) => {
116
+ const frame = useCurrentFrame();
117
+ const { width } = useVideoConfig();
118
+ const words = scene.title.split(' ');
119
+
120
+ if (words.length === 1) return <LayoutStandard scene={scene} scale={1} opacity={1} color={color} />;
121
+
122
+ const wordsToShow = words.length;
123
+ const framesPerWord = durationInFrames / wordsToShow;
124
+ const currentWordIndex = Math.floor(frame / framesPerWord);
125
+ const currentWord = words[Math.min(currentWordIndex, words.length - 1)];
126
+
127
+ const localFrame = frame % framesPerWord;
128
+ const pop = interpolate(localFrame, [0, 3], [0.8, 1.2], { extrapolateRight: 'clamp' });
129
+ const fontSize = width * 0.11;
130
+
131
+ return (
132
+ <div style={{
133
+ display: 'flex',
134
+ alignItems: 'center',
135
+ justifyContent: 'center',
136
+ width: '100%',
137
+ height: '100%'
138
+ }}>
139
+ <h1 style={{
140
+ color,
141
+ transform: `scale(${pop})`,
142
+ fontSize: fontSize,
143
+ fontWeight: 900,
144
+ textTransform: 'uppercase',
145
+ letterSpacing: '-0.05em'
146
+ }}>
147
+ {currentWord}
148
+ </h1>
149
+ </div>
150
+ );
151
+ };
152
+
153
+ // 3. REPEATER: Background text texture + Main Text
154
+ const LayoutRepeater: React.FC<{ scene: ScriptScene; scale: number; color: string; bg: string }> = ({ scene, scale, color, bg }) => {
155
+ const frame = useCurrentFrame();
156
+ const { width } = useVideoConfig();
157
+
158
+ const bgFontSize = width * 0.04;
159
+ const mainFontSize = width * 0.06;
160
+
161
+ return (
162
+ <AbsoluteFill style={{ overflow: 'hidden', alignItems: 'center', justifyContent: 'center' }}>
163
+ <div style={{
164
+ position: 'absolute',
165
+ top: '-50%',
166
+ left: '-50%',
167
+ width: '200%',
168
+ height: '200%',
169
+ display: 'flex',
170
+ flexWrap: 'wrap',
171
+ opacity: 0.1,
172
+ transform: `rotate(-15deg) translateY(${frame * 2}px)`,
173
+ justifyContent: 'center',
174
+ alignContent: 'center',
175
+ gap: '20px'
176
+ }}>
177
+ {Array(40).fill(scene.title).map((t, i) => (
178
+ <span key={i} style={{ color, fontSize: bgFontSize, fontWeight: 900, textTransform: 'uppercase' }}>{t}</span>
179
+ ))}
180
+ </div>
181
+
182
+ <div style={{ transform: `scale(${scale})`, zIndex: 10, border: `4px solid ${color}`, padding: `${width * 0.02}px ${width * 0.04}px`, backgroundColor: bg }}>
183
+ <h1 style={{ color, fontSize: mainFontSize, fontWeight: 900, textTransform: 'uppercase', lineHeight: 1 }}>
184
+ {scene.title}
185
+ </h1>
186
+ </div>
187
+ </AbsoluteFill>
188
+ );
189
+ };
190
+
191
+ // 4. TERMINAL: Monospace, grid, technical feel
192
+ const LayoutTerminal: React.FC<{ scene: ScriptScene; color: string }> = ({ scene, color }) => {
193
+ const frame = useCurrentFrame();
194
+ const { width } = useVideoConfig();
195
+ const showCursor = Math.floor(frame / 15) % 2 === 0;
196
+
197
+ const padding = width * 0.05;
198
+ const smallSize = width * 0.02;
199
+ const bigSize = width * 0.05;
200
+
201
+ return (
202
+ <AbsoluteFill style={{ alignItems: 'flex-start', justifyContent: 'center', padding: padding, fontFamily: 'JetBrains Mono, monospace' }}>
203
+ <GridBackground color={color} velocity={5} />
204
+
205
+ <div style={{ zIndex: 10 }}>
206
+ <div style={{ color, fontSize: smallSize, marginBottom: '20px', opacity: 0.7 }}>
207
+ $ run_sequence --force
208
+ </div>
209
+ <div style={{ color, fontSize: bigSize, fontWeight: 'bold', lineHeight: 1.1, textTransform: 'uppercase' }}>
210
+ {'>'} {scene.title}
211
+ {showCursor && <span style={{ color: color }}>_</span>}
212
+ </div>
213
+ {scene.codeSnippet && (
214
+ <div style={{ marginTop: '40px', padding: '20px', borderLeft: `4px solid ${color}`, backgroundColor: 'rgba(255,255,255,0.1)' }}>
215
+ <code style={{ color, fontSize: smallSize * 1.5 }}>
216
+ import &#123; {scene.codeSnippet} &#125; from 'future';
217
+ </code>
218
+ </div>
219
+ )}
220
+ </div>
221
+ </AbsoluteFill>
222
+ );
223
+ };
224
+
225
+ // 5. ECHO: Main text with expanding stroke echoes
226
+ const LayoutEcho: React.FC<{ scene: ScriptScene; color: string }> = ({ scene, color }) => {
227
+ const frame = useCurrentFrame();
228
+ const { width } = useVideoConfig();
229
+ const fontSize = width * 0.08;
230
+
231
+ const echoes = [1, 2, 3].map(i => {
232
+ const delay = i * 4;
233
+ const activeFrame = Math.max(0, frame - delay);
234
+ const scale = interpolate(activeFrame, [0, 20], [1, 2.5], { extrapolateRight: 'clamp' });
235
+ const opacity = interpolate(activeFrame, [0, 15], [0.6, 0], { extrapolateRight: 'clamp' });
236
+ return { scale, opacity };
237
+ });
238
+
239
+ return (
240
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', width: '100%', height: '100%' }}>
241
+ {echoes.map((e, i) => (
242
+ <h1 key={i} style={{
243
+ position: 'absolute',
244
+ color: 'transparent',
245
+ WebkitTextStroke: `2px ${color}`,
246
+ fontSize: fontSize,
247
+ fontWeight: 900,
248
+ transform: `scale(${e.scale})`,
249
+ opacity: e.opacity,
250
+ zIndex: 0,
251
+ textTransform: 'uppercase',
252
+ textAlign: 'center',
253
+ width: '100%'
254
+ }}>
255
+ {scene.title}
256
+ </h1>
257
+ ))}
258
+ <h1 style={{ color, fontSize: fontSize, fontWeight: 900, zIndex: 10, textTransform: 'uppercase', textAlign: 'center', lineHeight: 0.9 }}>
259
+ {scene.title}
260
+ </h1>
261
+ </div>
262
+ );
263
+ };
264
+
265
+ // 6. VERTICAL STACK: Wall of text
266
+ const LayoutVertical: React.FC<{ scene: ScriptScene; color: string }> = ({ scene, color }) => {
267
+ const { width } = useVideoConfig();
268
+ const fontSize = width * 0.06;
269
+
270
+ return (
271
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%', lineHeight: 0.8 }}>
272
+ {[...Array(7)].map((_, i) => (
273
+ <h1 key={i} style={{
274
+ fontSize: fontSize,
275
+ fontWeight: 900,
276
+ color: i === 3 ? color : 'transparent',
277
+ WebkitTextStroke: i === 3 ? 'none' : `1px ${color}`,
278
+ opacity: i === 3 ? 1 : 0.3,
279
+ textTransform: 'uppercase',
280
+ textAlign: 'center',
281
+ margin: 0
282
+ }}>
283
+ {scene.title}
284
+ </h1>
285
+ ))}
286
+ </div>
287
+ );
288
+ };
289
+
290
+ // 7. TILT: 3D Perspective Rotation
291
+ const LayoutTilt: React.FC<{ scene: ScriptScene; color: string }> = ({ scene, color }) => {
292
+ const frame = useCurrentFrame();
293
+ const { width } = useVideoConfig();
294
+ const fontSize = width * 0.1;
295
+
296
+ const rotateX = interpolate(frame, [0, 30], [45, -10], { extrapolateRight: 'clamp' });
297
+ const scale = interpolate(frame, [0, 30], [0.8, 1.1], { extrapolateRight: 'clamp' });
298
+
299
+ return (
300
+ <div style={{
301
+ display: 'flex',
302
+ alignItems: 'center',
303
+ justifyContent: 'center',
304
+ width: '100%',
305
+ height: '100%',
306
+ perspective: '1200px'
307
+ }}>
308
+ <h1 style={{
309
+ fontSize: fontSize,
310
+ fontWeight: 900,
311
+ color: color,
312
+ transform: `rotateX(${rotateX}deg) scale(${scale})`,
313
+ textTransform: 'uppercase',
314
+ textAlign: 'center',
315
+ textShadow: `0 20px 40px ${color}40`,
316
+ lineHeight: 0.85
317
+ }}>
318
+ {scene.title}
319
+ </h1>
320
+ </div>
321
+ );
322
+ };
323
+
324
+ // --- MAIN SCENE WRAPPER ---
325
+
326
+ const StompScene: React.FC<{ scene: ScriptScene; index: number; durationInFrames: number; primaryColor: string }> = ({ scene, index, durationInFrames, primaryColor }) => {
327
+ const frame = useCurrentFrame();
328
+ const { fps } = useVideoConfig();
329
+
330
+ const scale = spring({
331
+ frame,
332
+ fps,
333
+ config: { damping: 12, stiffness: 200, mass: 0.5 },
334
+ from: 3,
335
+ to: 1,
336
+ });
337
+
338
+ const opacity = interpolate(frame, [0, 5], [0, 1]);
339
+
340
+ const isInverted = index % 2 !== 0;
341
+ const mainColor = primaryColor || '#FFFFFF';
342
+
343
+ const bg = isInverted ? mainColor : '#050505';
344
+ const textPrimary = isInverted ? '#000000' : mainColor;
345
+ const textSecondary = isInverted ? '#444' : '#666';
346
+
347
+ const layoutSeed = random(index) * 100;
348
+ let layoutType = 'STANDARD';
349
+
350
+ if (layoutSeed < 14) layoutType = 'STANDARD';
351
+ else if (layoutSeed < 28) layoutType = 'RAPID_FIRE';
352
+ else if (layoutSeed < 42) layoutType = 'REPEATER';
353
+ else if (layoutSeed < 56) layoutType = 'TERMINAL';
354
+ else if (layoutSeed < 70) layoutType = 'ECHO';
355
+ else if (layoutSeed < 84) layoutType = 'VERTICAL';
356
+ else layoutType = 'TILT';
357
+
358
+ if (scene.title.split(' ').length > 3) layoutType = 'RAPID_FIRE';
359
+
360
+ return (
361
+ <AbsoluteFill style={{ backgroundColor: bg, overflow: 'hidden' }}>
362
+ <AbsoluteFill style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
363
+ {layoutType === 'STANDARD' && <LayoutStandard scene={scene} scale={scale} opacity={opacity} color={textPrimary} />}
364
+ {layoutType === 'RAPID_FIRE' && <LayoutRapidFire scene={scene} durationInFrames={durationInFrames} color={textPrimary} />}
365
+ {layoutType === 'REPEATER' && <LayoutRepeater scene={scene} scale={scale} color={textPrimary} bg={bg} />}
366
+ {layoutType === 'TERMINAL' && <LayoutTerminal scene={scene} color={textPrimary} />}
367
+ {layoutType === 'ECHO' && <LayoutEcho scene={scene} color={textPrimary} />}
368
+ {layoutType === 'VERTICAL' && <LayoutVertical scene={scene} color={textPrimary} />}
369
+ {layoutType === 'TILT' && <LayoutTilt scene={scene} color={textPrimary} />}
370
+ </AbsoluteFill>
371
+
372
+ <div className="absolute bottom-10 left-0 w-full px-12 flex justify-between items-end z-20">
373
+ <div style={{ color: textSecondary }} className="font-mono text-xl font-bold uppercase tracking-wider max-w-2xl">
374
+ {scene.voiceover}
375
+ </div>
376
+ <div style={{ color: textSecondary }} className="font-mono text-sm tracking-widest border border-current px-2 py-1 rounded">
377
+ SCENE_0{index + 1} // {scene.duration}s
378
+ </div>
379
+ </div>
380
+
381
+ <Sequence from={0} durationInFrames={4}>
382
+ <AbsoluteFill style={{ backgroundColor: textPrimary, opacity: interpolate(frame, [0, 4], [1, 0]) }} />
383
+ </Sequence>
384
+ </AbsoluteFill>
385
+ );
386
+ };
387
+
388
+ export const RemotionComposition: React.FC<{ script: VideoScript }> = ({ script }) => {
389
+ const { fps, width } = useVideoConfig();
390
+
391
+ let currentStartFrame = 0;
392
+
393
+ const tracks: Record<string, string> = {
394
+ '8BIT': "/audio/01.mp3",
395
+ 'STOMP': "/audio/02.mp3",
396
+ 'UPBEAT': "/audio/03.mp3",
397
+ 'ROCK': "/audio/04.mp3"
398
+ };
399
+
400
+ const audioSrc = tracks[script.config?.musicTrack] || tracks['8BIT'];
401
+
402
+ return (
403
+ <AbsoluteFill style={{ backgroundColor: '#000' }}>
404
+ <Scanlines />
405
+
406
+ <OffthreadVideo
407
+ src={audioSrc}
408
+ volume={script.config?.bgVolume ?? 0.4}
409
+ startFrom={0}
410
+ />
411
+
412
+ {script.voiceoverUrl && (
413
+ <OffthreadVideo
414
+ src={script.voiceoverUrl}
415
+ volume={script.config?.voiceVolume ?? 1.0}
416
+ startFrom={0}
417
+ />
418
+ )}
419
+
420
+ {script.scenes.map((scene, index) => {
421
+ const durationInFrames = Math.ceil(scene.duration * fps);
422
+ const from = currentStartFrame;
423
+ currentStartFrame += durationInFrames;
424
+
425
+ return (
426
+ <Sequence key={index} from={from} durationInFrames={durationInFrames}>
427
+ <StompScene
428
+ scene={scene}
429
+ index={index}
430
+ durationInFrames={durationInFrames}
431
+ primaryColor={script.config?.primaryColor || '#FFFFFF'}
432
+ />
433
+ </Sequence>
434
+ );
435
+ })}
436
+
437
+ {/* Outro */}
438
+ <Sequence from={currentStartFrame} durationInFrames={90}>
439
+ <AbsoluteFill className="bg-black flex items-center justify-center">
440
+ <div className="flex flex-col items-center z-10">
441
+ <h1 className="font-[900] text-white tracking-tighter uppercase mb-4 animate-pulse" style={{ fontSize: width * 0.15 }}>
442
+ SHIP.
443
+ </h1>
444
+ <div className="w-full h-1 bg-white mb-8"></div>
445
+
446
+ {script.config?.githubRepo ? (
447
+ <div className="flex flex-col items-center gap-4">
448
+ <div className="flex items-center gap-3 text-white">
449
+ <svg viewBox="0 0 24 24" width={width * 0.04} height={width * 0.04} stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round">
450
+ <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
451
+ </svg>
452
+ <span className="font-mono font-bold" style={{ fontSize: width * 0.03 }}>{script.config.githubRepo}</span>
453
+ </div>
454
+ <div className="font-mono text-gray-500" style={{ fontSize: width * 0.015 }}>
455
+ GENERATED WITH SHIP
456
+ </div>
457
+ </div>
458
+ ) : (
459
+ <div className="font-mono text-gray-500" style={{ fontSize: width * 0.015 }}>GENERATED WITH SHIP</div>
460
+ )}
461
+ </div>
462
+ <GridBackground color="#333" />
463
+ </AbsoluteFill>
464
+ </Sequence>
465
+ </AbsoluteFill>
466
+ );
467
+ };
remotion/src/index.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Composition } from 'remotion';
2
+ import { RemotionComposition } from './Composition';
3
+
4
+ export const RemotionRoot: React.FC = () => {
5
+ return (
6
+ <>
7
+ <Composition
8
+ id="VideoComposition"
9
+ component={RemotionComposition}
10
+ durationInFrames={300}
11
+ fps={30}
12
+ width={1920}
13
+ height={1080}
14
+ defaultProps={{
15
+ script: {
16
+ title: "Sample Video",
17
+ scenes: [],
18
+ config: {
19
+ visualTheme: "STOMP",
20
+ primaryColor: "#FFFFFF",
21
+ musicTrack: "STOMP",
22
+ aspectRatio: "16:9"
23
+ }
24
+ }
25
+ }}
26
+ />
27
+ </>
28
+ );
29
+ };
remotion/src/types.ts ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export enum AppStep {
2
+ IDLE = 'IDLE',
3
+ ANALYZING = 'ANALYZING',
4
+ REVIEW = 'REVIEW',
5
+ RENDERING = 'RENDERING',
6
+ COMPLETED = 'COMPLETED',
7
+ AUTH = 'AUTH',
8
+ DASHBOARD = 'DASHBOARD',
9
+ EDITOR = 'EDITOR',
10
+ }
11
+
12
+ export enum InputType {
13
+ GITHUB = 'GITHUB',
14
+ URL = 'URL',
15
+ TEXT = 'TEXT',
16
+ }
17
+
18
+ export interface ScriptScene {
19
+ sceneNumber: number;
20
+ duration: number;
21
+ visualDescription: string;
22
+ voiceover: string;
23
+ voiceoverUrl?: string;
24
+ codeSnippet?: string;
25
+ title: string;
26
+ }
27
+
28
+ export type VideoAspectRatio = '16:9' | '9:16' | '1:1' | '4:5';
29
+ export type VideoResolution = '720p' | '1080p';
30
+
31
+ export interface VideoConfig {
32
+ primaryColor: string;
33
+ musicTrack: string;
34
+ visualTheme: 'STOMP' | 'MINIMAL' | 'GLITCH';
35
+ aspectRatio: VideoAspectRatio;
36
+ voiceId?: string;
37
+ bgVolume?: number;
38
+ voiceVolume?: number;
39
+ targetDuration?: number;
40
+ githubRepo?: string;
41
+ }
42
+
43
+ export interface VideoScript {
44
+ id?: string;
45
+ title: string;
46
+ targetAudience: string;
47
+ scenes: ScriptScene[];
48
+ summary: string;
49
+ config: VideoConfig;
50
+ voiceoverUrl?: string;
51
+ lastModified?: Date;
52
+ }
53
+
54
+ export interface GeneratedVideo {
55
+ uri: string;
56
+ expiryTime?: string;
57
+ }