auto-save 2026-05-12 17:00 (~3)

This commit is contained in:
2026-05-12 17:01:09 +08:00
parent 345391d005
commit 4138bea9fa
3 changed files with 32 additions and 19 deletions

View File

@@ -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
}
]
}

View File

@@ -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<Job | null>(null)
const [submitting, setSubmitting] = useState(false)
const [analyzing, setAnalyzing] = useState(false)
const [selectedFrames, setSelectedFrames] = useState<Set<number>>(new Set())
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)
const transcribeTriggeredRef = useRef<string | null>(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
// 轮询 Jobdownloaded / 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<Node>(

View File

@@ -221,7 +221,7 @@ export function KeyframeNode({ data, selected }: any) {
type="ai" status={st}
icon={<ImageIcon className="h-4 w-4" />}
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) {
})}
</div>
) : (
<div className="text-[11.5px] text-[var(--text-faint)] py-2"> + 10 </div>
<div className="text-[11.5px] text-[var(--text-faint)] py-2"> 5 </div>
)}
</NodeShell>
)