auto-save 2026-05-17 13:13 (~6)

This commit is contained in:
2026-05-17 13:13:05 +08:00
parent dab3e023f7
commit 253e82a322
6 changed files with 130 additions and 49 deletions

View File

@@ -15,7 +15,6 @@ import {
type NodeData,
} from "@/components/nodes"
import { ThemeToggle } from "@/components/theme-toggle"
import { AudioStrip } from "@/components/audio-strip"
import { AdRecreationBoard } from "@/components/ad-recreation-board"
import {
addManualFrame, analyzeJob, createJob, getJob, listJobs, uploadJob, deleteJob, deleteFrame, deleteGeneratedImage,
@@ -130,12 +129,9 @@ const EDGES_RAW: Array<[string, string]> = [
export default function Home() {
const { resolvedTheme } = useTheme()
const [clientReady, setClientReady] = useState(false)
const [jobs, setJobs] = useState<Job[]>([])
const [activeJobId, setActiveJobId] = useState<string | null>(null)
const job = useMemo(() => jobs.find((j) => j.id === activeJobId) ?? null, [jobs, activeJobId])
const [audioStripJobId, setAudioStripJobId] = useState<string | null>(null)
const audioStripJob = useMemo(() => jobs.find((j) => j.id === audioStripJobId) ?? null, [jobs, audioStripJobId])
const [submitting, setSubmitting] = useState(false)
const [analyzing, setAnalyzing] = useState(false)
const [frameTargets, setFrameTargets] = useState<Record<string, FrameExtractTarget>>({})
@@ -163,10 +159,6 @@ export default function Home() {
const flowRef = useRef<any>(null)
const lastVideoPanelFocusKey = useRef("")
useEffect(() => {
setClientReady(true)
}, [])
const setSelectedFramesForJob = useCallback((jobId: string, updater: Set<number> | ((prev: Set<number>) => Set<number>)) => {
setSelectedFramesByJob((prev) => {
const current = new Set(prev[jobId] ?? [])
@@ -199,10 +191,6 @@ export default function Home() {
const handleSwitchJob = useCallback((id: string) => {
setActiveJobId(id)
}, [])
const handleOpenAudioStrip = useCallback((jobId?: string) => {
const targetId = jobId ?? activeJobId
if (targetId) setAudioStripJobId(targetId)
}, [activeJobId])
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)
const handleSubmit = useCallback(async (url: string) => {
@@ -227,7 +215,6 @@ export default function Home() {
const created = await uploadJob(file)
addJob(created)
setProductionJobIds((prev) => new Set(prev).add(created.id))
setAudioStripJobId(created.id)
toast.success(`已上传 ${created.id.slice(0, 8)},下载完成后自动解析音频`)
} catch (e) {
toast.error("上传失败:" + (e instanceof Error ? e.message : String(e)))
@@ -441,7 +428,6 @@ export default function Home() {
const handleTranscribeAudio = useCallback(async (jobId?: string, options?: { silent?: boolean }) => {
const targetId = jobId ?? activeJobId
if (!targetId) return
setAudioStripJobId(targetId)
const target = jobs.find((item) => item.id === targetId)
if (!target) return
if (!target.video_url) {
@@ -538,7 +524,6 @@ export default function Home() {
return
}
setProductionJobIds((prev) => new Set(prev).add(target.id))
setAudioStripJobId(target.id)
toast.success("已进入第一步:下载完成后自动解析音频文案、讲话人和背景音")
if (target.video_url && ["downloaded", "frames_extracted", "transcribed", "failed"].includes(target.status)) {
void handleTranscribeAudio(target.id, { silent: true })
@@ -898,10 +883,9 @@ export default function Home() {
onCopyImage: handleCopyImage,
onGenerateProductFusionVideo: handleGenerateProductFusionVideo,
onTranscribeAudio: handleTranscribeAudio,
onOpenAudioStrip: handleOpenAudioStrip,
pinnedNodes,
onToggleNodePin: handleToggleNodePin,
}), [job, jobs, activeJobId, submitting, analyzing, frameTargets, frameCounts, frameQualities, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleStartProduction, handleUpload, handleAnalyze, handleAnalyzeJob, handleFrameTargetChange, handleFrameCountChange, handleFrameQualityChange, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleCloseExpandedFrame, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, updateJobInList, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, handleOpenStoryboard, handleOpenWorkbench, clipboard, handleCopyImage, handleGenerateProductFusionVideo, handleTranscribeAudio, handleOpenAudioStrip, pinnedNodes, handleToggleNodePin])
}), [job, jobs, activeJobId, submitting, analyzing, frameTargets, frameCounts, frameQualities, selectedFrames, expandedFrame, framePanelScale, framePanelPinned, framePanelDock, videoPanelJobId, videoPanelScale, videoPanelDock, handleSubmit, handleStartProduction, handleUpload, handleAnalyze, handleAnalyzeJob, handleFrameTargetChange, handleFrameCountChange, handleFrameQualityChange, handleToggleFrame, handleOpenFramePanel, handleFramePanelScaleChange, handleCloseExpandedFrame, handleAddManualFrame, handleAddManualFrameForJob, handleOpenVideoPanel, handleVideoPanelScaleChange, handleSwitchJob, updateJobInList, handleDeleteJob, handleDeleteFrame, handleDeleteFrameForJob, handleDeleteGenerated, handleDeleteVideo, handleDeleteCutout, handleOpenStoryboard, handleOpenWorkbench, clipboard, handleCopyImage, handleGenerateProductFusionVideo, handleTranscribeAudio, pinnedNodes, handleToggleNodePin])
// 用 useNodesState 让 ReactFlow 自己管位置(避免轮询时重置 drag
const savedSizes = useMemo(() => loadNodeSizes(), [])
@@ -1145,7 +1129,6 @@ export default function Home() {
<div className="absolute bottom-4 right-4 z-30 pointer-events-auto">
<ThemeToggle />
</div>
{clientReady && <AudioStrip job={audioStripJob} open={!!audioStripJob} onClose={() => setAudioStripJobId(null)} />}
<Toaster theme="system" position="top-center" />
</main>
</>

View File

@@ -384,9 +384,6 @@ export function AdRecreationBoard({
<Mic className="h-3.5 w-3.5" />
</ActionButton>
<ActionButton disabled={!job?.source_audio_url && !job?.audio_script?.voice_url} variant="ghost" onClick={() => data.onOpenAudioStrip?.(job?.id)}>
</ActionButton>
</div>
</div>