@xcelsior/demo-pipeline 0.1.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 +47 -0
- package/bin/cli.js +88 -0
- package/package.json +19 -0
- package/template/.claude/commands/demo-video.md +42 -0
- package/template/README.md +69 -0
- package/template/demo.config.json +15 -0
- package/template/package.json +13 -0
- package/template/pipeline/capture_mobile.sh +60 -0
- package/template/pipeline/capture_web.py +186 -0
- package/template/pipeline/gen_music.py +52 -0
- package/template/pipeline/gen_vo.py +29 -0
- package/template/pipeline/run.sh +45 -0
- package/template/remotion/package.json +16 -0
- package/template/remotion/public/brand/logo-white.png +0 -0
- package/template/remotion/remotion.config.ts +5 -0
- package/template/remotion/src/DemoVideo.tsx +18 -0
- package/template/remotion/src/Root.tsx +47 -0
- package/template/remotion/src/Scene.tsx +131 -0
- package/template/remotion/src/edit/Cursor.tsx +83 -0
- package/template/remotion/src/edit/EditVideo.tsx +68 -0
- package/template/remotion/src/edit/data.ts +134 -0
- package/template/remotion/src/edit/mobile.events.json +11 -0
- package/template/remotion/src/edit/segments.tsx +268 -0
- package/template/remotion/src/edit/vo-manifest.json +38 -0
- package/template/remotion/src/edit/web.events.json +274 -0
- package/template/remotion/src/index.ts +4 -0
- package/template/remotion/src/scenes.ts +111 -0
- package/template/remotion/src/ui.tsx +161 -0
- package/template/remotion/tsconfig.json +13 -0
- package/template/setup.sh +29 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
AbsoluteFill,
|
|
4
|
+
Img,
|
|
5
|
+
staticFile,
|
|
6
|
+
interpolate,
|
|
7
|
+
useCurrentFrame,
|
|
8
|
+
spring,
|
|
9
|
+
useVideoConfig,
|
|
10
|
+
} from 'remotion';
|
|
11
|
+
import { loadFont } from '@remotion/google-fonts/Inter';
|
|
12
|
+
import { BRAND } from './scenes';
|
|
13
|
+
|
|
14
|
+
const { fontFamily } = loadFont();
|
|
15
|
+
export const FONT = fontFamily;
|
|
16
|
+
|
|
17
|
+
// Fade helper: fade in over `inF` frames, hold, fade out over last `outF`
|
|
18
|
+
export const useFade = (durF: number, inF = 12, outF = 12) => {
|
|
19
|
+
const frame = useCurrentFrame();
|
|
20
|
+
return interpolate(frame, [0, inF, durF - outF, durF], [0, 1, 1, 0], {
|
|
21
|
+
extrapolateLeft: 'clamp',
|
|
22
|
+
extrapolateRight: 'clamp',
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const Vignette: React.FC = () => (
|
|
27
|
+
<AbsoluteFill
|
|
28
|
+
style={{
|
|
29
|
+
background:
|
|
30
|
+
'radial-gradient(ellipse at center, rgba(0,0,0,0) 55%, rgba(0,0,0,0.45) 100%)',
|
|
31
|
+
pointerEvents: 'none',
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const LabelChip: React.FC<{ text: string }> = ({ text }) => {
|
|
37
|
+
const frame = useCurrentFrame();
|
|
38
|
+
const x = interpolate(frame, [4, 18], [-30, 0], {
|
|
39
|
+
extrapolateLeft: 'clamp',
|
|
40
|
+
extrapolateRight: 'clamp',
|
|
41
|
+
});
|
|
42
|
+
const o = interpolate(frame, [4, 18], [0, 1], {
|
|
43
|
+
extrapolateLeft: 'clamp',
|
|
44
|
+
extrapolateRight: 'clamp',
|
|
45
|
+
});
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
style={{
|
|
49
|
+
position: 'absolute',
|
|
50
|
+
top: 54,
|
|
51
|
+
left: 64,
|
|
52
|
+
transform: `translateX(${x}px)`,
|
|
53
|
+
opacity: o,
|
|
54
|
+
display: 'flex',
|
|
55
|
+
alignItems: 'center',
|
|
56
|
+
gap: 12,
|
|
57
|
+
background: 'rgba(11,18,32,0.72)',
|
|
58
|
+
border: '1px solid rgba(255,255,255,0.10)',
|
|
59
|
+
backdropFilter: 'blur(6px)',
|
|
60
|
+
padding: '12px 20px',
|
|
61
|
+
borderRadius: 999,
|
|
62
|
+
fontFamily: FONT,
|
|
63
|
+
color: BRAND.text,
|
|
64
|
+
fontSize: 26,
|
|
65
|
+
fontWeight: 600,
|
|
66
|
+
letterSpacing: 0.3,
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<span
|
|
70
|
+
style={{
|
|
71
|
+
width: 12,
|
|
72
|
+
height: 12,
|
|
73
|
+
borderRadius: 99,
|
|
74
|
+
background: BRAND.red,
|
|
75
|
+
boxShadow: `0 0 14px ${BRAND.red}`,
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
{text}
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const Caption: React.FC<{ text: string }> = ({ text }) => {
|
|
84
|
+
const frame = useCurrentFrame();
|
|
85
|
+
const y = interpolate(frame, [8, 24], [26, 0], {
|
|
86
|
+
extrapolateLeft: 'clamp',
|
|
87
|
+
extrapolateRight: 'clamp',
|
|
88
|
+
});
|
|
89
|
+
const o = interpolate(frame, [8, 24], [0, 1], {
|
|
90
|
+
extrapolateLeft: 'clamp',
|
|
91
|
+
extrapolateRight: 'clamp',
|
|
92
|
+
});
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
style={{
|
|
96
|
+
position: 'absolute',
|
|
97
|
+
bottom: 70,
|
|
98
|
+
left: 0,
|
|
99
|
+
right: 0,
|
|
100
|
+
display: 'flex',
|
|
101
|
+
justifyContent: 'center',
|
|
102
|
+
transform: `translateY(${y}px)`,
|
|
103
|
+
opacity: o,
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<div
|
|
107
|
+
style={{
|
|
108
|
+
maxWidth: 1400,
|
|
109
|
+
textAlign: 'center',
|
|
110
|
+
background: 'linear-gradient(180deg, rgba(11,18,32,0.78), rgba(7,11,20,0.86))',
|
|
111
|
+
border: '1px solid rgba(255,255,255,0.10)',
|
|
112
|
+
borderRadius: 18,
|
|
113
|
+
padding: '18px 38px',
|
|
114
|
+
fontFamily: FONT,
|
|
115
|
+
color: BRAND.text,
|
|
116
|
+
fontSize: 38,
|
|
117
|
+
fontWeight: 600,
|
|
118
|
+
boxShadow: '0 18px 50px rgba(0,0,0,0.45)',
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
{text}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const TitleCard: React.FC<{ title: string; subtitle: string }> = ({ title, subtitle }) => {
|
|
128
|
+
const frame = useCurrentFrame();
|
|
129
|
+
const { fps } = useVideoConfig();
|
|
130
|
+
const s = spring({ frame, fps, config: { damping: 200 } });
|
|
131
|
+
const scale = interpolate(s, [0, 1], [0.92, 1]);
|
|
132
|
+
const o = interpolate(frame, [0, 18], [0, 1], { extrapolateRight: 'clamp' });
|
|
133
|
+
return (
|
|
134
|
+
<AbsoluteFill
|
|
135
|
+
style={{
|
|
136
|
+
background: `radial-gradient(1200px 700px at 50% 38%, ${BRAND.panel} 0%, ${BRAND.dark} 55%, ${BRAND.darker} 100%)`,
|
|
137
|
+
justifyContent: 'center',
|
|
138
|
+
alignItems: 'center',
|
|
139
|
+
fontFamily: FONT,
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
<div style={{ transform: `scale(${scale})`, opacity: o, textAlign: 'center' }}>
|
|
143
|
+
<Img
|
|
144
|
+
src={staticFile('brand/logo-white.png')}
|
|
145
|
+
style={{ width: 640, height: 'auto' }}
|
|
146
|
+
/>
|
|
147
|
+
<div
|
|
148
|
+
style={{
|
|
149
|
+
marginTop: 30,
|
|
150
|
+
fontSize: 40,
|
|
151
|
+
fontWeight: 500,
|
|
152
|
+
color: BRAND.sub,
|
|
153
|
+
letterSpacing: 1,
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
{subtitle}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</AbsoluteFill>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# One-time setup for the demo-pipeline on a fresh machine.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
cd "$(dirname "$0")"
|
|
5
|
+
|
|
6
|
+
echo "[1/5] uv (Python toolchain)"
|
|
7
|
+
command -v uv >/dev/null || brew install uv
|
|
8
|
+
|
|
9
|
+
echo "[2/5] Python venv + capture/VO deps"
|
|
10
|
+
uv venv --python 3.12 .venv >/dev/null
|
|
11
|
+
uv pip install --python .venv playwright soundfile kokoro-onnx numpy >/dev/null
|
|
12
|
+
|
|
13
|
+
echo "[3/5] Playwright Chromium (records the web)"
|
|
14
|
+
.venv/bin/python -m playwright install chromium >/dev/null
|
|
15
|
+
|
|
16
|
+
echo "[4/5] Kokoro voice model (~310MB, once)"
|
|
17
|
+
mkdir -p models
|
|
18
|
+
BASE=https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0
|
|
19
|
+
[ -f models/kokoro.onnx ] || curl -fsSL -o models/kokoro.onnx "$BASE/kokoro-v1.0.onnx"
|
|
20
|
+
[ -f models/voices.bin ] || curl -fsSL -o models/voices.bin "$BASE/voices-v1.0.bin"
|
|
21
|
+
|
|
22
|
+
echo "[5/5] Remotion deps (the editor)"
|
|
23
|
+
( cd remotion && pnpm install >/dev/null )
|
|
24
|
+
|
|
25
|
+
command -v maestro >/dev/null || echo " (mobile only) install Maestro: curl -fsSL https://get.maestro.mobile.dev | bash"
|
|
26
|
+
echo ""
|
|
27
|
+
echo "✅ demo-pipeline ready."
|
|
28
|
+
echo " Run a full demo: pnpm --dir tools/demo-pipeline demo:all"
|
|
29
|
+
echo " Or in Claude: /demo-video"
|