auto-save 2026-05-14 04:32 (~5)
This commit is contained in:
@@ -42,6 +42,7 @@ const FRAME_TARGET_LABELS: Record<FrameExtractTarget, string> = {
|
||||
motion: "动作峰值",
|
||||
}
|
||||
const FRAME_QUALITY_LABELS: Record<FrameExtractQuality, string> = {
|
||||
auto: "自动",
|
||||
fast: "快速",
|
||||
accurate: "精细",
|
||||
ultra: "极准",
|
||||
@@ -178,7 +179,7 @@ export default function Home() {
|
||||
if (!targetJob) return
|
||||
const frameTarget = frameTargets[jobId] ?? "balanced"
|
||||
const frameCount = frameCounts[jobId] ?? 5
|
||||
const frameQuality = frameQualities[jobId] ?? "ultra"
|
||||
const frameQuality = frameQualities[jobId] ?? "auto"
|
||||
const mode = options?.mode ?? (targetJob.frames.length > 0 ? "append" : "replace")
|
||||
setActiveJobId(jobId)
|
||||
setAnalyzing(true)
|
||||
@@ -497,30 +498,43 @@ export default function Home() {
|
||||
})
|
||||
}, [job?.id, job?.frames])
|
||||
|
||||
// 轮询 Job(downloaded / transcribed / failed 三态停止)
|
||||
// 轮询 Job:任一视频在下载 / 抽帧 / 生视频时都继续轮询,支持多个抽帧任务排队。
|
||||
const prevStatusRef = useRef<string | null>(null)
|
||||
useEffect(() => {
|
||||
if (!job) return
|
||||
if (jobs.length === 0) return
|
||||
// 状态切到 downloaded 时提示用户点解析(仅一次)
|
||||
if (job.status === "downloaded" && prevStatusRef.current !== "downloaded") {
|
||||
if (job?.status === "downloaded" && prevStatusRef.current !== "downloaded") {
|
||||
toast.info("📥 视频已就绪 — 请点 Input 节点里的「点这里开始解析」按钮", { duration: 6000 })
|
||||
}
|
||||
prevStatusRef.current = job.status
|
||||
prevStatusRef.current = job?.status ?? null
|
||||
|
||||
const runningVideo = !!job.generated_videos?.some((v) => v.status === "queued" || v.status === "in_progress")
|
||||
const TERMINAL: Job["status"][] = ["downloaded", "frames_extracted", "transcribed", "failed"]
|
||||
if (TERMINAL.includes(job.status) && !runningVideo) {
|
||||
const runningIds = jobs
|
||||
.filter((item) => {
|
||||
const runningVideo = !!item.generated_videos?.some((v) => v.status === "queued" || v.status === "in_progress")
|
||||
return runningVideo || !TERMINAL.includes(item.status)
|
||||
})
|
||||
.map((item) => item.id)
|
||||
|
||||
if (runningIds.length === 0) {
|
||||
if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null }
|
||||
return
|
||||
}
|
||||
pollRef.current = setInterval(async () => {
|
||||
try {
|
||||
const latest = await getJob(job.id)
|
||||
setJob(latest)
|
||||
const latestJobs = await Promise.all(runningIds.map((id) => getJob(id).catch(() => null)))
|
||||
const byId = new Map(latestJobs.filter((item): item is Job => !!item).map((item) => [item.id, item]))
|
||||
if (byId.size > 0) {
|
||||
setJobs((prev) => prev.map((item) => byId.get(item.id) ?? item))
|
||||
}
|
||||
} catch { /* silent */ }
|
||||
}, 1500)
|
||||
return () => { if (pollRef.current) clearInterval(pollRef.current) }
|
||||
}, [job?.id, job?.status, job?.generated_videos?.map((v) => `${v.id}:${v.status}:${v.progress}`).join("|")])
|
||||
}, [
|
||||
job?.id,
|
||||
job?.status,
|
||||
jobs.map((item) => `${item.id}:${item.status}:${item.progress}:${item.generated_videos?.map((v) => `${v.id}:${v.status}:${v.progress}`).join(",")}`).join("|"),
|
||||
])
|
||||
|
||||
const [pinnedNodes, setPinnedNodes] = useState<Set<string>>(() => new Set(loadNodePins()))
|
||||
const handleToggleNodePin = useCallback((id: string) => {
|
||||
|
||||
Reference in New Issue
Block a user