【直播切片工作台】第21章:直播素材管理服务

简介: 本章详解直播素材管理服务:实现URL自动识别、双唯一约束冲突处理(返回已有记录)、RetryASR与字幕导出等动作接口;采用“哨兵错误+结构化错误+业务码”三位一体错误体系,确保前后端契约清晰、错误可追溯、响应语义丰富。(239字)

第21章:直播素材管理服务

本章分析 internal/service/live_material_service.go 与对应 handler:素材 CRUD 的业务规则(URL 类型自动识别、唯一冲突的三态错误)、RetryASR 与字幕导出两个动作型接口,以及「service 层错误即业务文档」的错误变量组织法。

流程

素材服务的输入输出围绕一条状态链展开。Create 的流程:TrimSpace 清洗 → 名称/链接非空校验 → detectLiveURLType 自动识别 URL 类型(含 .m3u8 → m3u8,否则 file,由后台判定而非客户端传入,接口注释明确这一点)→ 扩展名白名单校验(mp3/wav/mp4/mov/ogg/raw,ErrUnsupportedMediaFormat 列出全表)→ 落库(asr_status=pending)→ 触发 ASR Worker Enqueue 唤醒 → 返回完整实体。

Create 的冲突处理是本章最有设计感的部分:素材名称与 live_url 双唯一索引,插入撞唯一约束时 service 捕获并转换为携带已有记录的结构化错误:

LiveMaterialExistsError{ Material: 已存在的那条记录, Cause: 原始错误 }

handler 识别该错误类型后返回 409 + 业务码 CodeLiveMaterialExists = 40901 + data 里带上已有素材——用户录入重复素材时立刻看到「这个链接已录入过,名字叫什么」,而不是一句干瘪的「已存在」。这是 response.Conflict 自定义 code 通道(第 16 章)的标准用例。

Update 仅允许改 name/remark(service 注释「仅允许修改」,仓储层 Select 限定双保险,第 15 章);Delete 物理删除并级联删项目;RetryASR 仅 failed 状态可重试(ErrASRRetryOnlyFailed),重置 pending 后靠 Worker 扫库自动重跑;DownloadASRSubtitle 要求 ASR 已完成且段落非空,返回 TXT 内容与建议文件名。

实现

错误组织法值得单独说:service 文件顶部集中声明哨兵错误(ErrLiveMaterialNotFound 等 8 个)+ 一个结构化错误类型 + 一个业务码常a量。效果是:

  • handler 可以 errors.As 精确分流(NotFound→404、Exists→409+data、校验错→400、其他→500),错误处理集中在 handler 的一个 switch 里;
  • 错误文案与错误身份绑定,不会出现「同一个错误两处两种说法」;
  • 业务码 40901 紧挨着错误类型定义,前后端契约(前端 businessCodes.ts 对应处理)在同一处可查。

其余实现细节:

  • LiveMaterialListOptions 接收字符串日期(2006-01-02 布局),service 层解析为时间指针后交仓储——解析失败容错为 nil(不筛选)而非报错,列表接口对脏参数宽容。
  • 列表查询转仓储的 LiveMaterialListFilter,其中 ASRKeywords 走 jsonb 文本模糊匹配(第 15 章),素材列表因此可以「搜转写内容」。
  • Get 返回完整实体含 live_asr 原始 JSON,供调试页展示;List 则瘦身。
  • handler 侧 live_material_test.go 覆盖创建校验、冲突、retry 状态机等分支,service 用 stub 仓储。

📌 设计决策

  • url_type 服务端识别:客户端传类型必然出现「m3u8 标错成 file」的脏数据,识别规则集中在 detectLiveURLType 一处,未来可扩展(如识别 flv)。
  • 冲突返回已有记录:把「错误」升格为「信息」,用户免于重复录入的来回试错;代价是唯一冲突必须靠 DB 约束兜底(先查后插在并发下有窗口)。
  • RetryASR 重置而非直接执行:所有异步动作统一走「改状态 + 唤醒 Worker」模式,service 永不直接执行长任务。

代码示例

冲突错误的定义与判定(完整保留语义):

// internal/service/live_material_service.go(节选)
// CodeLiveMaterialExists 创建素材时记录已存在的业务错误码。
const CodeLiveMaterialExists = 40901

// LiveMaterialExistsError 创建时发现素材已存在;携带已有完整记录供接口返回。
type LiveMaterialExistsError struct {
   
    Material *model.LiveMaterial
    Cause    error
}

func (e *LiveMaterialExistsError) Error() string {
    /* 委托 Cause 或默认文案 */ }
func (e *LiveMaterialExistsError) Unwrap() error {
    return e.Cause }

服务接口的注释即业务规则书:

// LiveMaterialService 直播素材业务接口(节选)
type LiveMaterialService interface {
   
    // Create 创建直播素材,createdBy 来自 JWT 当前用户;分辨率由 ASR 预处理 ffprobe 回写。
    // url_type 由后台根据 live_url 自动识别(含 .m3u8 → m3u8,否则 file),不由客户端传入。
    Create(ctx context.Context, createdBy uint, name, liveURL, remark, ext string) (*model.LiveMaterial, error)
    // Update 更新直播素材,仅允许修改 name、remark。
    Update(ctx context.Context, id uint, name, remark string) (*model.LiveMaterial, error)
    // RetryASR 将失败的 ASR 重置为 pending,由后台 Worker 扫库重试。
    RetryASR(ctx context.Context, id uint) (*model.LiveMaterial, error)
    // DownloadASRSubtitle 返回可用于直接下载的 ASR 字幕 TXT 与建议文件名。
    DownloadASRSubtitle(ctx context.Context, id uint) (content []byte, fileName string, err error)
}

小结

  • 素材服务 = CRUD + 三态唯一冲突 + 两个动作接口(retry、字幕导出)。
  • 哨兵错误 + 结构化冲突错误 + 业务码常量的三件套,是前后端错误契约的范本。
  • 一切异步动作走「改状态 + Enqueue 唤醒」,service 不碰长任务。

思考题

  1. 冲突检测依赖 DB 唯一约束,若两个请求同时插入同名素材,第二个的错误路径如何保证 Material 字段非空?
  2. 素材删除级联删项目但任务表存了 live_name 快照——被删素材的历史任务还能正确展示吗?试论证。

项目信息

  • GitHub仓库:Chyona/live-mixer
相关文章
|
7天前
|
存储 JSON 前端开发
【直播切片工作台】第18章:认证与账号服务
本章详解认证与账号服务:登录防枚举(统一错误文案)、bcrypt密码安全三件套、逗号分隔角色模型、管理员创建账号、JWT携带用户信息免二次请求,以及stub驱动的service层测试实践。(239字)
|
2天前
|
应用服务中间件 API 语音技术
【直播切片工作台】第22章:素材下载与 URL 重写
本章详解素材下载核心链路:基于 `urlrewrite.Rewriter` 实现 Host 精确映射(如 `cdn.com → cdn-internal.com`),保障签名 URL 兼容;`FileDownloader` 流式落盘、错误上下文化、目录自动创建。改写失败静默降级,公网直连永为兜底——所有流水线的统一入口。
|
6天前
|
前端开发 应用服务中间件 API
【直播切片工作台】第19章:路由注册与版本分组
本章详解Go微服务中路由注册与版本管理:采用函数式批量注册、JWT鉴权分组、规避`:id`匹配陷阱;v1/v2并行演进,API结构清晰可溯,是理解接口全景与演进策略的关键章节。(239字)
|
9天前
|
JSON 缓存 前端开发
【直播切片工作台】第16章:统一响应封装
本章详解 `internal/pkg/response`——仅89行代码定义全API统一响应契约:`{code, message, data}`信封、语义化错误函数(BadRequest/NotFound等)、标准化分页结构。HTTP状态码与业务code双层编码,中文message直通前端toast,零依赖、易测试,是前后端协作的基石。(239字)
|
10天前
|
SQL JSON 安全
【直播切片工作台】第15章:仓储层设计与任务抢占
本章详解仓储层设计规范:以`ClaimPendingByType`乐观锁抢占为核心,通过具名业务接口、SQL级状态守卫、Select限定更新、KeywordGroups统一检索及版本控制写回,保障多实例并发安全与数据一致性。(239字)
|
3月前
|
运维 数据可视化 Serverless
DeepSeek云端部署全流程指南:阿里云提供6种方法部署满血版或蒸馏版DeepSeek,新手0基础教程
DeepSeek-R1是主打强逻辑推理的开源大模型,在数学推演、代码生成、复杂长文本问答、多步骤逻辑任务上表现突出,仅需少量标注数据即可实现高精度推理输出。针对不同开发人群、业务规模、预算成本需求,阿里云完整提供六大差异化部署方案,覆盖MaaS托管API、PaaS可视化一键部署、IaaS自主集群推理三层架构,分别适配满血完整版大参数模型与轻量化蒸馏小模型,最快5分钟即可完成线上调用,最低成本仅十几元即可搭建专属推理服务,无任何开发经验的新手也能快速落地。本文完整拆解六大部署路径的适用人群、部署耗时、预估成本、核心优势,配套Python调用代码、vLLM推理启动命令、Serverless部署脚本
479 2
|
11月前
|
JSON API 数据安全/隐私保护
【剪映小助手】批量向现有草稿中添加音频素材
批量向现有草稿中添加音频素材。该接口支持添加多个音频文件到剪映草稿,为视频创建背景音乐、音效、旁白等音频内容。音频将被添加到独立的音频轨道中,不会影响视频内容。
|
5月前
|
缓存 编解码 运维
【开源剪映小助手】核心功能特性
capcut-mate 是面向剪映(CapCut)的自动化脚本与API平台,基于FastAPI构建,支持草稿管理、多素材批量装配(视频/音频/图片/字幕/特效)、编辑效果配置及异步视频生成,兼具本地缓存与剪映自动化导出能力。
|
7月前
|
人工智能 自然语言处理 数据可视化
阿里云百炼上线DeepSeek-V4:API 价格与官网一致,百万Tokens输入最低1元、输出最低2元
阿里云百炼正式推出DeepSeek-V4系列模型,包含DeepSeek-V4-pro和DeepSeek-V4-flash两款,全模型享受低至4.5折优惠,API价格与DeepSeek官网一致,百万Tokens输入最低1元、输出最低2元。该系列模型覆盖全面、成本优化显著且使用便捷,支持跨模型使用,承诺消费越高、周期越长,折扣越大。活动提供多种包月、包季套餐及特惠价格,满足不同用户需求,助力企业和开发者轻松实现AI落地,广泛应用于电商营销、广告创作、短剧漫剧、AI Coding、智能客服及AI办公等领域。
10112 4
|
7月前
|
缓存 监控 API
【开源剪映小助手】媒体处理功能
CapCut Mate是基于剪映的专业视频编辑辅助工具,提供视频、音频、图片、字幕的智能添加、处理与时间线管理。采用FastAPI架构,集成UI自动化控制、多级缓存及异步任务调度,支持微秒级精度编排与完善错误恢复,兼顾高性能与高可用性。(239字)

热门文章

最新文章