# Live Recorder Live Recorder 是一个多平台直播录制系统,支持自动检测开播、实时录制、弹幕采集、通知推送与事件脚本编排。 - 后端:`.NET 8 Web API + EF Core + SQLite` - 前端:`Vue 3 + TypeScript + Element Plus` 当前已适配 **抖音**(Douyin)与 **Bilibili**(部分),架构将平台特有逻辑隔离在 `Platforms` 目录下,新增虎牙、斗鱼、快手等平台无需改动应用层服务。 ## 目录结构 ```text . ├─ src │ ├─ LiveRecorder.Domain # 实体与枚举 │ ├─ LiveRecorder.Application # 接口、DTO、应用服务 │ ├─ LiveRecorder.Infrastructure # 持久层、平台适配器、后台服务 │ └─ LiveRecorder.WebApi # REST API 与中间件 ├─ frontend # Vue 3 SPA │ └─ src │ ├─ api / components / composables / router / stores / styles / views ├─ docker-compose.yml ├─ Jenkinsfile └─ README.md ``` ## 核心功能 ### 直播录制 - 支持分享链接与短链解析,自动提取真实房间号 - 后台轮询检测直播状态,可配置轮询间隔与自动录制开关 - 支持 `MP4`(单文件)与 `TS`(分段)两种输出格式 - 内置三种录制模板:`StreamCopy`、`BalancedMp4`、`ArchiveTs` - 断线重连:可配置重连开关、最大重连延迟、读写超时 - 存储守护:启动前检查磁盘剩余空间,不足时拒绝录制 ### 弹幕采集 弹幕通过 WebSocket 实时连接各平台的弹幕服务器,以 XML 格式分段写入文件。 - **抖音弹幕**:通过 `webcast/im/fetch/` 获取连接信息,建立 WSS 长连接,使用 `a_bogus` 签名算法(基于 `dycast` 开源项目的 `abogus.js`),支持 Protobuf 解码 - **Bilibili 弹幕**:通过 Bilibili 直播 WebSocket 协议连接弹幕服务器,支持聊天、进入、关注、礼物等事件 - 弹幕事件类型:聊天(chat)、礼物(gift)、点赞(like)、进入(enter)、关注(member)等 - 弹幕录制可按设置只保留聊天弹幕或包含所有事件类型 - 弹幕文件与视频文件同目录、同名(`.xml` 扩展名),内部以结构化 XML 保存 ### 通知推送 - **邮件通知**:支持 SMTP 配置、SSL、自定义发件人与 HTML 模板 - 开播提醒:直播间上线时发送 - 异常提醒:录制异常时发送 - 模板变量:`{{appName}}`、`{{anchor}}`、`{{title}}`、`{{summary}}`、`{{detail}}` 等 - **Webhook 通知**:支持自定义 URL、请求头、超时配置 - 开播事件与异常事件分别可独立开关 - 支持测试发送验证连通性 - 负载包含直播间信息、录制任务信息等完整上下文 ### 事件脚本 支持在关键生命周期节点执行自定义脚本(Shell / PowerShell 内联或外部脚本文件): | 事件 | 说明 | |------|------| | `live_started` | 检测到直播上线时触发 | | `live_ended` | 检测到直播下线时触发 | | `segment_completed` | 一个录制分片完成时触发 | 脚本通过环境变量获取上下文信息: ``` LIVE_RECORDER_EVENT # 事件类型 LIVE_RECORDER_PLATFORM # 平台名称 LIVE_RECORDER_LIVE_ROOM_ID # 直播间内部 ID LIVE_RECORDER_ROOM_ID # 平台房间号 LIVE_RECORDER_TITLE # 直播间标题 LIVE_RECORDER_ANCHOR # 主播昵称 LIVE_RECORDER_SOURCE_URL # 源 URL LIVE_RECORDER_OCCURRED_AT_UTC # 事件发生时间(北京时间) LIVE_RECORDER_SEGMENT_FILE_PATH # 分片文件路径(仅 segment_completed) LIVE_RECORDER_DANMAKU_FILE_PATH # 弹幕文件路径(仅 segment_completed) LIVE_RECORDER_DURATION_SECONDS # 分片时长(仅 segment_completed) ``` 支持超时控制与自定义日志输出。 ### 异常恢复(Recovery) - 自动检测处于 Live 状态但未在录制的直播间 - 自动检测录制完成但未做最终化(MP4 remux)的任务 - 支持一键重试录制或逐一恢复 - 后台轮询调度器同时兼具恢复层功能:当 ffmpeg 异常退出而直播间仍在直播时自动重新创建录制任务 ### 数据保留清理(Retention) - 可配置保留天数,超期自动清理录制记录、弹幕文件、视频文件与系统日志 - 支持后台定时清理(每 24 小时执行一次) ### 每日回顾(Daily Review) - 按日期查看当天所有录制会话的汇总信息 - 亮点卡片:最长录制、最多弹幕、最多异常等 - 时间线视图展示会话与分片时间段 - 弹幕数量随时间分布的可视化数据 ### 系统日志 - 持久化系统日志,支持按类别、时间筛选 - 日志关联直播间、录制会话、录制任务 - 可在前端日志页面实时查看 ### 前端界面 - 登录页 - 直播间管理(添加、编辑、删除、状态查看、快速录制) - 录制任务列表与详情 - 录制会话列表与详情 - 日志查看器 - 每日回顾 - 异常恢复页面 - 系统设置(录制、通知、弹幕、事件脚本、保留策略、平台配置) - 路由懒加载,Vite vendor chunk 拆分 ## 适配器架构 ### 平台适配器接口 ```csharp ILivePlatformAdapter ├─ ParseRoomAsync(input) # 解析分享链接、提取房间 ID ├─ GetLiveStatusAsync(roomId) # 获取直播状态、标题、主播名、封面 └─ GetStreamUrlAsync(roomId) # 获取推流地址 ILiveDanmakuAdapter ├─ CanHandle(platformType) # 判断是否支持该平台 └─ ConnectAsync(context) # 建立弹幕 WebSocket 连接 ILiveDanmakuConnection └─ StartAsync(onEvent) # 开始接收弹幕,通过回调推送事件 ``` ### 抖音适配器 - `DouyinHttpClient`:封装 User-Agent、Referer、Cookie、ttwid 管理 - 分享链接解析、重定向跟随 - 从 HTML 中提取 `roomId`、`user_unique_id`、主播名、标题(支持新版 `__pace_f` 数据结构) - 调用 `webcast/room/web/enter/` 获取直播间元信息 - 调用 `webcast/im/fetch/` 获取弹幕 WebSocket 连接参数 - `a_bogus` 签名:通过 Node.js 子进程调用 `abogus.js` 计算请求签名 - `DouyinLivePlatformAdapter`:解析推流地址,选择画质 - `DouyinDanmakuAdapter`:WebSocket 弹幕连接管理、心跳保活、自动重连 ### Bilibili 适配器 - `BilibiliHttpClient`:Wbi 签名、分享链接解析 - `BilibiliLivePlatformAdapter`:直播状态查询,推流地址获取 - `BilibiliDanmakuAdapter`:弹幕 WebSocket 协议解析 ## 设置项速览 ### 录制设置 | 设置 | 说明 | |------|------| | FFmpeg 路径 | ffmpeg 可执行文件路径 | | 输出根目录 | 录制文件输出根目录 | | 输出模板 | 路径模板,支持 `{platform}` `{roomId}` `{anchor}` `{title}` `{yyyy}` `{MM}` `{dd}` `{HHmmss}` 等变量 | | 默认画质 | 优先选择的流画质 | | 保存格式 | MP4 / TS | | 保存模式 | 单文件 / 分段 | | 分段时长 | 分段模式下的每段时长(秒) | | 录制模板 | StreamCopy / BalancedMp4 / ArchiveTs | | 重连开关 | 断线后是否自动重连 | | 最大重连延迟 | 重连退避最大秒数 | | 读写超时 | 网络读写超时秒数 | | 存储守护 | 启用磁盘空间检查 | | 最低剩余空间 | 存储守护的最低空间阈值(GB) | ### 弹幕设置 | 设置 | 说明 | |------|------| | 启用弹幕录制 | 录制时是否同时采集弹幕 | | 包含非聊天事件 | 是否记录进入、关注、礼物等非聊天弹幕 | | 最小轮询间隔 | 弹幕连接重试的最小间隔(毫秒) | | 最大重试延迟 | 弹幕连接重试退避最大秒数 | ### 通知设置 | 设置 | 说明 | |------|------| | 启用邮件通知 | SMTP 邮件通知总开关 | | SMTP 服务器 / 端口 / SSL / 账号 | 邮件服务器配置 | | 开播提醒 / 异常提醒 | 分别控制是否发送 | | HTML 模板 | 可自定义邮件 HTML 模板,支持变量占位 | | 启用 Webhook | Webhook 推送总开关 | | Webhook URL / 请求头 / 超时 | Webhook 配置 | | 开播通知 / 异常通知 | 分别控制是否推送 | ### 事件脚本设置 | 设置 | 说明 | |------|------| | 启用事件脚本 | 总开关 | | 开播脚本 / 下播脚本 / 分片完成脚本 | 分别控制与配置 | | 脚本模式 | 内联脚本 / 外部文件 | | 超时(秒) | 脚本执行超时限制 | ### 保留策略 | 设置 | 说明 | |------|------| | 启用保留清理 | 是否开启自动清理 | | 保留天数 | 超过此天数的记录将被清理 | | 同时删除文件 | 是否删除对应的视频与弹幕文件 | ## 运行 ### 后端 ```powershell dotnet restore LiveRecorder.sln dotnet build LiveRecorder.sln --no-restore -m:1 dotnet run --project src/LiveRecorder.WebApi/LiveRecorder.WebApi.csproj --urls http://localhost:5000 ``` 默认账号: - 用户名:`admin` - 密码:`Admin@123` Swagger: - http://localhost:5000/swagger ### 前端 ```powershell cd frontend npm install npm run dev ``` 前端开发服务器: - http://localhost:5173 ### Docker 部署 ```powershell docker compose up -d ``` - API 服务运行在容器内 `:8080` - Nginx 反向代理将 `/api` 转发到 API,其余请求由前端 SPA 处理 - 默认暴露端口 `HTTP_PORT`(默认 8080) - 数据目录 `./data` 与录制目录 `./records` 映射到宿主机 - 支持多架构构建(`linux/amd64`, `linux/arm64`) ## 验证 - `dotnet build LiveRecorder.sln --no-restore -m:1` - `npm run build`(frontend 目录) - Docker Compose 完整部署验证通过 ## 后续规划 1. 完善 Bilibili 全功能适配,新增虎牙、斗鱼、快手适配器 2. 按直播间独立配置录制参数 3. 日志全文搜索与时间范围过滤 4. 基于 JWT 或外部 SSO 替换轻量 Token 会话 5. 弹幕回放播放器集成 6. 录制文件自动上传至 S3/OSS 等对象存储