Studio 前端创作流程
本页讲 platform/studio/web(Next.js 16)的架构和交互设计,聚焦"用户怎么用、界面怎么组织";后端接口见 API 参考。
架构速览
路由不是常规 App Router 多页面站点:src/app/ 下只有一个真实路由段(page.tsx + layout.tsx),页面切换靠 page.tsx 内部监听 window.location.hash(#/、#/project/{id}、#/series/{id}、#/project/{id}/editor 等)自行分发,动态懒加载对应组件。这是配合 Next.js 静态导出(output: 'export')+ nginx try_files ... /studio/index.html 单文件回退刻意做的哈希路由 SPA:反正只有一份 HTML 壳,服务器不需要为每个虚拟路径单独出页面。
组件分层:
状态管理:Zustand,4 个全局 store:
src/generated/modelCatalog.json 只是从后端单向导出的静态配置(见 模型目录设计),不是 API client;同目录下的 src/generated/api-types.ts(2026-09 新增,从 api/openapi.json 用 pnpm generate:api-types 生成)也只是类型定义,同样不是 client。全站唯一的后端通信层仍是 src/lib/api.ts——一个手写的、按功能分区的百余方法大对象,axios 和裸 fetch 混用,没有 React Query/SWR 之类的数据层框架;生成的类型目前只能给这些手写调用逐个标注参数/返回值用,详见 API 参考 和已知问题。
鉴权:自己的登录页 + 静默续期(2026-09)
Studio 现在有自己的登录页(AuthGate 包住整个应用;未登录时渲染 LoginPage,不渲染 children),浏览器直接打车库 Go 后端的 POST /api/v1/auth/login——跟 admin 控制台同一账号库、同一批账密,不是另建一套账号系统。选择"浏览器直连 Go"而不是经 studio-api 代理,是因为 Go 的 CORS 中间件(H5_ALLOWED_ORIGINS)本来就是为"credential-free Bearer 请求"设计的,直接复用即可。
会话存在 localStorage(authStore,Zustand persist,key aivideo-auth),不再要求 Studio 与 admin 同域名。src/lib/auth.ts 同时 patch axios 拦截器和全局 window.fetch:请求收到 401 时先尝试一次去重的静默刷新(拿 refreshToken 调 /api/v1/auth/refresh-token,多个并发 401 共享同一个刷新 Promise,不会打多次),成功就重试原请求;刷新也失败(或压根没有 refresh token)就清空 store——AuthGate 订阅了这个 store,会立刻反应式地换成登录页,不做整页跳转。
代价也要说清楚:AuthGate 是把整棵组件树换成 LoginPage,不是叠一层弹窗,所以重新登录后 URL hash 能带回原来的项目/镜头,但组件内存态(正在编辑没保存的 prompt、打开的弹窗)不会保留。旧的 authorized-token cookie 仍作为兜底读取路径(getAccessToken() 优先读 store,读不到才退回读 cookie),同域部署且已登录 admin 的场景不受影响。
核心创作旅程
工作区首页
#/:系列分组 + 独立项目分组两段式画廊(卡片/列表视图可切换),状态筛选(全部/已完成/生成中/草稿)+ 搜索。新建走一个下拉菜单,"新建系列"弹窗是一个三组二选一按钮矩阵(不是分步向导):工作流模式(R2V 节奏优先 / legacy 画面优先)、内容模式(有剧本 / 无剧本直接分镜)、默认生成模式,每组两张可点选卡片,选中态高亮 + "推荐"角标。
单集创作台
ProjectClient.tsx 用 PipelineSidebar 左侧步骤条驱动,步骤集合按 workflow_mode/content_mode 动态计算:
- 当前主推的 5 步 R2V 流:Script → Art Direction → Cast → Storyboard → Assembly;
content_mode==='freeform'(无剧本)时连 Script 步都跳过,标签序号自动重编。 - Legacy 9 步流:Script → Art Direction → Assets → Storyboard → Motion → Assembly,是更早期的分步设计,仍在维护但不是主推路径。
侧边栏每步的完成状态是"有没有数据"的保守推断(比如 art_direction 字段存在即视为 ready),不是显式打勾;Assembly 步在还没有分镜时是"软锁定",可点但提示 gated。
- Script:文本编辑 + AI 解析提取角色/场景/道具;解析结果先弹出确认框,用户确认后才真正落库(状态挂在全局 store,切换步骤也不丢)。
- Art Direction:预设风格卡片网格 + AI 根据剧本推荐风格,支持自定义 prompt;系列级默认与集级覆盖两层继承。
- Cast(R2V 流特有):只读聚合视图,展示本集分镜引用了哪些角色/场景/道具,点卡片进工作台弹窗(生成/换图/绑定语音)。
Storyboard:核心分镜创作台
这是全站最大最复杂的组件(StoryboardR2V.tsx,2000+ 行),不是拖拽画布,是纵向堆叠的镜头卡片列表,支持上移/下移/复制/删除。每张卡片是一个双阶段生成台:
- T2I→I2V 模式:先文生图产出关键帧(支持"抽卡"式多次生成,保留历史缩略图条,用户挑一张作为首帧),再拿选中首帧走图生视频。
- Direct R2V 模式:跳过关键帧,直接用参考图/参考视频驱动生成。
两种模式一个 tab 切换,per-shot 可单独覆盖系列默认偏好。Prompt 输入框支持 @角色名/@场景名/@道具名 mention 语法插入资产标签(侧拉抽屉选择器),并配有 AI 润色面板。生成结果是九宫格候选池,可加星标、写备注、pin 为最终选用镜头,也可以多个候选并排比较。顶部有"上一集摘要"横幅和"下集悬念预测"辅助跨集连续性。台词逐句 TTS 生成、预览、配音对轨预览也在同一张卡片里完成。
另有一套独立的 Tiptap 富文本剧本编辑器(ScriptEditor/,独立子目录,20+ 文件):好莱坞剧本格式、场次大纲、角色/地点/道具属性面板、编辑/分镜/朗读/专注四种视图模式、离线本地缓存自动恢复,通过 #/studio/editor、#/project/{id}/editor 独立入口进入。它和 Script 步骤用的 ScriptProcessor.tsx(纯文本框)功能有明显重叠,两者目前并存,去留不明——详见 已知问题。
Assembly:合成导出
三个 Tab:Takes(按分镜分组展示所有已完成视频候选,逐镜选定最终版本)→ Mix(BGM 预设 + 对白/BGM/音效三轨音量滑杆)→ Export(打包下载)。合并走一次性请求,不是分步流水线式提交。
与后端的交互模式
全部是同步 REST 请求 + 客户端轮询,没有 WebSocket。长耗时 AI 生成的进度靠固定间隔(3-5 秒)的 setInterval 轮询任务状态,没有指数退避,失败时静默重试下一 tick。唯一的例外是"批量精修分镜"这一个场景用了 SSE(fetch + ReadableStream 手写解析),因为那是单次长连接场景,比轮询更合适。
失败处理没有统一的重试框架,各组件各自 try/catch,多数情况下 toast 提示 + 把该镜头状态标成失败让用户手动重来;有一个诊断面板可以看后端日志尾部帮助排查卡住的任务,但没有自动止损机制。
国际化
next-intl,仅 zh(默认)/en 两种语言,消息文件按 ~38 个功能域命名空间组织。切换入口在设置页的一个选择控件,写入 settingsStore 并持久化到 localStorage,纯客户端切换,不改 URL、不刷新页面。
测试
两套 vitest 配置:pnpm test 跑纯逻辑单测(node 环境,8 个文件,测的是从组件里抽出来的业务函数:i18n key 一致性、模型目录选择器、prompt 拼装、字段校验等);pnpm test:ui 跑组件测试(happy-dom 环境,仅 2 个文件)。README 提到的"46 个上游遗留失败"就是这 2 个组件测试文件的全部用例,原因是渲染时没有用 NextIntlClientProvider 包裹被测组件——是测试基建缺口,不是业务逻辑问题。
构建部署
next.config.mjs:生产环境 output: 'export' + basePath: '/studio',images.unoptimized: true(静态导出必须关图片优化)。typescript.ignoreBuildErrors: true——生产构建不因类型错误失败,类型安全完全依赖开发者单独跑 pnpm typecheck,详见 已知问题。