* feat(downloader): 录制写盘字节计数与实时速率,/v1/streamers 新增 live_bytes_per_sec 各下载器在写出点往 `ByteCounter`(Arc<AtomicU64>)累加,写盘路径上只多一次 `fetch_add`,不加锁、不 await、不改任何控制流: - stream-gears/httpflv:`FlvFile::write_tag` 按 11 + 数据 + 4 字节计 - stream-gears/hls:`download_to_file` 每个 chunk 落盘后计 - mesio:条目进入修复管线前按 `FlvData::size()` / `HlsData::size()` 计 (writer 来自外部 crate,拿不到逐条写出量;修复只增删极少量 tag) - ffmpeg / streamlink:子进程自己落盘,改为每秒读一次 `.part` 长度做差分 `DownloadTask` 持有 `RateMeter`:1 秒采样、10 秒滑动窗口取首尾差值。窗口明显长于 GOP,避免 httpflv 按关键帧批量落盘造成的抖动;采样任务随 `execute` 启停。 `GET /v1/streamers` 每项新增 `live_bytes_per_sec`(字节/秒):未录制、采样不足、 或下载器不经本进程写盘(边录边传、yt-dlp)时为 null。 Part of #1708 * feat(live): 解析主播头像、补斗鱼封面,新增录制中封面/头像的同源代理端点 `LiveStream` 新增 `avatar_url: Option<String>`: - B 站:`getInfoByRoom` 同一次响应里的 `anchor_info.base_info.face` - 抖音:房间对象 `owner.avatar_thumb.url_list[0]`(web / H5 两个接口都有) - 斗鱼:betard 的 `owner_avatar`,缺失时退回 `avatar.{middle,big,small}`; 封面 `room_pic` 一并补上(此前写死空串) - 虎牙:`gameLiveInfo` / `liveData` 的 `avatar180`,与 screenshot 一样升到 https - 其余平台为 None,不改行为 `DownloadTask` 在内存里保存当前录制的封面与头像地址(每次 check_stream 刷新, 随任务消亡,不落库、不加迁移)。`GET /v1/streamers` 每项新增 `live_cover_url` / `live_avatar_url`(未录制为 null)。 新增 `GET /v1/streamers/{id}/cover` 与 `/avatar`:页面直接引用图片 CDN 会带上 biliup 的 Referer 被 B 站等拒绝(403),改由服务端以直播间页面地址作 Referer 拉取后转发, 按图片 URL 做 5 分钟内存缓存(上限 64 条、单图 4MB)。只按主播 id 取 worker 里已有的 地址,不接受任意 URL;上游不是图片时不转发。路由注册在 `router()` 内,`--auth` 时 与其它 /v1 接口同一道登录校验。 Part of #1708 * feat(ui): 录制中卡片显示直播间封面、主播头像与实时写盘速率 `StreamerCard`(主页「进行中」与直播管理网格共用)在状态为录制中时: - 顶部加 16:9 封面缩略图,懒加载,图片走 `/v1/streamers/{id}/cover` 同源代理; 加载中显示占位底色,加载失败整块消失,卡片回到原有样式 - 名称前加 22px 头像圆图(`/v1/streamers/{id}/avatar`),失败则不占位 - 时长右侧加写盘速率,按 B/s、KB/s、MB/s 自动换单位;后端未采样(null)显示「—」 图片地址带原始 URL 的短哈希作查询参数,换一场直播封面变了就不会沿用旧缓存。 未开播 / 暂停的卡片不变。直播管理列表视图新增「码率」列,主播名前带头像。 不引入新依赖:头像与封面用 next/image(项目已配置 unoptimized),速率格式化与 代理地址拼接放在 `use-dashboard.ts`。 Part of #1708 * perf(live): B 站封面/头像经代理时改取 CDN 缩略图 实测 B 站 `user_cover` 原图可达 11520×8640、4 MB,卡片上只显示成 16:9 缩略图和 22px 头像,原样转发既慢又占缓存。对 `*.hdslb.com` 的地址借 CDN 的 `@宽w_高h_1c` 后缀取 640×360 / 128×128 的裁切图(同一张图从 4 MB 降到约 25 KB);已带后缀或带 query 的 地址、以及其它平台的地址原样使用。 Part of #1708 * refactor(downloader): ffmpeg / streamlink 写盘速率改为解析 stderr 进度行,不再读盘 review 意见:每秒 stat 一次 `.part` 长度会额外占磁盘 IO。改为让子进程自己报告: - ffmpeg 加 `-progress pipe:2 -nostats`,把 key=value 进度块打到 stderr(不受 `-loglevel quiet` 影响),`total_size=N` 就是本进程累计写出字节 - streamlink 加 `--progress force`,stderr 不是终端时也按周期打 `[download] Written 12.34 MiB to <file> (5s @ 2.50 MiB/s)` 都在既有的 `spawn_log` stderr 读取协程里顺带解析(`SubprocessProgress`),进度行降为 debug 日志不再刷 info;累计值只加增量、回退时重置基准。解析不出来只是没有速率, 不影响录制。删掉 `FileSizeProbe`。 Part of #1708 * feat(live): 主播头像开播时下载一次存到 data/avatar/,接口直接读本地文件 review 意见:头像几乎不变,不必每次经代理拉取。 - 新增 `common/live_image.rs`:图片抓取(带 Referer、大小与类型校验、B 站缩略图后缀) 从 `api/live_media.rs` 挪到这里共用;新增 `AvatarStore`,按主播 id 存 `data/avatar/<id>.<ext>` + `<id>.json`(记录来源地址与 Content-Type),先写临时文件 再改名,同一时刻只允许一个下载写盘 - 开播进入录制流程时后台下载一次;后续 check_stream 拿到的地址变了才重新下载; 下载失败保留旧文件、只记日志 - `GET /v1/streamers/{id}/avatar` 改为读本地文件(`Cache-Control: max-age=3600`); 正在录制而本地还没有时现场下载一次再返回,失败退回旧文件;不在录制时只读本地, 没有就 404。服务重启后凭 `<id>.json` 直接复用,不重复下载 - 封面仍走内存缓存的代理(每场直播都会换,落盘意义不大) Part of #1708 * fix(downloader): streamlink 进度行打在 stdout,stdout / stderr 都解析 实测 `--output` 到文件时 streamlink 把控制台输出(含 `[download] Written …`) 打在 stdout 而不是 stderr,只解析 stderr 会得到恒为 0 的速率。两条流共用一个 进度跟踪器,避免重复累加。 Part of #1708
🛠️ 功能
- 提供 skill,让你的 Agent 成为 up 主:
npx skills add biliup/biliup - 开箱即用,多种安装方式,提供可视化 WebUi 界面
- 多主播录制/上传,24X7 无人值守运行,高自定义元信息
- 录制同时抓取弹幕,输出 XML 弹幕文件
- 作为自动化流程中的命令行工具封装使用
- 边录边传 0 落盘
论坛:BBS
📺 支持平台
内置 19 个直播平台解析,未匹配的地址会交给通用适配器(依赖本机的 yt-dlp / Streamlink)尝试处理。
| 平台 | 站点 | 弹幕 |
|---|---|---|
| 哔哩哔哩 | live.bilibili.com、b23.tv |
✅ |
| 抖音 | douyin.com |
✅ |
| 斗鱼 | douyu.com |
✅ |
| 虎牙 | huya.com |
✅ |
| 快手 | kuaishou.com、chenzhongtech.com |
|
| AcFun | acfun.cn |
|
| 网易 CC | cc.163.com |
|
| 映客 | inke.cn |
|
| 猫耳 FM | missevan.com |
|
| KilaKila / 红豆 FM | live.kilakila.cn、hongdoufm.com |
|
| YY | yy.com |
|
| TTingLive | ttinglive.com |
|
| Twitch | twitch.tv(直播与录像) |
✅ |
| YouTube | youtube.com、youtu.be |
✅ |
| TwitCasting | twitcasting.tv |
✅ |
| niconico | nicovideo.jp |
|
| AfreecaTV | afreecatv.com |
|
| Bigo Live | bigo.tv |
|
| Picarto | picarto.tv |
|
| 通用适配器 | 其他 http(s) 地址 |
📜 更新日志
Important
Disclaimer / 免责声明
- 本项目仅供个人学习研究,不保证稳定性,不提供技术支持
- 使用本项目产生的一切后果由用户自行承担
- 禁止商业用途,请遵守版权及平台规定
- This project is for personal learning and research purposes only
- No stability guarantee or technical support provided
- Users are solely responsible for any consequences of using this project
- Commercial use is strictly prohibited
- Please respect copyright and platform ToS
📜 使用文档
B 站命令行投稿工具,支持短信登录、账号密码登录、扫码登录、浏览器登录以及网页Cookie登录,并将登录后返回的 cookie 和 token 保存在 cookies.json 中,可用于其他项目。
- 下载 Release: biliupR
- 获取命令帮助
biliup --help - 登录信息文件可用
-u/--user-cookie指定,便于多账号切换
文档地址:https://biliup.github.io/biliup-rs
Upload video to bilibili.
Usage: biliup [OPTIONS] <COMMAND>
Commands:
login 登录B站并保存登录信息
renew 手动验证并刷新登录信息
upload 上传视频
append 是否要对某稿件追加视频
show 打印视频详情
comments 查看视频评论
reply 回复视频评论,默认只打印将要回复的内容
dump-flv 输出flv元数据
download 下载视频
server 启动web服务,默认端口19159
list 列出所有已上传的视频
help Print this message or the help of the given subcommand(s)
Options:
-p, --proxy <PROXY> 配置代理
-u, --user-cookie <USER_COOKIE> 登录信息文件 [default: cookies.json]
--rust-log <RUST_LOG> [default: tower_http=debug,info]
-h, --help Print help
-V, --version Print version
启动录制服务
启动web服务,默认端口19159
Usage: biliup server [OPTIONS]
Options:
-b, --bind <BIND> Specify bind address [default: 127.0.0.1]
-p, --port <PORT> Port to use [default: 19159]
--auth 开启登录密码认证
--secure-session-cookie 为会话 Cookie 附加 Secure 属性。仅当通过 HTTPS 反向代理访问 Web UI 时开启; 直接通过 HTTP 远程访问时开启会导致浏览器丢弃登录态
-c, --config <FILE> 使用 biliup 1.0.7 风格配置文件启动录制
-h, --help Print help
Important
自 #1660 起,
--bind的默认值由0.0.0.0改为127.0.0.1,即默认只监听本机,局域网/公网无法直接访问。 如需从其他设备访问,请加上--bind 0.0.0.0 --auth,详见下方「🔓 远程访问」一节。Docker 镜像已内置该参数,不受影响。
单独下载一场直播/视频,无需启动服务:
biliup download <URL> -o "./video/%Y-%m-%dT%H_%M_%S{title}" --split-time 1h
--split-size 与 --split-time 可按体积或时长自动分段,-o 支持 {title} 占位符与 strftime 时间格式。
🚀 快速开始
Windows
- 下载 Release: bbup-app
Linux 或 macOS
- 安装 uv
- 安装:
uv tool install biliup - 启动:
biliup server --auth - 访问 WebUI:
http://127.0.0.1:19159(默认只监听本机,远程访问见下方说明)
- 后台运行
nohup biliup server --auth &- 请查看参考
Termux
- 详见Wiki
Note
默认下载器
stream-gears由 Rust 实现,无需外部依赖。若配置ffmpeg下载器或使用后处理,需要本机安装ffmpeg;YouTube、niconico 等平台与通用适配器则依赖yt-dlp或streamlink。Docker 镜像已内置ffmpeg。
🔓 远程访问(监听 0.0.0.0)
默认的 127.0.0.1 只允许本机访问。需要从其他设备访问时,显式指定 --bind 0.0.0.0 并开启 --auth:
biliup server --bind 0.0.0.0 --auth
首次打开 http://your-ip:19159 会引导设置管理员密码(用户名固定为 biliup),之后即可正常登录使用。
Note
绑定非回环地址时必须同时开启
--auth,否则会拒绝启动,避免无认证的 Web API 被暴露:refusing to expose the unauthenticated Web API on 0.0.0.0:19159; use a loopback bind address or enable --auth
Warning
将 Web UI 暴露到公网存在风险,建议仅在可信局域网内使用,或置于反向代理之后。 首次访问即可设置管理员密码,请在启动后立即完成初始化,避免被他人抢先占用。 若通过 HTTPS 反向代理访问,请加上
--secure-session-cookie;直接以 HTTP 远程访问时不要加,否则浏览器会丢弃登录态(见 #1669)。
Docker
镜像已内置 --bind 0.0.0.0 --auth,开箱即用,无需额外配置:
docker compose up -d
打开 http://your-ip:19159 设置管理员密码即可。录播与配置默认持久化在容器的 /opt 卷中。
Important
若要自定义
command,必须带上--bind 0.0.0.0。容器内若监听127.0.0.1,宿主机的端口映射无法转发进容器,Web UI 将完全无法访问。
📤 边录边传(sync-downloader)
把 downloader 设为 sync-downloader 后,ffmpeg 会把直播流 remux 成 Matroska 写到 stdout,按 UPOS 分片一边录一边上传,每录满 file_size(默认 2.5 GB,向上对齐到 10 MiB)就作为一 P 追加到同一稿件。前提:
- 本机
PATH中有ffmpeg;HLS 流(B 站bili_protocol = "hls_fmp4")若装有streamlink会用它拉流再交给 ffmpeg,没有则由 ffmpeg 直拉。 - 主播必须绑定上传模板,且模板对应的 cookies 文件可用;
uploader = "Noop"或没有模板时不会录制,日志会给出边录边传需要先为主播设定上传模板。 - 上传并发固定 3 线程,不受
threads、segment_time控制。
downloader = "sync-downloader"
uploader = "bili_web"
file_size = 2621440000 # 每 P 大小,可按上传带宽调小
# sync_save_dir = "/data/sync" # 可选:额外保留每 P 的本地副本
[streamers."某主播"]
url = ["https://live.bilibili.com/1234"]
title = "{streamer}%Y-%m-%d 直播录像"
tid = 171
user_cookie = "cookies.json"
行为说明:
- 录制期间每 P 会同时写入系统临时目录(
$TMPDIR/biliup-sync/worker-<id>/)作为兜底;预传完整且校验一致时直接 complete,否则(直播提前结束、上传落后)按实际长度从临时文件重传。投稿确认后临时文件删除,设置了sync_save_dir的副本保留并交给postprocessor。 - 停止/暂停/编辑主播只会结束当前分段,已录内容仍会上传并投稿;上传或投稿失败时保留有内容的分段文件并在日志中打印路径,可手动补传。
- B 站直链约 1 小时过期。分段结束后拉不到数据时会立刻交回监控循环重新解析直链,并在同一稿件上继续追加分 P。
- 排查问题时留意
ERROR ... 下载流程出错日志:会带上具体原因(cookies 文件路径、preupload 拒绝等),而不是静默退回落盘录制。
🧑💻开发
架构概览
Rust后端 + 精简 Python 包 + Next.js前端的混合架构。
graph TB
subgraph "🌐 前端层"
UI[Next.js Web界面<br/>React + TypeScript<br/>Semi UI组件库]
end
subgraph "⚡ Rust后端服务"
CLI[命令行与 Web API<br/>biliup-cli<br/>REST API / WebUI / 配置导入]
CORE[核心库<br/>biliup<br/>直播解析 / 下载 / 上传]
DANMAKU[弹幕库<br/>danmaku<br/>多平台协议 / XML输出]
GEARS[Python绑定<br/>stream-gears<br/>python -m biliup 入口]
end
subgraph "🐍 Python包"
PYENTRY[最小入口<br/>biliup.__main__<br/>调用 stream_gears.main_loop]
PYUPLOAD[投稿库<br/>bili_webup / bili_webup_sync<br/>供外部项目调用]
end
subgraph "🗄️ 数据层"
DB[(SQLite数据库<br/>配置存储<br/>任务状态 & 日志)]
FILES[文件系统<br/>视频分段 / 弹幕XML<br/>缓存与临时文件]
end
subgraph "🌍 外部服务"
BILI[Bilibili API<br/>视频上传服务]
STREAMS[直播平台<br/>B站/斗鱼/虎牙/抖音/Twitch等]
end
UI --> CLI
CLI --> CORE
CLI --> DANMAKU
CLI --> DB
CLI --> FILES
CORE --> STREAMS
CORE --> BILI
DANMAKU --> STREAMS
DANMAKU --> FILES
GEARS --> CLI
PYENTRY --> GEARS
PYUPLOAD --> BILI
style UI fill:#e1f5fe
style CLI fill:#f3e5f5
style CORE fill:#f3e5f5
style DANMAKU fill:#f3e5f5
style GEARS fill:#f3e5f5
style PYENTRY fill:#e8f5e8
style PYUPLOAD fill:#e8f5e8
style DB fill:#fff3e0
style FILES fill:#fff3e0
style BILI fill:#ffebee
style STREAMS fill:#ffebee
目录结构
| 路径 | 说明 |
|---|---|
crates/biliup |
核心库:直播解析、下载器、B 站投稿与凭据管理 |
crates/biliup-cli |
命令行与 Web 服务:REST API、WebUI 托管、录制调度 |
crates/danmaku |
弹幕客户端:多平台协议解析与 XML 输出 |
crates/stream-gears |
PyO3 绑定,暴露给 python -m biliup |
app、public |
Next.js WebUI 源码;npm run build 产物输出到 out/ 并由后端内嵌 |
biliup |
精简 Python 包:最小入口与可供外部调用的投稿库 |
tauri-app |
桌面端外壳(实验性) |
frontend
- 确保 Node.js 版本 ≥ 18.17(Next.js 14 要求)
- 安装依赖:
npm i - 启动开发服务器:
npm run dev - 访问:
http://localhost:3000
Python
- 安装依赖
maturin dev npm run build- 启动 Biliup:
python3 -m biliup
Rust-cli
npm run build- 构建
cargo build --release --bin biliup - 开发启动 BiliupR:
cargo run
Note
biliup-cli通过rust-embed内嵌前端产物目录out/,因此在cargo build/cargo test之前必须先执行npm run build,否则编译会因out/不存在而失败。
测试
cargo test -p biliup -p biliup-cli -p danmaku
🤝Credits
- Thanks
ykdl, youtube-dl, streamlinkprovides downloader. - Thanks
THMonster/danmaku.
💴捐赠
