WebScreen 高光时刻 MP4 录制 Spec
1. 目标
WebScreen 根据 SaaS 高光记录,把每个 beginTime/endTime 时间段录制成独立 MP4。
已确认:
- 一个课堂可以有多条高光。
- 一个高光时间段生成一个 MP4;N 条高光生成 N 个文件。
- 当前只在
xdyui2测试,验证后再增加 CrazyTalk。 - WebScreen 只生成本地文件;服务器已有任务定时移动到 OSS。
- 原定时任务每天
07:57调用GET /recording。 - 任务模式暂时只处理
status=0;后端状态回写规则待确认。
2. 兼容原则
高光开关缺失或关闭时,GET /recording 完整执行原整课录制逻辑。
{
"HIGHLIGHTCONFIG": {
"enabled": false
}
}
只有显式配置 enabled: true 才进入高光录制。因此旧服务器、旧站点和旧客户不受影响。
以下接口保持原行为:
POST /recordingPOST /recordingTaskPOST /fileExistsPOST /mp4record/recording/:id
高光文件查询使用独立接口 POST /highlight/fileExists,不改变原 /fileExists 单文件响应。
3. 定时触发
WebScreen 本身不创建 cron。服务器已有 cron:
57 7 * * * wget http://127.0.0.1:3001/recording &
建议部署时改成不落 wget 临时文件的等价写法:
57 7 * * * wget -qO- http://127.0.0.1:3001/recording >/dev/null 2>&1
GET /recording 读取 HIGHLIGHTCONFIG.sourceMode:
-
site:全量高光模式。 -
task:指定课堂任务模式。
这里“全量”表示录制指定站点前一天的全部高光,不表示录制整堂课堂。
4. 配置
当前 xdyui2 测试配置:
{
"HIGHLIGHTCONFIG": {
"enabled": true,
"sourceMode": "site",
"siteIds": ["xdyui2"],
"apiBaseUrl": "https://saas.xuedianyun.com",
"pageSize": 100,
"taskPageSize": 100,
"maxPages": 1000,
"maxConcurrent": 2,
"maxDurationMs": 21600000,
"apiTimeoutMs": 10000,
"apiRetryCount": 2,
"apiRetryBaseDelayMs": 500,
"loadGraceMs": 60000,
"endGraceMs": 10000,
"taskRetentionMs": 86400000,
"outputNamespace": "",
"outputBaseUrl": "https://xdymp4.xuedianyun.com"
}
}
BACKMEDIACONFIG.url 继续由服务器配置决定,代码不硬编码 dev、devback 或 release。
5. 全量高光模式
配置:
"sourceMode": "site"
流程:
- cron 调用
GET /recording。 - 按 Asia/Shanghai 计算前一天
00:00:00.000至23:59:59.999。 - 遍历
HIGHLIGHTCONFIG.siteIds。 - 分页调用
getBySitePrivate.do。 - 每条有效高光加入录制队列。
- 一个时间段生成一个本地 MP4。
接口:
POST /3m/api/highlight/getBySitePrivate.do
Content-Type: application/x-www-form-urlencoded
签名:
authId = MD5(siteId + timestamp)
6. 指定课堂任务模式
配置:
"sourceMode": "task"
流程:
- 分页调用
getRecordingTasksPrivate.do。 - 只保留
status=0 && onlyHighlight=1。 - 只保留
siteIds白名单内的任务。 -
taskList.meetingNumber作为classId。 - 调用
getByClassPrivate.do获取该课堂全部高光。 - 每条高光加入录制队列。
任务接口签名:
authId = MD5(pageNo + pageSize + timestamp)
课堂高光接口签名:
authId = MD5(classId + timestamp)
代码必须保留:
// TODO: 等后端明确录制任务状态流转及完成回写接口。
任务状态回写接口确定前,使用内存队列、本地文件和 OSS 文件共同避免重复录制。
7. 字段映射
SaaS 高光记录:
{
"id": 4,
"meetingNumber": "1486758620",
"siteId": "xdyui2",
"beginTime": 1779159793000,
"endTime": 1779159893000
}
WebScreen 内部统一为:
highlightId = id
classId = meetingNumber
校验:
-
id为正整数。 -
meetingNumber、siteId只包含安全字符。 - 时间为 13 位毫秒时间戳。
-
endTime > beginTime。 - 时长不超过
maxDurationMs。 - 站点必须属于
HIGHLIGHTCONFIG.siteIds。
唯一键:
siteId:highlightId
8. 回放录制地址
使用 URLSearchParams 在现有 BACKMEDIACONFIG.url 上增加:
classId={meetingNumber}
recordMp4=true
playRecord=1
recBeginTime={beginTime}
recEndTime={endTime}
recBeginTime 和 recEndTime 都是绝对毫秒时间戳,不换算为相对秒数。
调用 web_capture_c 时使用参数数组和 spawn(..., { shell: false }),不把接口字段拼接进 shell 命令。
9. 文件与上传
文件名:
{classId}_highlight_{highlightId}.mp4
路径:
本地:media/{siteId}/{yyyyMMdd}/{classId}_highlight_{highlightId}.mp4
OSS: oss/{siteId}/{yyyyMMdd}/{classId}_highlight_{highlightId}.mp4
URL: https://xdymp4.xuedianyun.com/oss/{siteId}/{yyyyMMdd}/{fileName}
日期按高光 beginTime 的 Asia/Shanghai 日期计算。
录制过程先写入 PROJECTCATALOG/.highlight_tmp,完成后原子移动到 media,避免 OSS 搬运程序读取半成品。队列完成后在涉及的日期目录写入 download.json,兼容原搬运机制。
WebScreen 不主动上传 OSS。
10. 队列和去重
- 全局并发由
maxConcurrent控制。 - 同一课堂高光串行,避免同时加载同一课堂回放。
- 不同课堂可以并行。
- 队列中已有相同
siteId:highlightId时不重复加入。 - 本地最终文件存在时不重复录制。
- OSS 文件存在时不重复录制。
- OSS 查询异常时停止本轮处理,不能把查询失败当作文件不存在。
11. 高光多文件查询
POST /highlight/fileExists
Content-Type: application/json
请求:
{
"siteId": "xdyui2",
"classId": "1486758620"
}
WebScreen 调用 getByClassPrivate.do 获取该课堂全部高光,再逐条检查本地任务状态和 OSS。
响应:
{
"code": 0,
"message": "文件已生成",
"onlyHighlight": 1,
"classUrlList": [
{
"highlightId": 4,
"classId": "1486758620",
"siteId": "xdyui2",
"beginTime": 1779159793000,
"generated": true,
"status": "generated",
"url": "https://xdymp4.xuedianyun.com/oss/xdyui2/20260519/1486758620_highlight_4.mp4"
}
]
}
全部生成时 code=0;无文件或存在未生成文件时 code=1,但仍返回每条记录状态。
12. 验收标准
-
xdyui2一个课堂有 N 条高光时生成 N 个不同 MP4。 - 每个录制 URL 包含正确的
recBeginTime/recEndTime。 - 重复执行 cron 不重复录制本地或 OSS 已存在文件。
-
sourceMode=site只查询前一天 xdyui2 高光。 -
sourceMode=task只处理status=0 && onlyHighlight=1。 -
/highlight/fileExists返回课堂全部高光文件状态和地址。 - 原
/fileExists响应不变。 - 删除或关闭
HIGHLIGHTCONFIG后,GET /recording恢复原整课录制。
13. 已知待办
- 后端确认任务领取、成功、失败状态及回写接口。
- xdyui2 验证通过后再增加 CrazyTalk 的准确
siteId。 - 正式部署前确认服务器 OSS 搬运任务会处理新文件名和
download.json。