docs: 完善 README 并修复暗黑模式左侧导航栏可见性

This commit is contained in:
2026-04-27 23:02:22 +08:00
parent 89ba4163fc
commit c161660b51
2 changed files with 224 additions and 138 deletions
+212 -137
View File
@@ -1,157 +1,217 @@
# Live Recorder # Live Recorder
Live Recorder is a multi-platform live recording system built with: Live Recorder 是一个多平台直播录制系统,支持自动检测开播、实时录制、弹幕采集、通知推送与事件脚本编排。
- Backend: `.NET 8 Web API + EF Core + SQLite` - 后端:`.NET 8 Web API + EF Core + SQLite`
- Frontend: `Vue 3 + TypeScript + Element Plus` - 前端:`Vue 3 + TypeScript + Element Plus`
The current production-ready adapter is `DouyinLivePlatformAdapter`. The architecture keeps platform-specific logic isolated so Bilibili, Huya, Douyu, and Kuaishou can be added without changing application services. 当前已适配 **抖音**Douyin)与 **Bilibili**(部分),架构将平台特有逻辑隔离在 `Platforms` 目录下,新增虎牙、斗鱼、快手等平台无需改动应用层服务。
## Architecture ## 目录结构
```text ```text
. .
├─ src ├─ src
│ ├─ LiveRecorder.Domain │ ├─ LiveRecorder.Domain # 实体与枚举
│ ├─ Entities │ ├─ LiveRecorder.Application # 接口、DTO、应用服务
│ └─ Enums ├─ LiveRecorder.Infrastructure # 持久层、平台适配器、后台服务
─ LiveRecorder.Application ─ LiveRecorder.WebApi # REST API 与中间件
│ │ ├─ Abstractions ├─ frontend # Vue 3 SPA
│ ├─ Common └─ src
├─ Models ├─ api / components / composables / router / stores / styles / views
│ │ └─ Services ├─ docker-compose.yml
│ ├─ LiveRecorder.Infrastructure ├─ Jenkinsfile
│ │ ├─ Persistence └─ README.md
│ │ ├─ Platforms
│ │ │ ├─ Douyin
│ │ │ ├─ Bilibili
│ │ │ └─ Huya
│ │ └─ Services
│ └─ LiveRecorder.WebApi
│ ├─ Controllers
│ └─ Middleware
└─ frontend
└─ src
├─ api
├─ components
├─ router
├─ stores
└─ views
``` ```
## Core design ## 核心功能
### Domain ### 直播录制
- `LiveRoom`: platform, room id, source URL, title, anchor, cover, availability status - 支持分享链接与短链解析,自动提取真实房间号
- `RecordTask`: recording state, quality, output format, ffmpeg process id, output path - 后台轮询检测直播状态,可配置轮询间隔与自动录制开关
- `RecordResult`: final file or segment directory, file size, duration, final status - 支持 `MP4`(单文件)与 `TS`(分段)两种输出格式
- `SystemLogEntry`: persistent system log - 内置三种录制模板:`StreamCopy``BalancedMp4``ArchiveTs`
- `AppSetting`: runtime settings store - 断线重连:可配置重连开关、最大重连延迟、读写超时
- `UserAccount`, `UserSession`: lightweight token-based sign-in - 存储守护:启动前检查磁盘剩余空间,不足时拒绝录制
### Application ### 弹幕采集
- `ILivePlatformAdapter` 弹幕通过 WebSocket 实时连接各平台的弹幕服务器,以 XML 格式分段写入文件。
- `ParseRoomAsync`
- `GetLiveStatusAsync`
- `GetStreamUrlAsync`
- `ILivePlatformAdapterFactory`
- `LiveRoomService`
- `RecordService`
- `SystemSettingsService`
- `SystemLogService`
- `AuthService`
### Infrastructure - **抖音弹幕**:通过 `webcast/im/fetch/` 获取连接信息,建立 WSS 长连接,使用 `a_bogus` 签名算法(基于 `dycast` 开源项目的 `abogus.js`),支持 Protobuf 解码
- **Bilibili 弹幕**:通过 Bilibili 直播 WebSocket 协议连接弹幕服务器,支持聊天、进入、关注、礼物等事件
- 弹幕事件类型:聊天(chat)、礼物(gift)、点赞(like)、进入(enter)、关注(member)等
- 弹幕录制可按设置只保留聊天弹幕或包含所有事件类型
- 弹幕文件与视频文件同目录、同名(`.xml` 扩展名),内部以结构化 XML 保存
- `DouyinHttpClient` ### 通知推送
- wraps User-Agent, Referer, Cookie, and `ttwid` handling
- `DouyinLivePlatformAdapter`
- share-link resolution, room id extraction, status check, stream selection
- `FfmpegService`
- single file mode, segmented mode, reconnect arguments, recording templates
- `LiveRoomPollingBackgroundService`
- background polling scheduler for live detection and auto-start recording
- `LiveRecorderDbContext`
### Web API - **邮件通知**:支持 SMTP 配置、SSL、自定义发件人与 HTML 模板
- 开播提醒:直播间上线时发送
- 异常提醒:录制异常时发送
- 模板变量:`{{appName}}``{{anchor}}``{{title}}``{{summary}}``{{detail}}`
- **Webhook 通知**:支持自定义 URL、请求头、超时配置
- 开播事件与异常事件分别可独立开关
- 支持测试发送验证连通性
- 负载包含直播间信息、录制任务信息等完整上下文
- `AuthController` ### 事件脚本
- `LiveRoomsController`
- `RecordTasksController`
- `LogsController`
- `SettingsController`
- `ApiTokenAuthenticationMiddleware`
- `ExceptionHandlingMiddleware`
## Implemented features 支持在关键生命周期节点执行自定义脚本(Shell / PowerShell 内联或外部脚本文件):
### Douyin adapter | 事件 | 说明 |
|------|------|
| `live_started` | 检测到直播上线时触发 |
| `live_ended` | 检测到直播下线时触发 |
| `segment_completed` | 一个录制分片完成时触发 |
- resolve share links and redirects 脚本通过环境变量获取上下文信息:
- extract real `roomId`
- call `https://live.douyin.com/webcast/room/web/enter/`
- preload `ttwid` when needed
- parse both:
- `live_core_sdk_data.pull_data.stream_data`
- `flv_pull_url / hls_pull_url_map`
- select the highest quality by default or respect configured quality
### Background polling scheduler ```
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
```
- configurable polling interval 支持超时控制与自定义日志输出。
- configurable enable/disable switch
- configurable auto-start switch
- updates room status in background
- auto-creates a recording task when a room is live and no task is running
- also works as a recovery layer when ffmpeg exits unexpectedly while the room is still live
### Recording enhancements ### 异常恢复(Recovery
- save format setting: `MP4` or `TS` - 自动检测处于 Live 状态但未在录制的直播间
- save mode setting: - 自动检测录制完成但未做最终化(MP4 remux)的任务
- `SingleFile` - 支持一键重试录制或逐一恢复
- `Segmented` - 后台轮询调度器同时兼具恢复层功能:当 ffmpeg 异常退出而直播间仍在直播时自动重新创建录制任务
- recording template setting:
- `StreamCopy`
- `BalancedMp4`
- `ArchiveTs`
- reconnect tuning:
- enable/disable reconnect
- max reconnect delay
- read/write timeout
### Frontend ### 数据保留清理(Retention
- login page - 可配置保留天数,超期自动清理录制记录、弹幕文件、视频文件与系统日志
- live room management - 支持后台定时清理(每 24 小时执行一次)
- recording task list
- recording task detail
- log viewer
- system settings
- route lazy loading
- vendor chunk splitting in Vite
## Settings exposed in UI ### 每日回顾(Daily Review
- ffmpeg path - 按日期查看当天所有录制会话的汇总信息
- output root - 亮点卡片:最长录制、最多弹幕、最多异常等
- default quality - 时间线视图展示会话与分片时间段
- live save format - 弹幕数量随时间分布的可视化数据
- save mode
- recording template
- segment duration
- reconnect switch and reconnect delay
- read/write timeout
- background polling switch
- auto-start-on-live switch
- polling interval
- Douyin User-Agent / Referer / Cookie
## Run ### 系统日志
### Backend - 持久化系统日志,支持按类别、时间筛选
- 日志关联直播间、录制会话、录制任务
- 可在前端日志页面实时查看
### 前端界面
- 登录页
- 直播间管理(添加、编辑、删除、状态查看、快速录制)
- 录制任务列表与详情
- 录制会话列表与详情
- 日志查看器
- 每日回顾
- 异常恢复页面
- 系统设置(录制、通知、弹幕、事件脚本、保留策略、平台配置)
- 路由懒加载,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 ```powershell
dotnet restore LiveRecorder.sln dotnet restore LiveRecorder.sln
@@ -159,16 +219,16 @@ dotnet build LiveRecorder.sln --no-restore -m:1
dotnet run --project src/LiveRecorder.WebApi/LiveRecorder.WebApi.csproj --urls http://localhost:5000 dotnet run --project src/LiveRecorder.WebApi/LiveRecorder.WebApi.csproj --urls http://localhost:5000
``` ```
Default account: 默认账号:
- username: `admin` - 用户名:`admin`
- password: `Admin@123` - 密码:`Admin@123`
Swagger: Swagger
- [http://localhost:5000/swagger](http://localhost:5000/swagger) - http://localhost:5000/swagger
### Frontend ### 前端
```powershell ```powershell
cd frontend cd frontend
@@ -176,18 +236,33 @@ npm install
npm run dev npm run dev
``` ```
Frontend dev server: 前端开发服务器:
- [http://localhost:5173](http://localhost:5173) - http://localhost:5173
## Verified locally ### 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` - `dotnet build LiveRecorder.sln --no-restore -m:1`
- `npm run build` - `npm run build`frontend 目录)
- Docker Compose 完整部署验证通过
## Next steps ## 后续规划
1. Implement full adapters for Bilibili, Huya, Douyu, and Kuaishou. 1. 完善 Bilibili 全功能适配,新增虎牙、斗鱼、快手适配器
2. Add per-room recording preferences instead of relying only on global defaults. 2. 按直播间独立配置录制参数
3. Add full-text log search and time-range filters. 3. 日志全文搜索与时间范围过滤
4. Replace lightweight token sessions with JWT or external SSO if needed. 4. 基于 JWT 或外部 SSO 替换轻量 Token 会话
5. 弹幕回放播放器集成
6. 录制文件自动上传至 S3/OSS 等对象存储
+12 -1
View File
@@ -393,7 +393,18 @@ async function handleLogout() {
} }
:global(html[data-theme="dark"]) .app-sidebar { :global(html[data-theme="dark"]) .app-sidebar {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)); background: var(--bg-subtle);
border-right-color: var(--border-base);
}
:global(html[data-theme="dark"]) .app-nav-menu :deep(.el-menu-item.is-active) {
background: linear-gradient(180deg, rgba(91, 168, 255, 0.18), rgba(91, 168, 255, 0.1));
box-shadow: inset 0 0 0 1px rgba(91, 168, 255, 0.2);
}
:global(html[data-theme="dark"]) .app-nav-icon-item.is-active {
background: linear-gradient(180deg, rgba(91, 168, 255, 0.18), rgba(91, 168, 255, 0.1));
box-shadow: inset 0 0 0 1px rgba(91, 168, 255, 0.2);
} }
.app-sidebar--collapsed { .app-sidebar--collapsed {