From 4138bea9fa67bf4c4b87786608c61168195f318b Mon Sep 17 00:00:00 2001 From: kang Date: Tue, 12 May 2026 17:01:09 +0800 Subject: [PATCH] auto-save 2026-05-12 17:00 (~3) --- .memory/worklog.json | 7 ++++++ web/app/page.tsx | 40 +++++++++++++++++++--------------- web/components/nodes/index.tsx | 4 ++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 4af06c6..e667b33 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -97,6 +97,13 @@ "message": "auto-save 2026-05-12 16:49 (~3)", "hash": "4779c26", "files_changed": 3 + }, + { + "ts": "2026-05-12T16:55:37+08:00", + "type": "commit", + "message": "auto-save 2026-05-12 16:55 (~4)", + "hash": "345391d", + "files_changed": 4 } ] } diff --git a/web/app/page.tsx b/web/app/page.tsx index ffdc2ee..d52daa8 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -13,7 +13,7 @@ import { type NodeData, } from "@/components/nodes" import { ThemeToggle } from "@/components/theme-toggle" -import { createJob, getJob, triggerTranscribe, uploadJob, type Job } from "@/lib/api" +import { analyzeJob, createJob, getJob, uploadJob, type Job } from "@/lib/api" const NODE_TYPES = { input: InputNode, @@ -62,14 +62,13 @@ export default function Home() { const { resolvedTheme } = useTheme() const [job, setJob] = useState(null) const [submitting, setSubmitting] = useState(false) + const [analyzing, setAnalyzing] = useState(false) const [selectedFrames, setSelectedFrames] = useState>(new Set()) const pollRef = useRef | null>(null) - const transcribeTriggeredRef = useRef(null) const handleSubmit = useCallback(async (url: string) => { setSubmitting(true) setSelectedFrames(new Set()) - transcribeTriggeredRef.current = null try { const created = await createJob(url) setJob(created) @@ -84,7 +83,6 @@ export default function Home() { const handleUpload = useCallback(async (file: File) => { setSubmitting(true) setSelectedFrames(new Set()) - transcribeTriggeredRef.current = null try { toast.info(`上传中:${file.name} (${(file.size / 1024 / 1024).toFixed(1)} MB)`) const created = await uploadJob(file) @@ -97,19 +95,34 @@ export default function Home() { } }, []) + const handleAnalyze = useCallback(async () => { + if (!job) return + setAnalyzing(true) + setSelectedFrames(new Set()) + try { + await analyzeJob(job.id, 5) + toast.info("开始解析:拆轨 → 抽帧 → ASR → 翻译") + } catch (e) { + toast.error("解析触发失败:" + (e instanceof Error ? e.message : String(e))) + } finally { + setAnalyzing(false) + } + }, [job?.id]) + const handleToggleFrame = useCallback((idx: number) => { setSelectedFrames((prev) => { const next = new Set(prev) if (next.has(idx)) next.delete(idx) - else if (next.size < 10) next.add(idx) + else next.add(idx) return next }) }, []) - // 轮询 Job + // 轮询 Job(downloaded / transcribed / failed 三态停止) useEffect(() => { if (!job) return - if (job.status === "transcribed" || job.status === "failed") { + const TERMINAL: Job["status"][] = ["downloaded", "transcribed", "failed"] + if (TERMINAL.includes(job.status)) { if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null } return } @@ -122,23 +135,16 @@ export default function Home() { return () => { if (pollRef.current) clearInterval(pollRef.current) } }, [job?.id, job?.status]) - // 抽帧完后自动触发 transcribe - useEffect(() => { - if (!job) return - if (job.status !== "frames_extracted") return - if (transcribeTriggeredRef.current === job.id) return - transcribeTriggeredRef.current = job.id - triggerTranscribe(job.id).catch((e) => toast.error("启动转录失败:" + e.message)) - }, [job?.id, job?.status]) - const nodeData: NodeData = useMemo(() => ({ job, submitting, + analyzing, selectedFrames, onSubmitUrl: handleSubmit, onUploadFile: handleUpload, + onAnalyze: handleAnalyze, onToggleFrame: handleToggleFrame, - }), [job, submitting, selectedFrames, handleSubmit, handleUpload, handleToggleFrame]) + }), [job, submitting, analyzing, selectedFrames, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame]) // 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag) const [nodes, setNodes, onNodesChange] = useNodesState( diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index 9d54fd7..83edd8f 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -221,7 +221,7 @@ export function KeyframeNode({ data, selected }: any) { type="ai" status={st} icon={} title="关键帧 · Keyframes" - subtitle={`STEP 4 · ${d.selectedFrames.size}/10`} + subtitle={`STEP 4 · ${d.selectedFrames.size}/${d.job?.frames.length || 5}`} width={360} selected={selected} > @@ -253,7 +253,7 @@ export function KeyframeNode({ data, selected }: any) { })} ) : ( -
等待视频流,自动 + 手动抽取 ≤10 张
+
等待解析后抽取(默认 5 张)
)} )