feat(media): C1 转码 worker(独立进程消费 transcode_task,产出四档 HLS + 封面)

为什么:codex-研发任务清单阶段 C 第一项要求消费 transcode_task 产出多码率 HLS + 截图;
勘察发现除 worker 外还缺两件——TranscodeService.enqueue 没有任何调用方(队列永远空)、
media_asset.hls_url/poster_url 没人回写,所以一并补齐,已有能力(条件更新认领 / 3 次重试)直接复用。

- 新增 TranscodeRunner 端口 + FfmpegTranscodeRunner(每档一条 ffmpeg 出 VOD HLS + 手写 master.m3u8 + 抽封面 + ffprobe 读时长)
- 新增 TranscodeWorker:claimNext → 转码 → 登记 media_rendition → 媒资 ready;失败记原因、第 3 次才落 FAILED 并把媒资置 failed
- 存储统一走 MediaStorageService(新增 fileFor / objectKeyOf,含目录穿越校验),worker 不自己拼磁盘路径
- 队列隔离:app-api/admin-api 默认 worker-enabled=false,新增独立模块 fruitshorts-transcode-worker(随机端口)
- MediaService 上传完成对视频素材入队并置 transcoding(图片仍直接 ready)
- 实测抓到并修掉两个真问题:① scale 未加 force_divisible_by=2 时 854x480 算出奇数宽 853,libx264 拒绝转码
  ② 只写 -hls_time 不强制关键帧时 8 秒片源只出 1 个分片,补 -force_key_frames 后按 6s 正确切分
- README 开发环境补 ffmpeg 9.0.2(只有 worker 需要)
- 实测:mvn test 83 条全过、0 skipped(新增 worker 12 条,含真实 ffmpeg smoke);
  端到端:正常源 SUCCESS + 4 档 rendition + 媒资 ready;坏源重试 3 次后 FAILED
This commit is contained in:
jianghaifeng committed 2026-10-16 12:41:48 +08:00
1 parent 1dc97b4bf6
commit 9f76302e53
15 files changed
+1136 -3

No files matched your search

@@ -0,0 +1,51 @@
# Author: fruit.com.cn
#
# 转码 worker 独立进程:只做队列消费,不对外提供接口。
# 端口用 0(随机)——本进程不需要被访问,也不会跟 8080/8081 抢端口。
server:
port: 0
spring:
application:
name: fruitshorts-transcode-worker
autoconfigure:
exclude: org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:5432/fruitshorts
username: ${DB_USER:fruitshorts}
password: ${DB_PASSWORD:fruitshorts}
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
banner: false
db-config:
# 与两个入口保持一致:主键由应用侧雪花生成(S0),否则 worker 插入 media_rendition 会没有 id
id-type: assign_id
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
sequence:
# 单机固定值;多实例部署前改成按实例分配
worker-id: 2
datacenter-id: 1
fruitshorts:
media:
# 必须与 app-api/admin-api 指向同一个存储根,worker 才找得到上传后的源文件
root-dir: ./data/uploads
public-prefix: /files
transcode:
# 只有本进程打开转码(§8.5 隔离要求)
worker-enabled: true
poll-delay-ms: 3000
ffmpeg-path: ${FFMPEG_PATH:ffmpeg}
ffprobe-path: ${FFPROBE_PATH:ffprobe}
segment-seconds: 6
poster-second: 1
logging:
level:
com.fruitshorts: info