diff --git a/.memory/decisions.md b/.memory/decisions.md new file mode 100644 index 0000000..a006f76 --- /dev/null +++ b/.memory/decisions.md @@ -0,0 +1,97 @@ +# 关键决策记录 + +## 2026-05-12 + +### UI 架构演进(被用户多次调整) +1. ❌ 初版:上下流式 04 暗色画廊 → 否决 +2. ❌ 横向小节点紧凑 DAG(10 节点)+ stage bar 顶部 → 用户要换方向 +3. ❌ Storyflow 大节点列式 + mini DAG 拓扑底部 → 用户说"理解错了" +4. ❌ 顶部看板(看板每列对应节点)→ 用户嫌太扎眼 +5. ❌ 折叠看板(点击展开)→ 多选展开 → 改回单选展开 +6. ✅ **侧面 sidebar(108px)+ drawer 弹出**(最终) + +### 节点合并 +- 用户:"输入/下载/拆分 三合一" → input 节点吃下 download + split 的信息和状态 +- 原 10 节点 → 8 节点 + +### 视觉决策 +- Kanban 风格(不是 Apple 04 暗色画廊) +- 4 类型颜色:input 紫蓝 / process 橙 / ai 紫粉 / output 翠绿 +- 关键帧节点改为 process 类型(橙),因为不调模型(只 ffmpeg) +- mini-card 内嵌:玻璃质感 + 渐变 + 圆角 + 深阴影 + +### 关键帧选取算法(D 启发式) +1. ffmpeg 均匀采样 30 张候选(fast seek) +2. pHash 去重相似帧(汉明距离 < 8) +3. Laplacian variance 评分清晰度(手写 numpy,不用 scipy) +4. 时序分桶 n 等分,每桶取最清晰 +5. 输出 5 张 + +不用场景切换检测(太慢,且 TK 一镜到底视频抓不出来)。 + +### 抽帧 ffmpeg 参数(重要坑) +``` +ffmpeg -y -ss -i source.mp4 \ + -frames:v 1 \ + -pix_fmt yuvj420p \ # ⚠️ mjpeg 不支持 yuv420p + -q:v 3 \ + out.jpg +``` + +### 视频缩略图设计 +- Input 节点上方:每个 job 一个 80px 缩略图浮条 + 「+」按钮再上传 +- hover:自动播放视频片段(muted loop) +- click:node 上方 360px 展开 controls + 加帧按钮(不全屏) + +### 关键帧缩略图设计 +- 浮条 grid-cols-5(超过 5 张换行向上扩展) +- aspectRatio 跟视频原比例(竖屏 9:16 / 横屏 16:9 自适应) +- hover:弹 KEYFRAME_WIDTH*2 静态大图(**不放视频,因为关键帧是垫图素材**) +- click:打开 keyframe drawer 内的 lightbox + +### Lightbox 嵌入策略 +- 浮动 fixed 模式被否决("全屏太遮挡"+"切换累") +- 最终:嵌入 keyframe sidebar drawer,drawer 宽度自适应(400 / 760) +- 颜色顶栏用 TYPE_GRAD["process"] 橙红渐变,跟 keyframe tile 同色系 + +### Vision identify 结果结构 +```json +{ + "scene": "一句话场景", + "objects": [{"name", "position", "color", "extract_prompt"}], + "style": "风格 / 打光 / 色调", + "suggested_prompt": "完整英文 prompt" +} +``` + +下游生图直接拿 suggested_prompt 作 base prompt + 用户的正向/负向 prompt。 + +### 生图正负 prompt 拼合 +``` +{画面描述}. Include: {正向}. Do NOT include: {负向}. +Output must be clean without any watermark, username text, or platform logo. +``` + +默认 negative:「水印, @用户名, TikTok logo, 平台文字, 浮水印」 → 直出"纯净图"。 + +## 关键架构选择 + +### 多 job 支持 +- jobs: Job[] state +- activeJobId 切换 active +- URL: `?job=id1,id2,id3` 持久化所有 +- 提交链接 / 上传 → push 新 job + setActive + +### 状态机 +``` +created → downloading → downloaded(停,等用户点解析) + → splitting → frames_extracted + → transcribing → transcribed | failed +``` + +「解析」按钮是关键的人工触发点(不是全自动),用户能选择性跑。 + +## 仍未解决的设计问题 +- 区域化修图(inpainting)UI 没定(A 纯 prompt / B 矩形 / C 画笔 / D SAM) +- 多 job 并发轮询逻辑(当前只 active job 轮询) +- 生图结果选用后怎么传到 video gen 节点(数据流) diff --git a/.memory/plan.md b/.memory/plan.md index 7afcab2..4d40eea 100644 --- a/.memory/plan.md +++ b/.memory/plan.md @@ -1,5 +1,10 @@ # SKG TK 二创验证 — 初版 Plan +> ⚠️ 2026-05-13 已过时:实际进展见 `.memory/status.md`、关键决策见 `.memory/decisions.md`、SKG 网关能力见 `.memory/skg-gateway.md` + +--- + + > intake 阶段 2026-05-12 输出,开发会话起点物。第一件事仍是跑 `/plan` 二次细化。 ## 目标 diff --git a/.memory/skg-gateway.md b/.memory/skg-gateway.md new file mode 100644 index 0000000..b4ad2a5 --- /dev/null +++ b/.memory/skg-gateway.md @@ -0,0 +1,78 @@ +# SKG AI 网关探测记录 + +`https://ai.skg.com/ezlink/v1` · OpenAI 兼容 · one-hub 多渠道代理后端 + +## 端点能力矩阵 + +| 端点 | 状态 | 用法 | 备注 | +|---|---|---|---| +| `GET /v1/models` | ✅ | 列模型 | 列了 100+ 模型(OpenAI/Anthropic/Gemini/DeepSeek 全家桶 + Sora/Whisper/TTS) | +| `POST /v1/chat/completions` | ✅ | text/multimodal | image_url 通;input_audio 不通 | +| `POST /v1/images/generations` | ✅ | text→image | 支持 `image` 字段做 i2i | +| `POST /v1/images/edits` | ❌ 404 | - | | +| `POST /v1/audio/transcriptions` | ❌ 404 | - | whisper 端点没暴露 | +| `POST /v1/audio/speech` | ❌ 404 | - | tts 端点没暴露 | +| `POST /v1/files` | ❌ 403 | - | "必须指定渠道" | +| `POST /v1/videos/*` | ❌ 404 | - | sora-2 在 models 但端点没通 | +| `POST /v1/responses` | ❌ 400 | - | 不支持 | +| `POST /v1beta/models/{m}:generateContent` | ❌ 404 | - | 原生 Gemini 路径未暴露 | +| `POST /v1/messages` (Anthropic 原生) | ❌ 404 | - | | + +## 当前 key 渠道分组 +`纯OpenAI+AWSClaude+Gemini官方` + +错误 `503 当前分组「纯OpenAI+AWSClaude+Gemini官方」下对于模型 gpt-4o-audio-preview 无可用渠道` 揭示分组规则——audio 渠道未配。 + +要让 IT 开 audio:让他们把 `gpt-4o-audio-preview` 或 `whisper-1` 渠道加进这个分组。 + +## Vision 探测过程 +1. **第一次**:用 dog.jpg(wiki 200px 缩略图)测,全失败 ❌ → **误判为不通** +2. **第二次**:用 macOS Stone.png 纯灰图测,✅ 通 +3. **第三次**:用真关键帧 jpg 测,✅ 完整识别(虽然 content 偶尔空,要从 reasoning_content 挖) + +**教训**:网关层探测要用多种格式 / 多张图,单图失败不能下结论。 + +## image-to-image 探测 +- `/v1/images/generations` 加 `image: "data:image/jpeg;base64,..."` 字段 +- 返回 `data: [{b64_json: ...}]` +- 网关后台 one-hub 内部命名 `one-hub-gemini-image` +- chat completions 同样 multimodal 报 "image upload err" 不通;只走 generations 端点 + +## 模型实际可用清单 + +### Vision multimodal(识图) +- gemini-2.5-flash ⭐ 推荐(速度快,描述好) +- gemini-2.5-pro(描述更详细,但 thinking 占 token) +- claude-sonnet-4-5/4-6 +- gpt-4.1, gpt-5/5.1/5.2 + +### Text chat +- gemini-2.5-flash ⭐ 默认 +- gemini-2.5-pro +- 所有 Claude / GPT 模型 + +### Image generation +- **gemini-3-pro-image-preview** ⭐ = nano-banana-pro(推荐) +- gemini-3.1-flash-image-preview +- gemini-2.5-flash-image +- gpt-image-1 → 404(看起来网关没接通) + +### Audio +- whisper-1 列了但端点 404 +- gpt-4o-transcribe 列了但端点 404 +- tts-1 列了但端点 404 +- 全部需要 IT 开 + +### Video +- sora-2 列了但端点 404 +- 需要 IT 开 / 外部 key + +## prompt 拼合标准(生图) +``` +{base scene description}. +Include: {positive elements, separated by comma}. +Do NOT include: {negative elements}. +Output must be clean without any watermark, username text, or platform logo. +``` + +经过测试 nano-banana-pro 对 "Do NOT include" 响应不错(生成图明显避开列出的元素)。 diff --git a/.memory/status.md b/.memory/status.md new file mode 100644 index 0000000..8360798 --- /dev/null +++ b/.memory/status.md @@ -0,0 +1,146 @@ +# SKG TK 二创验证 — 当前状态(2026-05-13) + +## 一句话 +SKG AI 素材生产管线第二条思路:TK 链接/上传 → 拆轨 → 抽关键帧(5 张+手动加)→ Vision 识别 → 改写文案 → 生图 → 生视频 → 合成。**MVP 通到生图,剩余 3 个节点占位**。 + +## 路径 / 端口 +- 路径:`~/Projects/business/20260512-20260512-skg-tk-二创验证/` +- web dev:`cd web && pnpm dev`(端口 **4290**) +- api dev:`cd api && source .venv/bin/activate && uvicorn main:app --port 4291 --reload` +- 测试 job:`?job=c6767f3a166b`(chrisorb 71s 竖屏 TK) + +## SKG 网关能力(实测 · 关键!) +`base_url: https://ai.skg.com/ezlink/v1` +key 写在 `api/.env` 的 `LLM_API_KEY` + +| 端点 / 字段 | 状态 | 用途 | +|---|---|---| +| `/v1/chat/completions` text-only | ✅ 通 | translate / rewrite | +| `/v1/chat/completions` + image_url | ✅ **通**(之前误判为不通,是 dog.jpg 那张图损坏) | vision 识别图片(gemini-2.5-flash 推荐) | +| `/v1/chat/completions` + input_audio | ❌ 不通 | ASR 不能走这条 | +| `/v1/audio/transcriptions` (whisper) | ❌ 404 | 整个 audio 端点都没暴露 | +| `/v1/audio/speech` (tts) | ❌ 404 | | +| `/v1/images/generations` (text→image) | ✅ 通 | 生图(gemini-3-pro-image-preview = nano-banana-pro) | +| `/v1/images/generations` + image 参数 | ✅ **通**(image-to-image) | 实测能传 reference image,关键的发现 | +| `/v1/images/edits` | ❌ 404 | | +| `/v1/videos/*` (sora-2) | ❌ 404 | 视频生成需要 IT 开通或外部 key | +| `/v1/files` | ❌ 403 "必须指定渠道" | | + +**网关后端 = one-hub 多渠道代理**。当前 key 分组叫「纯OpenAI+AWSClaude+Gemini官方」,缺 audio 渠道(`gpt-4o-audio-preview` 503 "无可用渠道")和 video 渠道。 + +## 模型选型(已写入 api/.env) +``` +ASR_MODEL=whisper-1 # ⚠️ 端点 404,ASR 还没真跑通 +TRANSLATE_MODEL=gemini-2.5-flash # ✅ text 已通 +REWRITE_MODEL=gemini-2.5-pro # 占位 +VISION_MODEL=gemini-2.5-flash # ✅ 识别已通 +IMAGE_MODEL=gemini-3-pro-image-preview # ✅ nano-banana-pro,i2i 已通 +``` + +## Pipeline 状态(8 节点合并版) +原 10 节点已合并:input + download + split 合一;translate 合到 transcript;videogen 和 compose 占位。 + +| 步 | 节点 | 状态 | 备注 | +|---|---|---|---| +| 1 | **输入·Input**(合并下载+拆分) | ✅ | yt-dlp 真下 + ffmpeg 拆 wav | +| 2 | **关键帧·Keyframes** | ✅ | D 启发式:候选 30 张 → pHash 去重 + Laplacian variance 评分 + 时序分桶 → 5 张;手动加帧 OK | +| 3 | **转录·ASR** | ❌ 阻塞 | SKG 网关 audio 不通;待 IT 开 audio 渠道 / 外部 key | +| 4 | **翻译·Translate** | ❌ 阻塞 | 依赖 ASR | +| 5 | **改写·Rewrite** | ⏳ 占位 | 等用户给产品信息模板 | +| 6 | **生图·Image Gen** | ✅ **刚做完** | nano-banana-pro i2i + 正负 prompt | +| 7 | **生视频·Video Gen** | ⏳ 占位 | sora-2 端点不通 | +| 8 | **合成·Compose** | ⏳ 占位 | 本地 ffmpeg + 字幕 + TTS | + +## UI 架构(重要) +- **左侧 sidebar**(108px 极窄):8 个 stage tile 竖排 + DAG 路径分叉表达 +- **主区 ReactFlow**:8 节点 DAG(input → keyframe/asr → ... → compose) +- **点 sidebar tile**:从左滑出 drawer panel(粉/紫/橙 Kanban 风格) +- **关键帧 lightbox**:**embedded 嵌入到 keyframe drawer**(不全屏)—— ``,drawer 宽度有 expandedFrame 时 760,无时 400 +- **Input 节点上方**:多视频缩略图浮条 + 「+」加新视频 +- **关键帧节点上方**:5+ 张缩略图按视频原比例(aspect-ratio: width/height) +- **缩略图 hover**:弹大图静态(关键帧是垫图素材,不放视频) +- **缩略图点击**:打开 keyframe drawer 内的 lightbox(左大图 + 右识别面板) + +## 数据模型(关键 typescript / pydantic) +```typescript +KeyFrame { + index: number // 稳定 ID(不连续!frames 数组按 timestamp 排序) + timestamp: number + url: string + description?: { + scene, objects: [{name, position, color, extract_prompt}], + style, suggested_prompt + } + generated_images?: [{ id, prompt, model, mode, url, selected, created_at }] +} + +Job { frames: KeyFrame[] ... } +``` + +**前端取帧必须用 `frames.find(x => x.index === activeIndex)` 不能用数组下标**(之前的 bug)。 + +## 关键文件 +- `web/app/page.tsx` — 多 job state 管理(jobs[] + activeJobId),8 节点 LAYOUT +- `web/components/dashboard.tsx` — sidebar + drawer + 9 个 Kanban section(input/keyframe/asr/translate/rewrite/imagegen/videogen/compose),含 `ImageGenCard` 子组件 +- `web/components/lightbox.tsx` — `FrameLightbox` 支持 `embedded` prop +- `web/components/video-lightbox.tsx` — Input 节点点视频缩略图弹的播放器 +- `web/components/nodes/index.tsx` — ReactFlow 8 节点定义 +- `web/lib/api.ts` — API client +- `api/main.py` — FastAPI 所有端点,KeyFrame/GeneratedImage 模型 + +## 已通的 API 端点 +``` +POST /jobs 创建 job(链接) +POST /jobs/upload 上传视频 +GET /jobs/{id} job 状态 +POST /jobs/{id}/analyze?frames=5 拆轨+抽帧+ASR 自动一气呵成 +POST /jobs/{id}/frames?t= 手动按时间戳加帧 +POST /jobs/{id}/frames/{idx}/describe ✅ Vision 识别(3 次重试 + reasoning_content 兜底) +POST /jobs/{id}/frames/{idx}/generate ✅ 生图(i2i / text-only, 含 negative_prompt) +GET /jobs/{id}/frames/{idx}/gen/{gen_id}.jpg 生成图二进制 +POST /jobs/{id}/frames/{idx}/gen/{gen_id}/select 选用某 gen 给下游 +GET /jobs/{id}/video.mp4 原视频 +GET /jobs/{id}/frames/{idx}.jpg 关键帧 jpg +GET /health +``` + +## 已知坑 / 不要再踩 +1. **关键帧 index 不连续**:手动加帧后 frames 数组按 timestamp 排序,index 是稳定 ID。lightbox 必须用 `frames.find(x => x.index === activeIndex)`,**不要**用 `frames[activeIndex]`。 +2. **SKG 网关 vision 之前测试结果错误**:用 `dog.jpg` 那张 wikipedia 200px 缩略图损坏 / metadata 异常,导致一直以为 image input 不通。用标准 PNG / 真实 jpeg 测就通了。 +3. **Gemini 2.5 Flash 默认带 thinking**,`content` 字段经常为空(token 都给了 reasoning),要从 `reasoning_content` 正则挖 JSON 兜底。 +4. **缩略图 aspect-ratio**:必须用 `aspectRatio: ${job.width}/${job.height}` 自适应,不要强制 `aspect-video` 16:9(竖屏视频会被裁切)。 +5. **ReactFlow `type="input"` / `"output"` 是 reserved**:自带白底默认样式,要 CSS 覆盖 `.react-flow .react-flow__node-input { background: transparent !important; ... }`。 +6. **ReactFlow 12 colorMode 独立于 next-themes**:必须 `` 联动,否则节点白底。 +7. **FastAPI BackgroundTasks 用法**:`bg.add_task(func, arg)` 不能传 coroutine。 +8. **ffmpeg 8 mjpeg encoder 拒绝 yuv420p**:抽帧必须加 `-pix_fmt yuvj420p`,且 `-vsync` 改 `-fps_mode`。 +9. **抽帧速度**:场景切换检测(`select='gt(scene,0.4)'`)超慢(71s 视频要 30s+),换均匀采样 fast seek(5 张 < 3 秒)。 + +## 待办(按优先级) +1. **ASR 阻塞**:找 SKG IT 开 audio 渠道,或给一个外部 ASR key(Deepgram / 讯飞 / OpenAI 直连) +2. **生图测试反馈**:刚做完,等用户在浏览器试 → 调 negative prompt / 模型选型 +3. **区域化修图(inpainting)**:用户讨论了,方案 A 纯 prompt / B 矩形框 / C 画笔 mask / D SAM;暂时搁置 +4. **改写 Rewrite**:等用户给产品信息卡模板 +5. **视频生成**:sora-2 走 SKG 端点不通;考虑外部 key (Runway/Kling/Veo3) +6. **合成 Compose**:全本地 ffmpeg + 字幕 + TTS + +## 操作流(开发会话) +```bash +# 1. 启动后端(如未跑) +cd ~/Projects/business/20260512-20260512-skg-tk-二创验证/api +source .venv/bin/activate +uvicorn main:app --port 4291 --reload + +# 2. 启动前端(如未跑) +cd ../web +pnpm dev + +# 3. 浏览器 +open http://localhost:4290/?job=c6767f3a166b +``` + +## 用户偏好提醒(feedback memory) +- feedback_image-gen-model:生图统一用 nano-banana-pro ✅ +- feedback_keep-scope-small:小需求小做 +- feedback_flow-dont-stop:连续执行到交付,真分叉才问 +- feedback_demand-before-infra:基建前先反问谁/痛点/频率 +- feedback_no-guessing-ports:操作前先核实 diff --git a/.memory/worklog.json b/.memory/worklog.json index 30856ec..d98de76 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1084,6 +1084,13 @@ "message": "auto-save 2026-05-13 09:03 (~1)", "hash": "d6a5223", "files_changed": 1 + }, + { + "ts": "2026-05-13T09:09:27+08:00", + "type": "commit", + "message": "auto-save 2026-05-13 09:08 (~1)", + "hash": "b87387d", + "files_changed": 1 } ] }