diff --git a/.memory/worklog.json b/.memory/worklog.json index 561e758..ecf17cf 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -3186,6 +3186,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 6 项未提交变更 · 最近提交:auto-save 2026-05-14 04:15 (~4)", "files_changed": 6 + }, + { + "ts": "2026-05-14T04:21:26+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 04:21 (~6)", + "hash": "ec96e81", + "files_changed": 6 + }, + { + "ts": "2026-05-13T20:23:12Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 4 项未提交变更 · 最近提交:auto-save 2026-05-14 04:21 (~6)", + "files_changed": 4 } ] } diff --git a/api/main.py b/api/main.py index c93767e..189173e 100644 --- a/api/main.py +++ b/api/main.py @@ -640,7 +640,7 @@ async def pipeline_analyze( existing_frames = list(job.frames) if not replacing else [] if replacing and frames_dir.exists(): shutil.rmtree(frames_dir) - frames_dir.mkdir(parents=True) + frames_dir.mkdir(parents=True, exist_ok=True) scan_dir = d / "frame_scan" if scan_dir.exists(): shutil.rmtree(scan_dir) diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 0be467b..24fb53b 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -723,7 +723,7 @@ api/main.py 输入 Input - 创建/上传任务,显示视频就绪;每个视频缩略图上方都有绑定自己的自动抽帧快捷工具条,可快速选目标、张数、精度并多次追加;单击视频缩略图打开画布内抽帧面板。 + 创建/上传任务,显示视频就绪;每个视频缩略图上方都有绑定自己的自动抽帧快捷工具条,默认只露出目标和抽帧按钮,张数/精度收进设置;横屏/竖屏都按真实比例显示和评分;单击视频缩略图打开画布内抽帧面板。 不要自动一路跑到 ASR 或生图;用户需要控制解析节奏。 page.tsxInputNodeVideoFramePanelNodeapi/main.py @@ -817,6 +817,19 @@ api/main.py

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-14 · 抽帧工具条降噪并修复追加失败

+ Input + Bugfix +
+
+

问题:每个缩略图上方同时放目标、张数、精度和按钮太拥挤;另外追加抽帧时可能没有新增图片。

+

根因:追加模式下 frames 目录已经存在,但后端仍使用非 exist_okmkdir,触发 File exists 后任务进入解析失败。

+

改动:工具条默认只显示目标、抽帧/追加和设置按钮;张数、精度折叠到设置里。后端改为允许已存在的 frames 目录,追加模式不再因目录存在失败。缩略图高度增大到 192px,横屏/竖屏都按真实比例显示;抽帧评分也按视频原比例缩放,不固定 16:9。

+

影响:api/main.pyweb/app/page.tsxweb/components/nodes/index.tsxdocs/source-analysis.html。已用临时 job 验证 append:已有 1 张关键帧时追加 3 张后变为 4 张。

+
+

2026-05-14 · 自动抽帧增加本地精度模式

diff --git a/web/app/page.tsx b/web/app/page.tsx index 33df0f6..a77512e 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -178,7 +178,7 @@ export default function Home() { if (!targetJob) return const frameTarget = frameTargets[jobId] ?? "balanced" const frameCount = frameCounts[jobId] ?? 5 - const frameQuality = frameQualities[jobId] ?? "accurate" + const frameQuality = frameQualities[jobId] ?? "ultra" const mode = options?.mode ?? (targetJob.frames.length > 0 ? "append" : "replace") setActiveJobId(jobId) setAnalyzing(true) diff --git a/web/components/nodes/index.tsx b/web/components/nodes/index.tsx index 4550256..0d08844 100644 --- a/web/components/nodes/index.tsx +++ b/web/components/nodes/index.tsx @@ -10,7 +10,7 @@ import { type NodeProps, useReactFlow } from "@xyflow/react" import { Link2, Upload, Download, Scissors, Image as ImageIcon, Mic, Languages, FileEdit, Film, FileVideo, Loader2, Plus, X, LayoutGrid, Maximize2, - Copy, Trash2, Move, PanelLeft, PanelRight, PanelBottom, ChevronLeft, ChevronRight, + Copy, Trash2, Move, PanelLeft, PanelRight, PanelBottom, ChevronLeft, ChevronRight, SlidersHorizontal, } from "lucide-react" import { toast } from "sonner" import { NodeShell, type NodeStatus, type NodeKind } from "./node-shell" @@ -124,7 +124,7 @@ function clamp(value: number, min: number, max: number) { return Math.max(min, Math.min(max, value)) } -const THUMBNAIL_HEIGHT = 176 +const THUMBNAIL_HEIGHT = 192 const FLOATING_PANEL_EDGE_INSET = 8 const FRAME_TARGET_OPTIONS: Array<{ value: FrameExtractTarget; label: string; hint: string }> = [ { value: "balanced", label: "综合关键帧", hint: "清晰、去重、变化、时间覆盖" }, @@ -439,6 +439,7 @@ function FrameExtractQuickBar({ }) { const option = FRAME_TARGET_OPTIONS.find((item) => item.value === target) ?? FRAME_TARGET_OPTIONS[0] const qualityOption = FRAME_QUALITY_OPTIONS.find((item) => item.value === quality) ?? FRAME_QUALITY_OPTIONS[1] + const [settingsOpen, setSettingsOpen] = useState(false) return (
e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} > - -
+
- @@ -487,12 +465,50 @@ function FrameExtractQuickBar({ disabled={disabled} onClick={onAnalyze} title={hasFrames ? "追加自动抽帧" : "自动抽帧"} - className="inline-flex h-7 shrink-0 items-center justify-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-violet-500 px-2 text-[10.5px] font-bold text-white shadow-lg shadow-violet-950/35 transition hover:opacity-95 disabled:cursor-not-allowed disabled:opacity-45" + className="inline-flex h-8 shrink-0 items-center justify-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-violet-500 px-2.5 text-[10.5px] font-bold text-white shadow-lg shadow-violet-950/35 transition hover:opacity-95 disabled:cursor-not-allowed disabled:opacity-45" > {running ? : } {running ? "抽取" : hasFrames ? "追加" : "抽帧"} +
+ {settingsOpen && ( +
+ + +
+ )}
) } @@ -553,7 +569,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an const toolWidth = Math.max(148, thumbNaturalWidth) const target = d.frameTargets[j.id] ?? "balanced" const count = d.frameCounts[j.id] ?? 5 - const quality = d.frameQualities[j.id] ?? "accurate" + const quality = d.frameQualities[j.id] ?? "ultra" const jHasFrames = j.frames.length > 0 const jRunning = ["splitting", "transcribing"].includes(j.status) return ( @@ -576,7 +592,7 @@ export function InputNode({ data, selected }: NodeProps<{ data: NodeData }> | an onAnalyze={() => d.onAnalyzeJob(j.id, { mode: jHasFrames ? "append" : "replace" })} /> ) : ( -
+
)}