{"id":44748882,"url":"https://github.com/eric2788/bilirec","last_synced_at":"2026-02-15T22:33:41.988Z","repository":{"id":331417427,"uuid":"1125370723","full_name":"eric2788/bilirec","owner":"eric2788","description":"针对树莓派构建的轻量级B站直播录制系统","archived":false,"fork":false,"pushed_at":"2026-01-27T10:12:20.000Z","size":224,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-27T16:05:52.153Z","etag":null,"topics":["bilibili","bilibili-live","livestream","recorder"],"latest_commit_sha":null,"homepage":"https://bilirec.ericlamm.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eric2788.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-30T15:55:52.000Z","updated_at":"2026-01-27T10:12:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/eric2788/bilirec","commit_stats":null,"previous_names":["eric2788/bilirec"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eric2788/bilirec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric2788%2Fbilirec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric2788%2Fbilirec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric2788%2Fbilirec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric2788%2Fbilirec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eric2788","download_url":"https://codeload.github.com/eric2788/bilirec/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eric2788%2Fbilirec/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29490932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bilibili","bilibili-live","livestream","recorder"],"created_at":"2026-02-15T22:33:40.445Z","updated_at":"2026-02-15T22:33:41.980Z","avatar_url":"https://github.com/eric2788.png","language":"Go","funding_links":[],"categories":["直播相关工具"],"sub_categories":["直播脚本"],"readme":"# Bilirec - Bilibili 直播录制工具\n\n一个用 Go 语言编写的 Bilibili 直播录制工具，支持自动录制直播流并保存为 FLV 格式。\n\n## 功能特性\n\n- ✅ 手动触发录制任务，实时录制直播流\n- ✅ 支持多个直播间同时录制\n- ✅ 自动处理流中断和恢复\n- ✅ RESTful API 管理录制任务\n- ✅ 文件管理和下载功能\n- ✅ 支持匿名登录或账号登录\n- ✅ 自动刷新 Cookie 保持登录状态\n- ✅ 低内存与低 CPU 占用，适合在资源受限设备（如树莓派）上运行\n\n## 安装\n\n### 使用 Docker\n\n可以通过构建镜像或直接运行容器来启动 Bilirec。\n\n从源码构建镜像并运行（示例）：\n\n```bash\n# 在仓库根目录构建镜像\ndocker build -t bilirec:latest .\n\n# 运行容器（示例）\ndocker run -d \\\n  --name bilirec \\\n  -p 8080:8080 \\\n  -e PORT=8080 \\\n  -e FRONTEND_URL=http://localhost:8080 \\\n  -v /path/to/records:/app/records \\\n  -v /path/to/secrets:/app/secrets \\\n  -v /path/to/database:/app/database \\\n  # 可选：启用 CloudConvert（替换为你的 API key）\n  -e CLOUDCONVERT_API_KEY=your_api_key \\\n  bilirec:latest\n```\n\n如果你有可用的镜像仓库（例如 GHCR 或 Docker Hub），也可以直接拉取并运行镜像（示例）：\n\n```bash\ndocker pull ghcr.io/eric2788/bilirec:latest\ndocker run -d --name bilirec -p 8080:8080 ghcr.io/eric2788/bilirec:latest\n```\n\n### 从源码构建\n\n```bash\ngit clone \u003crepository-url\u003e\ncd bilirec\ngo build -o bilirec main.go\n```\n\n## 配置\n\n所有配置通过环境变量设置：\n\n| 环境变量 | 说明 | 默认值 |\n|---------|------|--------|\n| `ANONYMOUS_LOGIN` | 是否使用匿名登录 | `false` |\n| `PORT` | API 服务端口 | `8080` |\n| `MAX_CONCURRENT_RECORDINGS` | 最大同时录制数 | `3` |\n| `MAX_RECORDING_HOURS` | 单次录制最长时间（小时） | `5` |\n| `MAX_RECOVERY_ATTEMPTS` | 单次录制的最大重连尝试次数 | `5` |\n| `OUTPUT_DIR` | 录制文件保存目录 | `records` |\n| `SECRET_DIR` | Cookie 和 Token 保存目录 | `secrets` |\n| `CONVERT_FLV_TO_MP4` | 在下载时是否将 FLV 转为 MP4 | `false` |\n| `DELETE_FLV_AFTER_CONVERT` | 转换后是否删除原始 FLV 文件 | `false` |\n| `BACKEND_HOST` | 后端主机（用于生成Cookie域名） | `localhost:8080` |\n| `FRONTEND_URL` | 前端 URL（用于 CORS 与 cookie 域） | `http://localhost:8080` |\n| `USERNAME` | 可选：启用用户名/密码认证时的用户名 | (未设置) |\n| `PASSWORD` | 可选：启用用户名/密码认证时的密码 | (未设置) |\n| `JWT_SECRET` | JWT 签名密钥 | `bilirec_secret` |\n| `DEBUG` | 启用调试模式（会开启 pprof 和临时 hex token） | `false` |\n| `PRODUCTION_MODE` | 启用生产模式（影响 cookie 与 CORS） | `false` |\n| `DATABASE_DIR` | 本地数据库目录（bbolt，用于持久化转换任务等） | `database` |\n| `CLOUDCONVERT_THRESHOLD` | 使用 CloudConvert 的文件大小阈值（字节） | `1073741824` (1 GB) |\n| `CLOUDCONVERT_API_KEY` | 可选：CloudConvert API Key（为空则禁用 CloudConvert） | (未设置) |\n| `UPLOAD_BUFFER_SIZE` | 上传时或向外部服务（如 CloudConvert）传输文件使用的缓冲区大小（字节） | `5242880` (5 MB) |\n| `DOWNLOAD_BUFFER_SIZE` | 文件下载 / 导出时使用的缓冲区大小（字节） | `5242880` (5 MB) |\n| `STREAM_WRITER_BUFFER_SIZE` | 流写入器（写入文件）缓冲区大小（字节） | `1048576` (1 MB) |\n| `LIVE_STREAM_WRITER_BUFFER_SIZE` | 实时流写入缓冲区（用于直播录制或实时下载，字节） | `5242880` (5 MB) |\n\n### 示例配置\n\n```bash\nexport ANONYMOUS_LOGIN=false\nexport PORT=8080\nexport MAX_CONCURRENT_RECORDINGS=5\nexport MAX_RECORDING_HOURS=10\nexport MAX_RECOVERY_ATTEMPTS=5\nexport OUTPUT_DIR=/path/to/records\nexport SECRET_DIR=/path/to/secrets\nexport DATABASE_DIR=/path/to/database\nexport CONVERT_FLV_TO_MP4=false\nexport DELETE_FLV_AFTER_CONVERT=false\n# 可选：CloudConvert（如果启用会对大文件使用云端转换）\nexport CLOUDCONVERT_THRESHOLD=1073741824\nexport CLOUDCONVERT_API_KEY=\nexport BACKEND_HOST=localhost:8080\nexport FRONTEND_URL=http://localhost:8080\nexport UPLOAD_BUFFER_SIZE=5242880\nexport DOWNLOAD_BUFFER_SIZE=5242880\nexport STREAM_WRITER_BUFFER_SIZE=1048576\nexport LIVE_STREAM_WRITER_BUFFER_SIZE=5242880\nexport JWT_SECRET=bilirec_secret\nexport DEBUG=false\n# 可选：启用 REST API 认证\nexport USERNAME=admin\nexport PASSWORD=changeme\nexport PRODUCTION_MODE=false\n```\n\n## 使用方法\n\n### 启动服务\n\n```bash\n./bilirec\n```\n\n首次启动如果未使用匿名登录，会显示二维码，使用 Bilibili 手机 APP 扫码登录。\n\n### API 接口\n\n\u003e Swagger UI 会在服务器运行时于根路径 `/` 提供 — 在浏览器中打开该地址即可查看与测试 API。\n\n#### 认证\n\n如果设置了 `USERNAME` 与 `PASSWORD`，REST API 会启用基于 JWT 的认证（登录会在 cookie 中设置 `jwtToken`）。使用：\n\n```http\nPOST /login\nContent-Type: application/json\n\n{ \"user\": \"\u003cusername\u003e\", \"pass\": \"\u003cpassword\u003e\" }\n```\n\n登录成功后会在响应中设置 JWT cookie（键名 `jwtToken`），随后对需要认证的接口请携带该 cookie。若未设置用户名/密码，API 默认为公开访问。\n\n#### 录制管理\n\n- **开始录制**\n  ```\n  POST /record/:roomID/start\n  ```\n\n- **停止录制**\n  ```\n  POST /record/:roomID/stop\n  ```\n\n- **获取录制状态**\n  ```\n  GET /record/:roomID/status\n  ```\n\n- **获取录制统计**\n  ```\n  GET /record/:roomID/stats\n  ```\n  返回：\n  ```json\n  {\n    \"bytes_written\": 1048576,\n    \"status\": \"recording\",\n    \"start_time\": 1234567890,\n    \"recovered_count\": 0,\n    \"elapsed_seconds\": 120\n  }\n  ```\n\n- **列出所有录制任务**\n  ```\n  GET /record/list\n  ```\n\n#### 文件管理\n\n- **列出文件**\n  ```\n  GET /files/browse/*\n  ```\n\n- **下载文件**\n  ```\n  GET /files/download/*\n  ```\n  下载接口直接返回存储的文件，**不再支持**通过查询参数进行即时格式转换（此前的 `?format=...` 参数已移除）。\n  若要将录制的 FLV 转为 MP4，请启用 `CONVERT_FLV_TO_MP4`：在录制完成时，recorder 会将 FLV 文件加入转换队列，由后台任务异步转换为 MP4（转换行为受 `DELETE_FLV_AFTER_CONVERT` 控制）。\n  当同时设置了 `CLOUDCONVERT_API_KEY` 且文件大小 \u003e= `CLOUDCONVERT_THRESHOLD`（默认 1 GB）时，系统会优先使用 CloudConvert（异步任务，可通过 `/convert/tasks` 查询转换状态）；否则由本地 ffmpeg 后台任务处理。\n\n- **临时 / 预签名下载（Presigned）**\n  ```\n  POST /files/presigned/{path}?ttl=\u003cseconds\u003e\n  ```\n  该接口需要身份认证（JWT），用于为文件创建一个临时的预签名下载令牌（`ttl` 可选，单位秒，默认 3600）。成功创建后会返回包含临时令牌或 URL 的响应。使用该令牌可以进行匿名下载：\n\n  ```\n  GET /files/tempdownload?presigned=\u003ctoken\u003e\n  ```\n  `GET /files/tempdownload` 无需认证，但必须提供有效的 `presigned` 查询参数。该临时链接会在创建时设置过期时间，过期后将无法使用。\n\n- **删除多个文件**\n  ```\n  DELETE /files/batch\n  ```\n  请求体：JSON 数组，包含要删除的相对文件路径，示例：\n\n  ```json\n  [\"room123/20250101.flv\", \"room456/20250102.flv\"]\n  ```\n\n- **删除目录**\n  ```\n  DELETE /files/{path}\n  ```\n\n#### 转换任务\n\n- **列出进行中的转换任务**（需要认证，返回任务信息）\n  ```\n  GET /convert/tasks\n  ```\n\n- **取消转换任务**（需要认证）\n  ```\n  DELETE /convert/tasks/:task_id\n  ```\n  返回 `204 No Content` 表示取消成功，若任务不存在返回 `404`。\n\n#### 房间信息\n\n- **获取房间信息**\n  ```\n  GET /room/:roomID/info\n  ```\n  获取指定房间的详细信息。\n\n- **批量获取房间信息**\n  ```\n  GET /room/infos?roomIDs=123,456,789\n  ```\n  通过逗号分隔的房间 ID 列表获取多个房间的信息。\n\n- **检查直播状态**\n  ```\n  GET /room/:roomID/live\n  ```\n  检查指定房间的直播是否进行中。\n\n#### 房间订阅管理\n\n- **订阅房间**\n  ```\n  POST /room/:roomID\n  ```\n  订阅一个直播房间，当房间开播时会接收更新。\n  - 状态 `200`: 订阅成功\n  - 状态 `409`: 已订阅此房间\n  - 状态 `400`: 无效的房间 ID\n\n- **取消订阅**\n  ```\n  DELETE /room/:roomID\n  ```\n  取消订阅直播房间。\n  - 状态 `200`: 取消订阅成功\n  - 状态 `404`: 未订阅此房间\n  - 状态 `400`: 无效的房间 ID\n\n- **检查订阅状态**\n  ```\n  GET /room/subscribe/:roomID\n  ```\n  检查是否已订阅指定房间。返回：\n  ```json\n  {\n    \"room_id\": 123,\n    \"is_subscribed\": true\n  }\n  ```\n\n- **列出所有订阅房间**\n  ```\n  GET /room/subscribe\n  ```\n  获取所有已订阅房间的 ID 列表。返回：\n  ```json\n  {\n    \"room_ids\": [123, 456, 789]\n  }\n  ```\n\n## 开发与调试\n\n- **启用调试**：设置环境变量 `DEBUG=true` 启用调试模式，服务器启动时会在日志中打印一个临时十六进制令牌（hex token）。\n- **pprof 性能分析**：调试模式下会在 `/debug/pprof` 挂载 pprof 以便性能分析。该路由受保护：可以在请求头 `Authorization` 中填入启动日志中显示的 hex 令牌来访问，或使用已配置的 `USERNAME` / `PASSWORD` 进行 Basic Auth 登录（若已设置）。\n- **实现参考**：该逻辑位于 `internal/modules/rest/rest.go` 中（`DEBUG` 控制是否启用，令牌或用户名/密码用于授权访问）。\n\n## 项目结构\n\n```\n.\n├── .github/                          # CI / workflows\n├── Dockerfile\n├── go.mod\n├── LICENSE\n├── README.md\n├── main.go\n├── main_test.go\n├── internal/                         # 内部包（不对外暴露）\n│   ├── controllers/                  # HTTP 控制器\n│   │   ├── convert/                  # 转换任务管理\n│   │   ├── file/                     # 文件管理\n│   │   ├── record/                   # 录制管理\n│   │   └── room/                     # 房间信息与订阅\n│   ├── modules/                      # 核心模块\n│   │   ├── bilibili/                 # Bilibili API 封装与认证\n│   │   ├── config/                   # 配置管理\n│   │   └── rest/                     # REST 服务\n│   ├── processors/                   # 流处理器\n│   └── services/                     # 业务逻辑服务\n│       ├── convert/                  # 转换服务\n│       ├── file/                     # 文件操作\n│       ├── path/                     # 路径管理\n│       ├── recorder/                 # 直播录制\n│       ├── room/                     # 房间信息与订阅\n│       └── stream/                   # 流处理\n├── pkg/                              # 可复用库与工具\n│   ├── cloudconvert/                 # CloudConvert API 客户端\n│   ├── db/                           # 数据库抽象层\n│   ├── ds/                           # 数据结构\n│   ├── flv/                          # FLV 格式处理\n│   ├── monitor/                      # 监控与统计\n│   ├── pipeline/                     # 流处理管道\n│   ├── pool/                         # 内存池\n│   └── signeddownload/               # 预签名下载\n└── utils/                            # 工具函数库\n```\n\n## 核心实现\n\n### 录制流程\n\n1. 通过 [`bilibili.Client`](internal/modules/bilibili/bilibili.go) 获取直播流地址\n2. 使用 [`stream.Service`](internal/services/stream/stream.go) 读取流数据\n3. [`recorder.Service`](internal/services/recorder/recorder.go) 管理录制任务（自动重连与恢复）\n4. 数据写入到 FLV 文件，保存在配置的输出目录；如果启用了 `CONVERT_FLV_TO_MP4`，录制完成时会自动将 FLV 文件加入转换队列并由后台任务异步转换为 MP4（转换行为受 `DELETE_FLV_AFTER_CONVERT` 控制，转换任务可通过 `/convert/tasks` 查询）。\n\n### 关键特性\n\n- **自动恢复**: 当流中断时自动重连，详见 [`recorder.Service`](internal/services/recorder/recorder.go)\n- **缓冲池**: 使用 [`pool.BufferPool`](pkg/pool/pool.go) 减少内存分配\n- **定期刷盘**: 每 5 秒自动刷新写入缓冲，防止数据丢失\n- **低资源占用**: 设计注重低内存和低 CPU 使用，适合树莓派等资源受限设备\n- **文件管理**: 支持列出、预览、下载（可转换格式）、批量删除文件及删除目录，详见 `internal/controllers/file/file.go`\n- **自动转换**: 如果启用 `CONVERT_FLV_TO_MP4`，录制完成时会自动将 FLV 转为 MP4；可通过 `DELETE_FLV_AFTER_CONVERT` 控制是否删除原始 FLV\n- **实时修复（Realtime Fixer）**: 在流式写入场景下逐个修复 FLV Tag 的时间戳并输出，包含重复 Tag 去重（可查询去重统计），并通过内存池、去重缓存与周期清理来保持低延迟与低内存占用，适合边录制边推送或实时下载的场景。\n- **REST API 文档**: Swagger UI 在根路径 `/` 提供（由 `swag` 生成，参见 `internal/modules/rest`）\n- **认证与调试**: 可选用户名/密码登录（设置 `USERNAME` 和 `PASSWORD`）启用 JWT 认证；调试模式下可通过 `/debug/pprof` 访问 pprof（受临时 token 或基本 auth 保护）\n- **Cookie 管理**: 自动刷新 Bilibili Cookie 保持登录状态\n\n## 依赖项\n\n主要依赖库：\n\n- [github.com/gofiber/fiber/v3](https://github.com/gofiber/fiber) - Web 框架\n- [github.com/CuteReimu/bilibili/v2](https://github.com/CuteReimu/bilibili) - Bilibili API 客户端\n- [go.uber.org/fx](https://github.com/uber-go/fx) - 依赖注入框架\n- [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) - 日志库\n\n## 许可证\n\n请参阅项目许可证文件。\n\n## 贡献\n\n欢迎提交 Issue 和 Pull Request！","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric2788%2Fbilirec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feric2788%2Fbilirec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric2788%2Fbilirec/lists"}