Go to file
nanxun f9c7ec5d43 fix: resolve low-storage deadlock by always resuming MP4 finalization
Under the Red storage tier, MP4 finalization (TS->MP4 remux) was being skipped, so tasks never reached Completed and the segment_completed event script — which uploads the file and deletes the local source to free space — never ran. The disk could never recover, deadlocking all recording and transcoding.

Two reversed checks caused this: (1) FfmpegService gated finalization on the legacy HasEnoughSpace MB threshold (effectively 4GB) instead of the tier system, and (2) the polling loop only resumed paused finalizations when NOT in the Red tier. Now finalization is gated solely on ShouldPauseActive (true Red only) and the polling loop always attempts to resume it every cycle, since finalization is the very mechanism that frees space. Once any segment finalizes, the upload+delete script runs and the disk recovers, letting the rest finish.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 15:13:07 +08:00
docs feat: migrate runtime to postgresql 2026-04-29 18:47:12 +08:00
frontend fix: avoid danmaku event name collision in session detail replay 2026-06-11 17:52:18 +08:00
mobile fix: track flutter mobile data layer 2026-05-17 15:24:28 +08:00
scripts build: add one-shot ARM64 image build/export script 2026-06-12 23:04:27 +08:00
src fix: resolve low-storage deadlock by always resuming MP4 finalization 2026-06-16 15:13:07 +08:00
tests/LiveRecorder.Tests feat: add database circuit breaker and health-readiness endpoint 2026-06-11 17:52:18 +08:00
.dockerignore feat: improve recording automation and task workflows 2026-04-23 23:18:11 +08:00
.gitignore build: add one-shot ARM64 image build/export script 2026-06-12 23:04:27 +08:00
docker-compose.yml feat: migrate runtime to postgresql 2026-04-29 18:47:12 +08:00
Jenkinsfile fix: avoid jenkinsfile label mojibake 2026-05-31 19:53:01 +08:00
LiveRecorder.sln feat: expand platform adapters and preview tooling 2026-05-13 19:40:43 +08:00
README.md docs: 完善 README 并修复暗黑模式左侧导航栏可见性 2026-04-27 23:02:22 +08:00

Live Recorder

Live Recorder 是一个多平台直播录制系统,支持自动检测开播、实时录制、弹幕采集、通知推送与事件脚本编排。

  • 后端:.NET 8 Web API + EF Core + SQLite
  • 前端:Vue 3 + TypeScript + Element Plus

当前已适配 抖音DouyinBilibili(部分),架构将平台特有逻辑隔离在 Platforms 目录下,新增虎牙、斗鱼、快手等平台无需改动应用层服务。

目录结构

.
├─ 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(分段)两种输出格式
  • 内置三种录制模板:StreamCopyBalancedMp4ArchiveTs
  • 断线重连:可配置重连开关、最大重连延迟、读写超时
  • 存储守护:启动前检查磁盘剩余空间,不足时拒绝录制

弹幕采集

弹幕通过 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 拆分

适配器架构

平台适配器接口

ILivePlatformAdapter
├─ ParseRoomAsync(input)       # 解析分享链接、提取房间 ID
├─ GetLiveStatusAsync(roomId)  # 获取直播状态、标题、主播名、封面
└─ GetStreamUrlAsync(roomId)   # 获取推流地址

ILiveDanmakuAdapter
├─ CanHandle(platformType)     # 判断是否支持该平台
└─ ConnectAsync(context)       # 建立弹幕 WebSocket 连接

ILiveDanmakuConnection
└─ StartAsync(onEvent)         # 开始接收弹幕,通过回调推送事件

抖音适配器

  • DouyinHttpClient:封装 User-Agent、Referer、Cookie、ttwid 管理
    • 分享链接解析、重定向跟随
    • 从 HTML 中提取 roomIduser_unique_id、主播名、标题(支持新版 __pace_f 数据结构)
    • 调用 webcast/room/web/enter/ 获取直播间元信息
    • 调用 webcast/im/fetch/ 获取弹幕 WebSocket 连接参数
    • a_bogus 签名:通过 Node.js 子进程调用 abogus.js 计算请求签名
  • DouyinLivePlatformAdapter:解析推流地址,选择画质
  • DouyinDanmakuAdapterWebSocket 弹幕连接管理、心跳保活、自动重连

Bilibili 适配器

  • BilibiliHttpClientWbi 签名、分享链接解析
  • 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 配置
开播通知 / 异常通知 分别控制是否推送

事件脚本设置

设置 说明
启用事件脚本 总开关
开播脚本 / 下播脚本 / 分片完成脚本 分别控制与配置
脚本模式 内联脚本 / 外部文件
超时(秒) 脚本执行超时限制

保留策略

设置 说明
启用保留清理 是否开启自动清理
保留天数 超过此天数的记录将被清理
同时删除文件 是否删除对应的视频与弹幕文件

运行

后端

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

前端

cd frontend
npm install
npm run dev

前端开发服务器:

Docker 部署

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 buildfrontend 目录)
  • Docker Compose 完整部署验证通过

后续规划

  1. 完善 Bilibili 全功能适配,新增虎牙、斗鱼、快手适配器
  2. 按直播间独立配置录制参数
  3. 日志全文搜索与时间范围过滤
  4. 基于 JWT 或外部 SSO 替换轻量 Token 会话
  5. 弹幕回放播放器集成
  6. 录制文件自动上传至 S3/OSS 等对象存储