【直播切片工作台】第11章:剪辑项目模型 VideoProject

第11章:剪辑项目模型 VideoProject

本章深入 internal/model/video_project.go:一个剪辑项目如何把「素材 + 提示词 + 选段 + 画布 + 元数据」打包成一张加工单。核心是 clips0(粗选区间)与 clips1(精修选段)的双层选段设计,以及短视频标题/描述/话题的约束常量族。

流程

剪辑项目是 AI 与人工协同的交界面。创建时用户提供 name、live_id(关联素材,需其 ASR 已完成)、prompt_id(提示词,默认 1);可选携带 clips0 粗选区间(人工圈定「值得 AI 关注的大范围」)。此后 clips 的流转有两条路:

  • 一键成片:AI 切片阶段读取 clips0(空则全片)+ 提示词 + ASR 段落 → LLM 挑选句段 → 直接回写 clips1(带文本与词级时间戳)→ 草稿阶段裁剪生成。
  • 协同模式:AI 切片回写 clips1 后任务结束 → 人工在前端调整 clips1(增删改区间、改正文本)→ 手动发起草稿任务。

画布 Width/Height 决定剪映工程分辨率(0 表示未设置,创建任务时按素材分辨率自动补齐);EnableCaptions 控制草稿是否加字幕轨;Title/Description/Topics 三个字段在 AI 切片成功后由 Worker 从 LLM 结果回写,作为短视频发布时的元数据(前端发布面板可直接复用)。

输入输出视角:项目输入是「素材 ID + 提示词 ID + 可选粗选」,输出是「clips1 + 元数据」,中间被任务驱动演进。项目本身不存任务状态------运行中任务通过 GET /video-projects/:id/running-tasks 实时查询。

实现

双层选段的设计意图:

  • clips0 \[\]ClipRange:只有毫秒区间,无文本。粗筛粒度(分钟级),表达「这段直播值得看」。
  • clips1 \[\]ClipWithText:带逐句文本与可选词级时间戳。精修粒度(秒级),是草稿裁剪与字幕的直接数据源。词级 words 由 AI 切片 Worker 从 ASR 段落的 words 中截取对应时间窗写入,字幕对齐因此能精确到字。

约束常量族把产品规则写成代码(按 Unicode 码点计数,避免「汉字一个字算仨字节」的坑):

go 复制代码
VideoProjectTitleMinRunes       = 2   // 标题 2~12 字
VideoProjectDescriptionMaxRunes = 128 // 描述上限
VideoProjectTopicMinRunes       = 2   // 话题每个 2~12 字
VideoProjectTopicsMaxCount      = 6   // 共 2~6 个

这些常量与 AI 切片的提示词输出格式(第 36 章的 aiSliceUserPromptOutputFormat)严格对齐------LLM 被要求按同样约束产出,Worker 校验再回写,模型输出与数据契约由同一组常量约束。

其他实现细节:

  • DefaultVideoProjectPromptID = 1:种子数据保证 ID=1 的提示词存在(第 14 章),新建项目缺省即有可用提示词。
  • ProjectSource 字段记录项目来源(如手动创建 / 导入),为多入口创建留审计痕迹。
  • PromptID 无外键约束(注释明示):删提示词不级联,service 层换提示词时校验存在性。
  • 列表视图 VideoProjectListItem 只含摘要字段,巨大的 clips JSONB 不进列表页。

📌 设计决策

  • 双层 clips 而非一层带标记:粗选与精修语义不同(范围 vs 句段),类型系统直接区分(ClipRange vs ClipWithText),避免同一结构里塞可空字段。
  • 元数据回写到项目而非仅存任务:一次 AI 切片产出可服务多次草稿生成与人工微调后重发,避免「重新切片才能改标题」。
  • 画布尺寸存在项目上、任务创建时快照到 task:草稿任务可被请求参数覆盖画布,项目值只是默认来源(见第 12 章 task.Width 注释)。

代码示例

项目实体全貌(节选注释):

go 复制代码
// internal/model/video_project.go
type VideoProject struct {
    ID       uint   `gorm:"primaryKey" json:"id"`
    Name     string `gorm:"size:64;uniqueIndex" json:"name"`
    LiveID   uint   `gorm:"column:live_id;not null;index;comment:关联直播素材ID" json:"live_id"`
    // PromptID 提示词 ID(对应 llm_system_prompt.id),无外键约束,默认 1。
    PromptID uint   `gorm:"column:prompt_id;not null;default:1;index" json:"prompt_id"`

    Clips0 []ClipRange    `gorm:"column:clips0;serializer:json;type:jsonb;not null;default:'[]';comment:视频切片列表毫秒" json:"clips0"`
    Clips1 []ClipWithText `gorm:"column:clips1;serializer:json;type:jsonb;not null;default:'[]';comment:带文本与词级时间戳的切片列表毫秒" json:"clips1"`

    Width           int    `gorm:"not null;default:0;comment:剪映草稿工程宽度像素" json:"width"`
    Height          int    `gorm:"not null;default:0" json:"height"`
    EnableCaptions  int    `gorm:"column:enable_captions;not null;default:1;comment:是否添加字幕0否1是" json:"enable_captions"`
    Title           string `gorm:"size:64;not null;default:'';comment:短视频标题" json:"title"`
    Description     string `gorm:"size:512;not null;default:''" json:"description"`
    Topics          []string `gorm:"column:topics;serializer:json;type:jsonb;not null;default:'[]'" json:"topics"`
    ProjectSource   string  `gorm:"column:project_source;size:32;not null;default:''" json:"project_source"`
}

草稿流水线消费 clips 的入口(第 38 章详述):

go 复制代码
// internal/draft/builder.go(节选)
clips := req.Clips
if len(clips) == 0 {
    if req.Project == nil {
        return nil, fmt.Errorf("clips 为空且未提供 project")
    }
    clips, err = prepare.ResolveClipRanges(req.Project) // 从项目解析 clips
    if err != nil {
        return nil, err
    }
}
// 合并间隔 ≤500ms 的相邻片段,减少 ffmpeg 调用与草稿碎片
clips = prepare.MergeAdjacentClipRanges(clips, prepare.ClipMergeGapMS)

小结

  • 项目 = 素材 + 提示词 + 双层选段 + 画布 + 发布元数据的聚合体,是 AI 与人工的协作界面。
  • clips0 管范围、clips1 管句段,类型即语义;词级时间戳让字幕精确到字。
  • 约束常量族同时约束 LLM 提示词与数据校验,单一事实源。

思考题

  1. clips1 的人工编辑与 AI 重跑同时发生时,如何设计「保留人工修改」的合并策略?
  2. Title/Topics 回写项目后,多次切片会互相覆盖,是否应改为「每次任务一份元数据」?

项目信息

相关推荐
高频因子挖掘机12 小时前
本地已有一年历史数据,第二天更新别只拉“昨天一天”:量化 K 线增量同步实践
后端·github·api
高频因子挖掘机12 小时前
只补缺失日期,不重拉全部历史:增量更新 K 线应该怎么设计?
后端·github·api
逛逛GitHub18 小时前
我用 Codex 破解了家里这个设备,还能语音遥控。。。
github
风之九子20 小时前
在上游一天两版的项目上做二次开发:9 天 65 次提交的 fork 手记 (dsh-our-free-model)
人工智能·ai·github
邱杰47021 小时前
数智人事系统(十七):加班核算与薪资构成
后端·github
小弥儿21 小时前
GitHub今日热榜 | 2026-10-20:智能体基建七席连庄,生成类项目回流
学习·开源·github
江厌0121 小时前
两张卡部署两个模型:物理分卡、MIG、张量并行的选择与可复现配置
github·模型部署·vllm
问天_观心1 天前
模型微调学习(二)
开发语言·人工智能·学习·语言模型·github
回眸&啤酒鸭1 天前
【回眸】GitHub Copilot 智能编程辅助效果实测
github·copilot
Brilliantwxx1 天前
【Linux】基础IO(下):输出重定向、dup2 与 minishell 重定向实战
linux·运维·服务器·github·bash