# 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:操作前先核实