Spaces:
Runtime error
Runtime error
feat: add interactive HF demo clips
Browse files- .gitattributes +3 -0
- .gitignore +2 -0
- frontend/app/editor/page.tsx +41 -31
- frontend/app/page.tsx +36 -49
- frontend/next-env.d.ts +1 -1
- frontend/public/demo/elevenclip_demo_01.mp4 +3 -0
- frontend/public/demo/elevenclip_demo_02.mp4 +3 -0
- frontend/public/demo/elevenclip_demo_03.mp4 +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
frontend/public/demo/elevenclip_demo_01.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
frontend/public/demo/elevenclip_demo_02.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
frontend/public/demo/elevenclip_demo_03.mp4 filter=lfs diff=lfs merge=lfs -text
|
.gitignore
CHANGED
|
@@ -17,6 +17,8 @@ graphify-out/
|
|
| 17 |
/tmp/
|
| 18 |
*.wav
|
| 19 |
*.mp4
|
|
|
|
|
|
|
| 20 |
*.ass
|
| 21 |
*.log
|
| 22 |
|
|
|
|
| 17 |
/tmp/
|
| 18 |
*.wav
|
| 19 |
*.mp4
|
| 20 |
+
!frontend/public/demo/
|
| 21 |
+
!frontend/public/demo/*.mp4
|
| 22 |
*.ass
|
| 23 |
*.log
|
| 24 |
|
frontend/app/editor/page.tsx
CHANGED
|
@@ -9,7 +9,11 @@ import {
|
|
| 9 |
Heart, MessageCircle, Share2, Music,
|
| 10 |
} from "lucide-react";
|
| 11 |
|
| 12 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
// ─── Types ────────────────────────────────────────────────────────────────────
|
| 15 |
type SubEvent = { index: number; text: string; start: number; end: number };
|
|
@@ -49,6 +53,7 @@ const L = {
|
|
| 49 |
cut: "Cut",
|
| 50 |
demoSession: "Demo session", waitRender: "Waiting for render", close: "Close",
|
| 51 |
preview: "PREVIEW",
|
|
|
|
| 52 |
},
|
| 53 |
th: {
|
| 54 |
back: "กลับ", clips: "คลิป", clip: "คลิป",
|
|
@@ -73,6 +78,7 @@ const L = {
|
|
| 73 |
cut: "จุดตัด",
|
| 74 |
demoSession: "Demo session", waitRender: "รอการเรนเดอร์", close: "ปิด",
|
| 75 |
preview: "PREVIEW",
|
|
|
|
| 76 |
},
|
| 77 |
zh: {
|
| 78 |
back: "返回", clips: "片段", clip: "片段",
|
|
@@ -97,6 +103,7 @@ const L = {
|
|
| 97 |
cut: "切割",
|
| 98 |
demoSession: "演示会话", waitRender: "等待渲染", close: "关闭",
|
| 99 |
preview: "预览",
|
|
|
|
| 100 |
},
|
| 101 |
} as const;
|
| 102 |
type Lbl = typeof L[Lang];
|
|
@@ -104,48 +111,44 @@ type Lbl = typeof L[Lang];
|
|
| 104 |
// ─── Mock data ─────────────────────────────────────────────────────────────────
|
| 105 |
const MOCK_CLIPS: (ClipResult & { suggested_caption: string })[] = [
|
| 106 |
{
|
| 107 |
-
index: 0, start:
|
| 108 |
-
download_url:
|
| 109 |
-
highlight_reason: "
|
| 110 |
-
suggested_caption: "
|
| 111 |
},
|
| 112 |
{
|
| 113 |
-
index: 1, start:
|
| 114 |
-
download_url:
|
| 115 |
-
highlight_reason: "
|
| 116 |
-
suggested_caption: "
|
| 117 |
},
|
| 118 |
{
|
| 119 |
-
index: 2, start:
|
| 120 |
-
download_url:
|
| 121 |
-
highlight_reason: "
|
| 122 |
-
suggested_caption: "
|
| 123 |
},
|
| 124 |
];
|
| 125 |
|
| 126 |
const MOCK_SUBS: Record<number, SubEvent[]> = {
|
| 127 |
0: [
|
| 128 |
-
{ index: 0, text: "
|
| 129 |
-
{ index: 1, text: "
|
| 130 |
-
{ index: 2, text: "
|
| 131 |
-
{ index: 3, text: "
|
| 132 |
-
{ index: 4, text: "
|
| 133 |
-
{ index: 5, text: "This is the best clip of the stream", start: 55.0, end: 58.5 },
|
| 134 |
],
|
| 135 |
1: [
|
| 136 |
-
{ index: 0, text: "
|
| 137 |
-
{ index: 1, text: "
|
| 138 |
-
{ index: 2, text: "
|
| 139 |
-
{ index: 3, text: "
|
| 140 |
-
{ index: 4, text: "YESSS! FINALLY!", start: 54.0, end: 57.0 },
|
| 141 |
-
{ index: 5, text: "We are SO back", start: 57.5, end: 60.0 },
|
| 142 |
],
|
| 143 |
2: [
|
| 144 |
-
{ index: 0, text: "
|
| 145 |
-
{ index: 1, text: "
|
| 146 |
-
{ index: 2, text: "
|
| 147 |
-
{ index: 3, text: "
|
| 148 |
-
{ index: 4, text: "Clip it. Someone clip this.", start: 50.0, end: 53.0 },
|
| 149 |
],
|
| 150 |
};
|
| 151 |
|
|
@@ -469,7 +472,9 @@ function EditorContent() {
|
|
| 469 |
const clip = clips[activeClip];
|
| 470 |
const apiBase = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
| 471 |
const rawUrl = clip ? (downloadUrls[clip.index] ?? clip.download_url) : "";
|
| 472 |
-
const clipDownloadUrl =
|
|
|
|
|
|
|
| 473 |
const suggestedCaption = (clip as (typeof clip & { suggested_caption?: string }))?.suggested_caption;
|
| 474 |
const currentCuts = clip ? (cutRegions[clip.index] ?? []) : [];
|
| 475 |
const clipDuration = clip ? clip.duration + (trimEnd[clip.index] ?? 0) - (trimStart[clip.index] ?? 0) : 60;
|
|
@@ -511,6 +516,11 @@ function EditorContent() {
|
|
| 511 |
</span>
|
| 512 |
</div>
|
| 513 |
</nav>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
|
| 515 |
{/* ── Main layout ── */}
|
| 516 |
<div className="flex flex-1 min-h-0 overflow-hidden">
|
|
|
|
| 9 |
Heart, MessageCircle, Share2, Music,
|
| 10 |
} from "lucide-react";
|
| 11 |
|
| 12 |
+
const DEMO_CLIP_URLS = [
|
| 13 |
+
"/demo/elevenclip_demo_01.mp4",
|
| 14 |
+
"/demo/elevenclip_demo_02.mp4",
|
| 15 |
+
"/demo/elevenclip_demo_03.mp4",
|
| 16 |
+
];
|
| 17 |
|
| 18 |
// ─── Types ────────────────────────────────────────────────────────────────────
|
| 19 |
type SubEvent = { index: number; text: string; start: number; end: number };
|
|
|
|
| 53 |
cut: "Cut",
|
| 54 |
demoSession: "Demo session", waitRender: "Waiting for render", close: "Close",
|
| 55 |
preview: "PREVIEW",
|
| 56 |
+
demoNotice: "Simulation only — these are pre-rendered demo results, not a live GPU run.",
|
| 57 |
},
|
| 58 |
th: {
|
| 59 |
back: "กลับ", clips: "คลิป", clip: "คลิป",
|
|
|
|
| 78 |
cut: "จุดตัด",
|
| 79 |
demoSession: "Demo session", waitRender: "รอการเรนเดอร์", close: "ปิด",
|
| 80 |
preview: "PREVIEW",
|
| 81 |
+
demoNotice: "โหมดจำลองเท่านั้น — คลิปเหล่านี้เป็นผลลัพธ์ที่เตรียมไว้ ไม่ได้รัน GPU สด",
|
| 82 |
},
|
| 83 |
zh: {
|
| 84 |
back: "返回", clips: "片段", clip: "片段",
|
|
|
|
| 103 |
cut: "切割",
|
| 104 |
demoSession: "演示会话", waitRender: "等待渲染", close: "关闭",
|
| 105 |
preview: "预览",
|
| 106 |
+
demoNotice: "仅为模拟演示 — 这些是预渲染结果,并非实时 GPU 运行。",
|
| 107 |
},
|
| 108 |
} as const;
|
| 109 |
type Lbl = typeof L[Lang];
|
|
|
|
| 111 |
// ─── Mock data ─────────────────────────────────────────────────────────────────
|
| 112 |
const MOCK_CLIPS: (ClipResult & { suggested_caption: string })[] = [
|
| 113 |
{
|
| 114 |
+
index: 0, start: 13.0, end: 43.0, duration: 30.0, score: 0.462,
|
| 115 |
+
download_url: DEMO_CLIP_URLS[0], raw_url: DEMO_CLIP_URLS[0], ass_path: "demo_0.ass",
|
| 116 |
+
highlight_reason: "The speaker is enthusiastically discussing the benefits of the AMD Instinct MI350P, making it a compelling highlight for tech enthusiasts.",
|
| 117 |
+
suggested_caption: "AMD Instinct MI350P explained in 30 seconds: AI infrastructure, performance, and enterprise-ready acceleration.",
|
| 118 |
},
|
| 119 |
{
|
| 120 |
+
index: 1, start: 125.0, end: 155.0, duration: 30.0, score: 0.45,
|
| 121 |
+
download_url: DEMO_CLIP_URLS[1], raw_url: DEMO_CLIP_URLS[1], ass_path: "demo_1.ass",
|
| 122 |
+
highlight_reason: "The speaker is enthusiastically discussing the benefits of the AMD Instinct MI350P, making it engaging for viewers interested in technology and AI.",
|
| 123 |
+
suggested_caption: "A quick look at how AMD Instinct GPUs power modern AI and high-performance computing workloads.",
|
| 124 |
},
|
| 125 |
{
|
| 126 |
+
index: 2, start: 149.0, end: 179.0, duration: 30.0, score: 0.398,
|
| 127 |
+
download_url: DEMO_CLIP_URLS[2], raw_url: DEMO_CLIP_URLS[2], ass_path: "demo_2.ass",
|
| 128 |
+
highlight_reason: "The speaker is enthusiastically discussing the benefits of the AMD Instinct MI350P, making it engaging for viewers interested in enterprise computing and AI.",
|
| 129 |
+
suggested_caption: "Enterprise AI needs serious compute. This clip highlights where AMD Instinct fits in the stack.",
|
| 130 |
},
|
| 131 |
];
|
| 132 |
|
| 133 |
const MOCK_SUBS: Record<number, SubEvent[]> = {
|
| 134 |
0: [
|
| 135 |
+
{ index: 0, text: "AMD Instinct MI350P", start: 0.0, end: 2.8 },
|
| 136 |
+
{ index: 1, text: "Built for AI acceleration", start: 3.2, end: 5.8 },
|
| 137 |
+
{ index: 2, text: "Enterprise compute at scale", start: 7.0, end: 9.6 },
|
| 138 |
+
{ index: 3, text: "Vision, audio, and transcript signals combined", start: 12.0, end: 15.0 },
|
| 139 |
+
{ index: 4, text: "HRE keeps the important subject visible", start: 20.0, end: 23.4 },
|
|
|
|
| 140 |
],
|
| 141 |
1: [
|
| 142 |
+
{ index: 0, text: "The key benefit is throughput", start: 0.0, end: 2.8 },
|
| 143 |
+
{ index: 1, text: "AI workloads need memory and bandwidth", start: 3.4, end: 6.3 },
|
| 144 |
+
{ index: 2, text: "Qwen selected this as a strong technical moment", start: 8.0, end: 11.2 },
|
| 145 |
+
{ index: 3, text: "Captions can be edited in the demo", start: 18.0, end: 21.0 },
|
|
|
|
|
|
|
| 146 |
],
|
| 147 |
2: [
|
| 148 |
+
{ index: 0, text: "Enterprise AI infrastructure", start: 0.0, end: 2.7 },
|
| 149 |
+
{ index: 1, text: "The model chooses the most useful 30 seconds", start: 4.0, end: 7.2 },
|
| 150 |
+
{ index: 2, text: "Zoom and caption style vary by moment", start: 9.0, end: 12.0 },
|
| 151 |
+
{ index: 3, text: "This preview uses pre-rendered demo output", start: 20.0, end: 23.0 },
|
|
|
|
| 152 |
],
|
| 153 |
};
|
| 154 |
|
|
|
|
| 472 |
const clip = clips[activeClip];
|
| 473 |
const apiBase = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000";
|
| 474 |
const rawUrl = clip ? (downloadUrls[clip.index] ?? clip.download_url) : "";
|
| 475 |
+
const clipDownloadUrl = isDemo
|
| 476 |
+
? rawUrl
|
| 477 |
+
: rawUrl.startsWith("http") ? rawUrl : rawUrl ? `${apiBase}${rawUrl}` : "";
|
| 478 |
const suggestedCaption = (clip as (typeof clip & { suggested_caption?: string }))?.suggested_caption;
|
| 479 |
const currentCuts = clip ? (cutRegions[clip.index] ?? []) : [];
|
| 480 |
const clipDuration = clip ? clip.duration + (trimEnd[clip.index] ?? 0) - (trimStart[clip.index] ?? 0) : 60;
|
|
|
|
| 516 |
</span>
|
| 517 |
</div>
|
| 518 |
</nav>
|
| 519 |
+
{isDemo && (
|
| 520 |
+
<div className="border-b border-cyan-400/20 bg-cyan-400/10 px-5 py-2 text-center text-xs text-cyan-100">
|
| 521 |
+
{lbl.demoNotice}
|
| 522 |
+
</div>
|
| 523 |
+
)}
|
| 524 |
|
| 525 |
{/* ── Main layout ── */}
|
| 526 |
<div className="flex flex-1 min-h-0 overflow-hidden">
|
frontend/app/page.tsx
CHANGED
|
@@ -28,6 +28,7 @@ const T = {
|
|
| 28 |
generateHRE: "Generate with HRE",
|
| 29 |
tryDemo: "Try Demo",
|
| 30 |
demoHint: "Demo mode — works without backend",
|
|
|
|
| 31 |
accessRequired: "Enter the demo access code to run real GPU generation.",
|
| 32 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 33 |
},
|
|
@@ -44,6 +45,7 @@ const T = {
|
|
| 44 |
generateHRE: "สร้างด้วย HRE",
|
| 45 |
tryDemo: "ลองดูตัวอย่าง",
|
| 46 |
demoHint: "โหมด Demo — ไม่ต้องเชื่อม backend",
|
|
|
|
| 47 |
accessRequired: "ใส่รหัสเดโมก่อน เพื่อรันของจริงบน GPU",
|
| 48 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 49 |
},
|
|
@@ -60,6 +62,7 @@ const T = {
|
|
| 60 |
generateHRE: "HRE 生成",
|
| 61 |
tryDemo: "试用演示",
|
| 62 |
demoHint: "演示模式 — 无需后端连接",
|
|
|
|
| 63 |
accessRequired: "请输入演示访问码以运行真实 GPU 生成。",
|
| 64 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 65 |
},
|
|
@@ -81,14 +84,14 @@ const FONT_MAP: Record<string, string> = {
|
|
| 81 |
};
|
| 82 |
|
| 83 |
const DEMO_STAGES = [
|
| 84 |
-
{ stage: "download", pct: 10, message: "
|
| 85 |
-
{ stage: "audio", pct: 22, message: "
|
| 86 |
-
{ stage: "scenes", pct: 35, message: "
|
| 87 |
-
{ stage: "transcribe",pct: 50, message: "Whisper
|
| 88 |
-
{ stage: "vision", pct: 65, message: "Qwen2.5-VL
|
| 89 |
-
{ stage: "scoring", pct: 80, message: "
|
| 90 |
-
{ stage: "cutting", pct: 90, message: "
|
| 91 |
-
{ stage: "subtitles", pct: 96, message: "
|
| 92 |
{ stage: "done", pct: 100, message: "" },
|
| 93 |
];
|
| 94 |
|
|
@@ -116,6 +119,7 @@ export default function HomePage() {
|
|
| 116 |
|
| 117 |
const [step, setStep] = useState<Step>(1);
|
| 118 |
const [videoFile, setVideoFile] = useState<File | null>(null);
|
|
|
|
| 119 |
const [channelDesc, setChannelDesc] = useState("");
|
| 120 |
|
| 121 |
const [clipSettings, setClipSettings] = useState({
|
|
@@ -153,7 +157,7 @@ export default function HomePage() {
|
|
| 153 |
const wsRef = useRef<WebSocket | null>(null);
|
| 154 |
const demoTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
| 155 |
|
| 156 |
-
const canProceedStep1 = !!videoFile;
|
| 157 |
|
| 158 |
useEffect(() => {
|
| 159 |
const font = FONT_MAP[clipSettings.subtitle_language] ?? "Noto Sans";
|
|
@@ -183,6 +187,10 @@ export default function HomePage() {
|
|
| 183 |
const handleGenerate = async () => {
|
| 184 |
if (!canProceedStep1) return;
|
| 185 |
setStep("generating");
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
if (DEMO_ONLY_PUBLIC && !accessCode.trim()) {
|
| 187 |
setProgress({ stage: "error", pct: 0, message: t.accessRequired });
|
| 188 |
return;
|
|
@@ -226,45 +234,18 @@ export default function HomePage() {
|
|
| 226 |
};
|
| 227 |
|
| 228 |
const handleDemo = async () => {
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
clip_language: "auto",
|
| 242 |
-
subtitle_language: "english",
|
| 243 |
-
mode: "hre",
|
| 244 |
-
style_config: {},
|
| 245 |
-
};
|
| 246 |
-
const sessionId = await startProcessing(settings, undefined, accessCode);
|
| 247 |
-
localStorage.setItem("elevnclip_session", sessionId);
|
| 248 |
-
|
| 249 |
-
const ws = connectProgressWS(sessionId, (data) => {
|
| 250 |
-
setProgress(data);
|
| 251 |
-
if (data.stage === "done") { ws.close(); router.push(`/editor?session=${sessionId}`); }
|
| 252 |
-
if (data.stage === "error") ws.close();
|
| 253 |
-
});
|
| 254 |
-
wsRef.current = ws;
|
| 255 |
-
|
| 256 |
-
const poll = setInterval(async () => {
|
| 257 |
-
try {
|
| 258 |
-
const data = await getClips(sessionId);
|
| 259 |
-
if (data.last_progress) setProgress(data.last_progress);
|
| 260 |
-
if (data.status === "done") { clearInterval(poll); router.push(`/editor?session=${sessionId}`); }
|
| 261 |
-
if (data.status === "error") clearInterval(poll);
|
| 262 |
-
} catch { /* ignore */ }
|
| 263 |
-
}, 3000);
|
| 264 |
-
setTimeout(() => clearInterval(poll), 600_000);
|
| 265 |
-
} catch {
|
| 266 |
-
runMockDemo();
|
| 267 |
-
}
|
| 268 |
};
|
| 269 |
|
| 270 |
return (
|
|
@@ -330,11 +311,17 @@ export default function HomePage() {
|
|
| 330 |
)}
|
| 331 |
|
| 332 |
<div className="bg-white/5 border border-white/10 rounded-2xl p-5 backdrop-blur-sm flex-1 min-h-0 overflow-y-auto flex flex-col">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
{step === 1 && (
|
| 334 |
<>
|
| 335 |
<h2 className="text-lg font-semibold mb-4">{t.addVideo}</h2>
|
| 336 |
<VideoUpload
|
| 337 |
-
onFileSelect={(f) => setVideoFile(f)}
|
| 338 |
onChannelDesc={setChannelDesc}
|
| 339 |
channelDesc={channelDesc}
|
| 340 |
accessCode={accessCode}
|
|
|
|
| 28 |
generateHRE: "Generate with HRE",
|
| 29 |
tryDemo: "Try Demo",
|
| 30 |
demoHint: "Demo mode — works without backend",
|
| 31 |
+
demoNotice: "Demo simulation: you can change settings and walk through the real flow, but generation uses pre-rendered ElevenClip AI results.",
|
| 32 |
accessRequired: "Enter the demo access code to run real GPU generation.",
|
| 33 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 34 |
},
|
|
|
|
| 45 |
generateHRE: "สร้างด้วย HRE",
|
| 46 |
tryDemo: "ลองดูตัวอย่าง",
|
| 47 |
demoHint: "โหมด Demo — ไม่ต้องเชื่อม backend",
|
| 48 |
+
demoNotice: "โหมดจำลอง: ปรับตั้งค่าและลองกดตาม flow จริงได้ แต่ผลลัพธ์ใช้คลิป ElevenClip AI ที่เราสร้างไว้แล้ว",
|
| 49 |
accessRequired: "ใส่รหัสเดโมก่อน เพื่อรันของจริงบน GPU",
|
| 50 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 51 |
},
|
|
|
|
| 62 |
generateHRE: "HRE 生成",
|
| 63 |
tryDemo: "试用演示",
|
| 64 |
demoHint: "演示模式 — 无需后端连接",
|
| 65 |
+
demoNotice: "模拟演示:可以调整设置并体验真实流程,但生成结果使用预先渲染的 ElevenClip AI 片段。",
|
| 66 |
accessRequired: "请输入演示访问码以运行真实 GPU 生成。",
|
| 67 |
hackathon: "AMD Developer Hackathon 2026 · Track 3: Vision & Multimodal AI",
|
| 68 |
},
|
|
|
|
| 84 |
};
|
| 85 |
|
| 86 |
const DEMO_STAGES = [
|
| 87 |
+
{ stage: "download", pct: 10, message: "Demo simulation — loading the AMD Instinct source video..." },
|
| 88 |
+
{ stage: "audio", pct: 22, message: "Simulating audio extraction and speech alignment..." },
|
| 89 |
+
{ stage: "scenes", pct: 35, message: "Simulating scene detection — strongest moments selected" },
|
| 90 |
+
{ stage: "transcribe",pct: 50, message: "Simulating Whisper transcript timing..." },
|
| 91 |
+
{ stage: "vision", pct: 65, message: "Simulating Qwen2.5-VL multimodal analysis..." },
|
| 92 |
+
{ stage: "scoring", pct: 80, message: "Simulating highlight scoring and HRE edit planning..." },
|
| 93 |
+
{ stage: "cutting", pct: 90, message: "Loading 3 pre-rendered ElevenClip AI demo clips..." },
|
| 94 |
+
{ stage: "subtitles", pct: 96, message: "Preparing interactive editor preview..." },
|
| 95 |
{ stage: "done", pct: 100, message: "" },
|
| 96 |
];
|
| 97 |
|
|
|
|
| 119 |
|
| 120 |
const [step, setStep] = useState<Step>(1);
|
| 121 |
const [videoFile, setVideoFile] = useState<File | null>(null);
|
| 122 |
+
const [demoMode, setDemoMode] = useState(false);
|
| 123 |
const [channelDesc, setChannelDesc] = useState("");
|
| 124 |
|
| 125 |
const [clipSettings, setClipSettings] = useState({
|
|
|
|
| 157 |
const wsRef = useRef<WebSocket | null>(null);
|
| 158 |
const demoTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
| 159 |
|
| 160 |
+
const canProceedStep1 = demoMode || !!videoFile;
|
| 161 |
|
| 162 |
useEffect(() => {
|
| 163 |
const font = FONT_MAP[clipSettings.subtitle_language] ?? "Noto Sans";
|
|
|
|
| 187 |
const handleGenerate = async () => {
|
| 188 |
if (!canProceedStep1) return;
|
| 189 |
setStep("generating");
|
| 190 |
+
if (demoMode) {
|
| 191 |
+
runMockDemo();
|
| 192 |
+
return;
|
| 193 |
+
}
|
| 194 |
if (DEMO_ONLY_PUBLIC && !accessCode.trim()) {
|
| 195 |
setProgress({ stage: "error", pct: 0, message: t.accessRequired });
|
| 196 |
return;
|
|
|
|
| 234 |
};
|
| 235 |
|
| 236 |
const handleDemo = async () => {
|
| 237 |
+
setDemoMode(true);
|
| 238 |
+
setVideoFile(null);
|
| 239 |
+
setChannelDesc("AMD Instinct product video for AI, HPC, and enterprise computing audiences");
|
| 240 |
+
setClipSettings({
|
| 241 |
+
clip_style: "educational",
|
| 242 |
+
target_duration: 30,
|
| 243 |
+
clip_count: 3,
|
| 244 |
+
clip_language: "auto",
|
| 245 |
+
subtitle_language: "english",
|
| 246 |
+
mode: "hre",
|
| 247 |
+
});
|
| 248 |
+
setStep(2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
};
|
| 250 |
|
| 251 |
return (
|
|
|
|
| 311 |
)}
|
| 312 |
|
| 313 |
<div className="bg-white/5 border border-white/10 rounded-2xl p-5 backdrop-blur-sm flex-1 min-h-0 overflow-y-auto flex flex-col">
|
| 314 |
+
{demoMode && step !== "generating" && (
|
| 315 |
+
<div className="mb-4 rounded-xl border border-cyan-400/25 bg-cyan-400/10 px-4 py-3 text-sm text-cyan-100">
|
| 316 |
+
{t.demoNotice}
|
| 317 |
+
</div>
|
| 318 |
+
)}
|
| 319 |
+
|
| 320 |
{step === 1 && (
|
| 321 |
<>
|
| 322 |
<h2 className="text-lg font-semibold mb-4">{t.addVideo}</h2>
|
| 323 |
<VideoUpload
|
| 324 |
+
onFileSelect={(f) => { setDemoMode(false); setVideoFile(f); }}
|
| 325 |
onChannelDesc={setChannelDesc}
|
| 326 |
channelDesc={channelDesc}
|
| 327 |
accessCode={accessCode}
|
frontend/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
/// <reference types="next" />
|
| 2 |
/// <reference types="next/image-types/global" />
|
| 3 |
-
import "./.next/
|
| 4 |
|
| 5 |
// NOTE: This file should not be edited
|
| 6 |
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
|
|
| 1 |
/// <reference types="next" />
|
| 2 |
/// <reference types="next/image-types/global" />
|
| 3 |
+
import "./.next/types/routes.d.ts";
|
| 4 |
|
| 5 |
// NOTE: This file should not be edited
|
| 6 |
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
frontend/public/demo/elevenclip_demo_01.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:eef8a018ca69c935728a6752288dce0d5a44aa255237728e1d22a45af09d383a
|
| 3 |
+
size 13201340
|
frontend/public/demo/elevenclip_demo_02.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cbbcf2d951ed4d058281812ca82843ddd35a35c03cbb08c9911ee86ce345b78b
|
| 3 |
+
size 12197380
|
frontend/public/demo/elevenclip_demo_03.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:584a72575f255953d27c18db1723c0e3b53b309908be715db6a9cb1c9ae63b78
|
| 3 |
+
size 11542882
|