auto-save 2026-05-12 19:47 (~2)

This commit is contained in:
2026-05-12 19:48:07 +08:00
parent f901b71d66
commit 07766e0e2d
2 changed files with 43 additions and 10 deletions

View File

@@ -265,6 +265,13 @@
"message": "auto-save 2026-05-12 19:36 (~3)", "message": "auto-save 2026-05-12 19:36 (~3)",
"hash": "902c3ed", "hash": "902c3ed",
"files_changed": 3 "files_changed": 3
},
{
"ts": "2026-05-12T19:42:27+08:00",
"type": "commit",
"message": "auto-save 2026-05-12 19:42 (~3)",
"hash": "f901b71",
"files_changed": 3
} }
] ]
} }

View File

@@ -229,6 +229,11 @@ export function KeyframeNode({ data, selected }: any) {
const st = keyframeStatus(d.job) const st = keyframeStatus(d.job)
const frames = d.job?.frames ?? [] const frames = d.job?.frames ?? []
const jobId = d.job?.id const jobId = d.job?.id
const job = d.job
const hasVideo = !!job?.video_url
const videoRef = useRef<HTMLVideoElement>(null)
const [videoT, setVideoT] = useState(0)
const [addingFrame, setAddingFrame] = useState(false)
return ( return (
<div className="relative" style={{ width: KEYFRAME_WIDTH }}> <div className="relative" style={{ width: KEYFRAME_WIDTH }}>
@@ -304,22 +309,43 @@ export function KeyframeNode({ data, selected }: any) {
type="process" status={st} type="process" status={st}
icon={<ImageIcon className="h-4 w-4" />} icon={<ImageIcon className="h-4 w-4" />}
title="关键帧 · Keyframes" title="关键帧 · Keyframes"
subtitle={`STEP 4 · ffmpeg · ${frames.length ? `${d.selectedFrames.size}/${frames.length} 选用` : "等待抽取"}`} subtitle={`STEP 2 · ffmpeg · ${frames.length ? `${d.selectedFrames.size}/${frames.length} 选用` : "等待抽取"}`}
width={KEYFRAME_WIDTH} width={KEYFRAME_WIDTH}
selected={selected} selected={selected}
> >
{frames.length > 0 ? ( {hasVideo && jobId ? (
<div className="text-[11.5px] leading-relaxed text-[var(--text-soft)]"> <div className="space-y-2">
<span className="text-[var(--text-strong)] font-medium">{frames.length}</span> · <video
ref={videoRef}
<br /> src={videoUrl(jobId)}
<span className="text-[10.5px] text-[var(--text-faint)]"> controls
/ onTimeUpdate={(e) => setVideoT((e.target as HTMLVideoElement).currentTime)}
</span> className="block w-full rounded-md bg-black border border-black/10 dark:border-white/10"
/>
<button
type="button"
disabled={addingFrame}
onClick={async (e) => {
e.stopPropagation()
const t = videoRef.current?.currentTime ?? videoT
setAddingFrame(true)
try { await d.onAddManualFrame(t) } finally { setAddingFrame(false) }
}}
className="w-full text-[12px] py-2 rounded-md bg-emerald-500 hover:bg-emerald-400 text-white disabled:opacity-50 inline-flex items-center justify-center gap-1.5 font-medium"
title="把视频当前时间点抽为新关键帧"
>
{addingFrame ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Plus className="h-3.5 w-3.5" />}
{addingFrame ? "抽帧中…" : `+ 把 ${videoT.toFixed(1)}s 加为关键帧`}
</button>
<div className="text-[10.5px] text-[var(--text-faint)] text-center">
{frames.length > 0
? `自动 ${frames.length} 张 · 选中 ${d.selectedFrames.size} 张 · 上方缩略图可点击放大`
: "等待解析后抽取(默认 5 张)"}
</div>
</div> </div>
) : ( ) : (
<div className="text-[11.5px] text-[var(--text-faint)] py-1"> <div className="text-[11.5px] text-[var(--text-faint)] py-1">
5 API KEYFRAME_COUNT
</div> </div>
)} )}
</NodeShell> </NodeShell>