"use client"; import { Suspense, useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { ArrowLeft, Loader2 } from "lucide-react"; import type { Project, RenderProgress } from "@/types"; import { ExportProgress } from "@/components/export-progress"; import { VideoPreview } from "@/components/video-preview"; import { DEFAULT_ANIMATION, DEFAULT_LAYOUT, DEFAULT_STYLE, } from "@/lib/defaults"; function ExportContent() { const searchParams = useSearchParams(); const router = useRouter(); const id = searchParams.get("id"); const [project, setProject] = useState(null); const [loading, setLoading] = useState(true); const [rendering, setRendering] = useState(false); const [progress, setProgress] = useState(null); const [downloadUrl, setDownloadUrl] = useState(); const [filename, setFilename] = useState(); useEffect(() => { if (!id) { router.push("/"); return; } void (async () => { const res = await fetch(`/api/project?id=${id}`); const data = await res.json(); if (!res.ok) { router.push("/"); return; } setProject(data); setLoading(false); })(); }, [id, router]); const pollProgress = useCallback(async () => { if (!id) return; const res = await fetch(`/api/progress?id=${id}`); const data = (await res.json()) as RenderProgress; setProgress(data); if (data.stage === "done" || data.stage === "error") { setRendering(false); } }, [id]); const startRender = useCallback(async () => { if (!project || !id) return; setRendering(true); setProgress({ percent: 0, stage: "extracting", message: "Starting..." }); const pollInterval = setInterval(() => void pollProgress(), 1000); try { const res = await fetch("/api/render", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, captions: project.captions, style: project.style, layout: project.layout, animation: project.animation, }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); await new Promise((resolve, reject) => { const waitForDone = setInterval(async () => { const progressRes = await fetch(`/api/progress?id=${id}`); const progressData = (await progressRes.json()) as RenderProgress; setProgress(progressData); if (progressData.stage === "done") { clearInterval(waitForDone); setDownloadUrl(`/api/download/${id}`); setFilename(`captioned_${id}.mp4`); resolve(); } else if (progressData.stage === "error") { clearInterval(waitForDone); reject(new Error(progressData.message)); } }, 1000); }); } catch (error) { setProgress({ percent: 0, stage: "error", message: error instanceof Error ? error.message : "Render failed", }); } finally { clearInterval(pollInterval); setRendering(false); } }, [project, id, pollProgress]); if (loading || !project || !id) { return (
); } return (
Edit captions

Export Video

void startRender()} />
); } export default function ExportPage() { return ( }> ); }