feat(media): 支持后台转码配置与缩略图轴
This commit is contained in:
1 parent
70da939a58
commit
316baf28d6
39 files changed
+1523
-38
No files matched your search
+44
-1
@@ -227,7 +227,8 @@ app.register.max-per-device-per-day
|
||||
"unlockCoin": 0,
|
||||
"likeCount": "45",
|
||||
"commentCount": "8",
|
||||
"playUrl": "/files/episode/202610/xxx.mp4?e=1760000000&s=9f2c..."
|
||||
"playUrl": "/files/episode/202610/xxx.mp4?e=1760000000&s=9f2c...",
|
||||
"thumbnailsUrl": "/files/transcoded/88/thumbnails/thumbnails.vtt?e=1760000000&s=8a1d..."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -244,6 +245,7 @@ app.register.max-per-device-per-day
|
||||
| `viewCount` | Long(JSON 字符串) | 取 `drama.view_count`,累计播放量,既有字段语义不变 |
|
||||
| `episodes[].likeCount` | Long(JSON 字符串) | 本集点赞数,取 `drama_episode.like_count` |
|
||||
| `episodes[].commentCount` | Long(JSON 字符串) | 本集未删除评论数,按 `episode_id` 统计 |
|
||||
| `episodes[].thumbnailsUrl` | String / null | 本集雪碧图 WebVTT 时间轴签名地址;没有 READY 产物时为 `null` |
|
||||
|
||||
计数是只读快照:互动操作完成后客户端重新拉取详情即可刷新;`Long` 字段一律按字符串下发,
|
||||
客户端需要转成数字再展示(与 `viewCount` 相同)。
|
||||
@@ -263,6 +265,7 @@ app.register.max-per-device-per-day
|
||||
"accessType": "FREE",
|
||||
"playUrl": "/files/episode/202610/xxx.mp4?e=1760000000&s=9f2c...",
|
||||
"qualities": ["AUTO"],
|
||||
"thumbnailsUrl": "/files/transcoded/88/thumbnails/thumbnails.vtt?e=1760000000&s=8a1d...",
|
||||
"unlockCoin": 0,
|
||||
"previewSeconds": 0,
|
||||
"likeCount": "45",
|
||||
@@ -277,6 +280,10 @@ app.register.max-per-device-per-day
|
||||
**判定顺序由服务端决定,客户端不得自行推断可否播放。** 签名默认 15 分钟有效;
|
||||
开启 `fruitshorts.media.signature-required` 后,`/files/episode/**` 没带有效签名会返回 403。
|
||||
|
||||
`thumbnailsUrl` 指向 WebVTT:cue 链接为同目录相对雪碧图
|
||||
`sprite_XXX.jpg#xywh=x,y,w,h`。有 READY `media_thumbnail` 时返回签名地址,
|
||||
无产物时为 `null`;该字段不影响 `playUrl`、`qualities` 和 HLS/原文件回落。
|
||||
|
||||
### 1.5 观看记录与收藏(已实现)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
@@ -597,6 +604,42 @@ GET /app/notices?pageIndex=1&pageSize=20
|
||||
**上传会话归属**:`uploadId` 相当于上传凭证,`chunk` / `complete` / 进度查询都校验会话发起人,
|
||||
别人拿到 `uploadId` 也塞不进分片(`{"code":403,"message":"无权操作该上传会话"}`)。
|
||||
|
||||
**转码档位配置**
|
||||
|
||||
| 方法 | 路径 | 权限点 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| GET | `/admin/media/transcode-profile` | `episode:upload` | 读取当前配置;缺省返回四档默认值 |
|
||||
| PUT | `/admin/media/transcode-profile` | `episode:upload` | 保存配置并记录操作日志 |
|
||||
|
||||
配置保存在 `sys_config` 的 `media.transcode.profile`,JSON 结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"segmentSeconds": 6,
|
||||
"ladder": [
|
||||
{
|
||||
"quality": "360p",
|
||||
"width": 640,
|
||||
"height": 360,
|
||||
"videoBitrateKbps": 400,
|
||||
"audioBitrateKbps": 64,
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"sprite": {
|
||||
"enabled": true,
|
||||
"intervalSeconds": 1,
|
||||
"columns": 10,
|
||||
"thumbWidth": 160
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`quality` 必须唯一且非空,宽高必须为大于 0 的偶数,音视频码率必须大于 0,
|
||||
至少启用一档;`segmentSeconds` 2–10、`intervalSeconds` 1–10、`columns` 5–20、
|
||||
`thumbWidth` 120–240。非法请求返回统一错误码 `400` 且不写库。
|
||||
|
||||
### 2.3 App 用户管理(已实现)
|
||||
|
||||
| 方法 | 路径 | 权限点 | 说明 |
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# 转码档位配置、雪碧图与播放缩略图轴
|
||||
|
||||
## 目标
|
||||
|
||||
1. 后台可配置转码档位与雪碧图参数,worker 对之后的新任务在 30 秒内自动生效。
|
||||
2. 转码成功后生成雪碧图与 VTT,登记到 `media_thumbnail`。
|
||||
3. 播放接口与详情剧集返回 `thumbnailsUrl`,无产物时为 `null`。
|
||||
|
||||
## 配置模型
|
||||
|
||||
- `sys_config.config_key = media.transcode.profile`,值为 JSON。
|
||||
- 配置包含总开关、分片秒数、码率阶梯与雪碧图参数。
|
||||
- 缺省配置等价于现有 360p/480p/720p/1080p 四档。
|
||||
- 后台 GET 缺省或历史配置非法时返回 yml 等价默认值;PUT 非法时返回统一 400,且不写库。
|
||||
- worker 读取配置缓存 30 秒;缺省、解析失败或校验失败时回退 yml 默认阶梯。
|
||||
|
||||
## 权限取舍
|
||||
|
||||
现有权限点没有 `media:transcode`。本次两个接口复用最小可用的
|
||||
`episode:upload`,避免新增权限点导致已有角色需要补授权;不新增菜单或权限 SQL。
|
||||
|
||||
## 数据与产物
|
||||
|
||||
- 新增 `media_thumbnail`,按 `episode_id` 唯一,记录 VTT、雪碧图前缀、采样参数、
|
||||
尺寸、帧数、时长、状态、错误与总大小。
|
||||
- 雪碧图目录固定在 `transcoded/{taskId}/thumbnails/`:
|
||||
`thumbnails.vtt` + `sprite_001.jpg` 等多张图。
|
||||
- VTT cue 链接使用相对路径 `sprite_XXX.jpg#xywh=x,y,w,h`,同一 VTT 目录下可直接解析。
|
||||
- 缩略图失败只把 `media_thumbnail.status` 置 `FAILED` 并记录错误;主 HLS 仍成功。
|
||||
|
||||
## 播放
|
||||
|
||||
- `PlayRespVO` 和 `AppEpisodeVO` 增加 `thumbnailsUrl`。
|
||||
- 查询 `media_thumbnail` 仅读取 `READY` 产物;objectKey 经
|
||||
`MediaStorageService.toPublicUrl` 后使用现有 `PlaybackSigner` 签名。
|
||||
- 详情剧集列表批量查询缩略图,禁止 N+1。
|
||||
- 不改变现有 HLS/原文件回落与 qualities 列表。
|
||||
|
||||
## 测试
|
||||
|
||||
- worker:配置解析、非法配置回退 yml、30 秒缓存、VTT 坐标、多雪碧图索引、
|
||||
缩略图失败不阻断主转码。
|
||||
- admin:GET 默认值、PUT 非法不落库、PUT 成功落库。
|
||||
- playback:有 READY 缩略图返回签名 `thumbnailsUrl`,无产物返回 `null`。
|
||||
- 门禁:`mvn -pl fruitshorts-transcode-worker,fruitshorts-admin-api,fruitshorts-app-api -am test`。
|
||||
|
||||
## 风险 / 边界
|
||||
|
||||
- 本次只对合并后的新转码任务生效,不回跑历史任务。
|
||||
- 配置中的顶层 `enabled` 会持久化并返回;本单不改变既有队列消费开关语义,
|
||||
worker 仍以 `worker-enabled` 决定是否消费任务。
|
||||
- 雪碧图生成依赖 ffmpeg `tile` 滤镜;真实产物由 worker 门禁和 PM 后续环境验收,
|
||||
本任务不启动服务、不改客户端。
|
||||
@@ -24740,3 +24740,74 @@ mvn -pl fruitshorts-admin-api -am test
|
||||
|
||||
解读:已推送历史不改写;身份合规靠命令级覆盖防复发;本条仅文档留痕,无代码改动。
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-22 | 转码档位配置、雪碧图与播放缩略图轴
|
||||
|
||||
### 开工确认与计划
|
||||
|
||||
- 仓库:FruitServer
|
||||
- 基线:`develop` / `origin/develop` = `70da939`
|
||||
- 工作区:开工前干净
|
||||
- 分支:`feature/media-transcode-profile-sprite`
|
||||
- 计划:`docs/plans/2026-10-22-media-transcode-profile-sprite.md`
|
||||
|
||||
```text
|
||||
$ git status --short --branch && git rev-parse --short HEAD
|
||||
## develop...origin/develop
|
||||
70da939
|
||||
```
|
||||
|
||||
解读:基于指定 develop 基线创建隔离工作树。现有权限点无 `media:transcode`,
|
||||
本次复用 `episode:upload`,不新增菜单或权限初始化数据。
|
||||
|
||||
### 实现与门禁
|
||||
|
||||
- 新增 `media.transcode.profile` JSON 模型、统一校验与 30 秒 worker 缓存;
|
||||
后台 GET 每次直读,PUT 校验通过后才写 `sys_config`。
|
||||
- 新增幂等迁移 `sql/patch-2026-10-22-media-thumbnail.sql`,包含该表、
|
||||
唯一索引、更新时间触发器与默认四档配置;同步 `schema-phase1.sql` / `data-phase1.sql`。
|
||||
- worker 产出 `thumbnails/thumbnails.vtt` 与按需多张 `sprite_%03d.jpg`;
|
||||
失败以 `media_thumbnail.status=FAILED`、`error_msg` 留存,主 HLS 仍成功。
|
||||
- 播放与详情增加 `thumbnailsUrl`;详情按剧集 ID 批量查询后统一签名。
|
||||
- 首次 reactor 测试暴露 4 个既有 App 测试未跟随 `DramaService` 新增
|
||||
`EpisodePriceSync` 构造参数,已在测试准备代码补齐替身,未改内容服务生产逻辑。
|
||||
|
||||
```text
|
||||
$ mvn -pl fruitshorts-transcode-worker,fruitshorts-admin-api,fruitshorts-app-api -am -DskipTests compile
|
||||
[INFO] fruitshorts-module-media ............................ SUCCESS
|
||||
[INFO] fruitshorts-admin-api ............................... SUCCESS
|
||||
[INFO] fruitshorts-app-api ................................. SUCCESS
|
||||
[INFO] fruitshorts-transcode-worker ........................ SUCCESS
|
||||
[INFO] BUILD SUCCESS
|
||||
```
|
||||
|
||||
解读:新增 media→system 依赖和全部生产代码编译通过。
|
||||
|
||||
```text
|
||||
$ mvn -pl fruitshorts-transcode-worker,fruitshorts-admin-api,fruitshorts-app-api -am test
|
||||
[INFO] Tests run: 41, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] Tests run: 103, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] Tests run: 19, Failures: 0, Errors: 0, Skipped: 0
|
||||
[INFO] BUILD SUCCESS
|
||||
```
|
||||
|
||||
解读:admin 41、app-api 103、worker 19 全过;worker 包含真实 ffmpeg smoke,
|
||||
验证 2 秒样本产出 HLS、封面、VTT 与 `sprite_001.jpg`。
|
||||
|
||||
```text
|
||||
$ mvn test
|
||||
[INFO] Reactor Summary for fruitshorts 1.0.0-SNAPSHOT:
|
||||
[INFO] fruitshorts-admin-api .............................. SUCCESS
|
||||
[INFO] fruitshorts-app-api ................................ SUCCESS
|
||||
[INFO] fruitshorts-transcode-worker ....................... SUCCESS
|
||||
[INFO] BUILD SUCCESS
|
||||
[INFO] Total time: 24.439 s
|
||||
$ sed -n '38,43p' sql/patch-2026-10-22-media-thumbnail.sql | ... | jq -e '.ladder | length == 4'
|
||||
true
|
||||
```
|
||||
|
||||
解读:仓库根全量 14 个 reactor 模块测试通过。迁移脚本按要求只写文件、
|
||||
默认配置 JSON 解析为 4 档;未执行数据库变更;未启动 8080/8081/worker,
|
||||
未操作真机,未改 Web/Flutter。
|
||||
|
||||
Reference in new issue
Block a user