auto-save 2026-05-14 04:04 (~6)

This commit is contained in:
2026-05-14 04:04:54 +08:00
parent b95706a3e4
commit 87f1182afe
6 changed files with 171 additions and 65 deletions

View File

@@ -19,7 +19,7 @@ import { ThemeToggle } from "@/components/theme-toggle"
import {
addManualFrame, analyzeJob, createJob, getJob, listJobs, uploadJob, deleteJob, deleteFrame, deleteGeneratedImage,
deleteGeneratedVideo, deleteCutout, generateStoryboardVideo,
type Job, type ImageRef, type StoryboardScene, type FrameExtractTarget,
type Job, type ImageRef, type StoryboardScene, type FrameExtractMode, type FrameExtractTarget,
} from "@/lib/api"
const NODE_TYPES = {
@@ -93,6 +93,7 @@ export default function Home() {
const [submitting, setSubmitting] = useState(false)
const [analyzing, setAnalyzing] = useState(false)
const [frameTarget, setFrameTarget] = useState<FrameExtractTarget>("balanced")
const [frameCount, setFrameCount] = useState(5)
const [selectedFrames, setSelectedFrames] = useState<Set<number>>(new Set())
const [expandedFrame, setExpandedFrame] = useState<number | null>(null)
const [framePanelScale, setFramePanelScale] = useState(1)
@@ -166,21 +167,27 @@ export default function Home() {
}
}, [addJob])
const handleAnalyze = useCallback(async () => {
const handleAnalyze = useCallback(async (options?: { mode?: FrameExtractMode }) => {
if (!job) return
const mode = options?.mode ?? (job.frames.length > 0 ? "append" : "replace")
setAnalyzing(true)
setSelectedFrames(new Set())
if (mode === "replace") setSelectedFrames(new Set())
try {
await analyzeJob(job.id, 5, frameTarget)
toast.info(`开始解析:拆轨 → ${FRAME_TARGET_LABELS[frameTarget]}抽帧。声音文案轨单独处理`)
await analyzeJob(job.id, frameCount, frameTarget, mode)
toast.info(`${mode === "append" ? "追加抽帧" : "开始解析"}${FRAME_TARGET_LABELS[frameTarget]} · ${frameCount}`)
// 乐观更新本地状态,让轮询 useEffect 重新启动
setJob((prev) => prev ? { ...prev, status: "splitting", message: `拆轨中 · ${FRAME_TARGET_LABELS[frameTarget]}`, progress: 30 } : prev)
setJob((prev) => prev ? {
...prev,
status: "splitting",
message: `${mode === "append" ? "追加抽帧中" : "拆轨中"} · ${FRAME_TARGET_LABELS[frameTarget]}`,
progress: 30,
} : prev)
} catch (e) {
toast.error("解析触发失败:" + (e instanceof Error ? e.message : String(e)))
} finally {
setAnalyzing(false)
}
}, [job?.id, frameTarget])
}, [job?.id, job?.frames.length, frameCount, frameTarget])
const handleAddManualFrameForJob = useCallback(async (jobId: string, t: number) => {
try {
@@ -505,6 +512,7 @@ export default function Home() {
submitting,
analyzing,
frameTarget,
frameCount,
selectedFrames,
expandedFrame,
framePanelScale,
@@ -517,6 +525,7 @@ export default function Home() {
onUploadFile: handleUpload,
onAnalyze: handleAnalyze,
onFrameTargetChange: setFrameTarget,
onFrameCountChange: setFrameCount,
onToggleFrame: handleToggleFrame,
onExpandFrame: setExpandedFrame,
onOpenFramePanel: handleOpenFramePanel,
@@ -546,7 +555,7 @@ export default function Home() {
onCopyImage: handleCopyImage,
pinnedNodes,
onToggleNodePin: handleToggleNodePin,
}), [job, jobs, activeJobId, submitting, analyzing, frameTarget, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, setJob, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, handleCopyImage, pinnedNodes, handleToggleNodePin])
}), [job, jobs, activeJobId, submitting, analyzing, frameTarget, frameCount, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleUpload, handleAnalyze, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, setJob, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, handleCopyImage, pinnedNodes, handleToggleNodePin])
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag
const savedSizes = useMemo(() => loadNodeSizes(), [])