Noah e988af8268 chore(deps): 升级前端三方依赖至最新稳定版并适配 (#1710)
* chore(deps): 升级 Next.js 14→16、React 18→19 及 TypeScript/ESLint/sass 工具链

- next 14.2 → 16.3.5(Turbopack 成为默认打包器,`output: 'export'` 与 out/ 页面文件名不变)
- react / react-dom 18.3 → 19.3,@types/react(-dom) 同步到 19.3
- qrcode.react 3 → 4、react-json-tree 0.19 → 0.20:仅为满足 React 19 的 peerDependency,API 用法不变
- typescript 5.9 → 6.0.3;tsconfig `target` es5 → ES2017(TS 6 弃用 es5,TS 7 已移除),
  `jsx` preserve → react-jsx(Next 16 强制),include 补 `.next/dev/types`
- next-env.d.ts 改为由 Next 生成、不再入库(官方文档要求,且 dev/build 会写入不同内容;
  仓库 .gitignore 已有 `/*.ts` 规则覆盖)
- eslint 8 → 9.39.5,eslint-config-next 16,eslint-config-prettier 10:
  Next 16 移除了 `next lint`,`.eslintrc.json` 迁移为 flat config `eslint.config.mjs`,
  `npm run lint` 改为直接调用 `eslint .`;eslint-plugin-react-hooks 7 新增的
  React Compiler 规则暂降为 warn(命中的都是既有写法,与本次升级无关)
- sass 1.97 → 1.104、@types/node 20 → 26(仅类型)
- next.config.js 关闭 Next 16.3 新增的 `agentRules`,避免 `next dev` 在仓库根目录生成 AGENTS.md

未升到最新的两项及原因:
- typescript 7.0.2 为原生实现、不再提供 JS API,eslint-config-next 依赖的 typescript-eslint
  明确拒绝 TS 7(`>=4.8.4 <6.1.0`),故停在 6.0.3
- eslint 10 移除了 `context.getFilename()`,eslint-config-next 依赖的 eslint-plugin-react 7.37.5
  尚未适配(peer `^9.7`),故停在 9.x 最后一版

* fix(ui): 适配 React 19 的类型变更

@types/react 19 带来的四类编译错误,逐个按最小改动处理,不涉及运行时逻辑:

- `useRef<T>()` 不再允许省略初始值 → `useRef<FormApi>(undefined)`(6 处,`.current` 类型不变)
- `useRef<HTMLDivElement>(null)` 现返回 `RefObject<HTMLDivElement | null>`,
  logviewer 的 `LogContent` props 类型同步放宽
- 全局 `JSX` 命名空间被移除 → `React.JSX.Element`(AvatarCard)
- `ReactElement` 的 props 默认类型由 `any` 改为 `unknown`,
  TemplateModal / OverrideModal 里 `cloneElement` 注入 onClick 的地方给 `isValidElement`
  标注具体的 props 形状,替代原来的 `<any>`
- 顺带删掉 layout.tsx 里一条已无作用的 `eslint-disable-next-line react-hooks/exhaustive-deps`
  (eslint-plugin-react-hooks 7 对该处不再报告,ESLint 会提示未使用的 disable 指令)

* chore(deps): 升级 Semi Design 2.89→2.103 及 swr/artplayer/mpegts.js,适配 ArrayField.add 新签名

- @douyinfe/semi-ui 2.89.1 → 2.103.0(semi-icons / semi-foundation 随之同步),同一 major,
  用到的 Form / Tabs / Collapse / Modal / Table / Select 等组件 API 未变
- ArrayField 的 `add` 由 `() => void` 变为 `(index?: number) => void`(支持在指定位置插入),
  原来直接写 `onClick={add}` 会把 MouseEvent 当 index 传入且不再通过类型检查,
  7 处改为 `onClick={() => add()}`,行为仍是追加到末尾
- swr 2.3.8 → 2.5.1、artplayer 5.3.0 → 5.4.0、mpegts.js 1.8.0 → 1.8.2:均为 minor/patch,无 API 变更

* chore(deps): 移除无引用的 ncp

`rg ncp` 在 app/、脚本与配置里均无命中(仅 CHANGELOG 提到 #1265 曾把它移出 dependencies),
是早年从 Svelte 前端遗留下来的复制工具,当前构建链(next build → out/ → rust-embed)用不到。

* fix(ui): 站内 Link 关闭预取,消除 Next 16 预取样式触发的 preload 警告

Next 16 的 Link 进入视口即预取目标路由的 RSC 载荷,载荷里的 `HL` 样式提示会让 React 为
目标页的每个 CSS 分块插入 `<link rel="preload" as="style">`;页面停留数秒后 Chrome 会对每个
未使用的预加载报一条 "was preloaded using link preload but not used" 警告。侧栏 9 个链接常驻
视口,配合 Turbopack 的细粒度 CSS 分块,每页会稳定出现 20+ 条(Next 14 下为 0)。

站内导航共 6 处 Link,全部加 `prefetch={false}`:静态导出由本机 axum 直接服务,点击时再拉取
几 KB 的路由载荷,实测 9 个侧栏页面客户端切换均正常且耗时 <150 ms;之后 6 秒停留 0 警告。

本 commit 可独立丢弃:丢弃后功能不受影响,只是控制台会有上述预加载警告。

* fix(ui): 在根布局最先导入 Semi Design 的 React 19 适配器

React 19 从 `react-dom` 上移除了 `render` / `unmountComponentAtNode`,也不再从 `react-dom`
re-export `createRoot`。Semi 的 Toast / Notification / Spin 图标等命令式渲染走
`_utils/reactRender.js`,在 React 19 下既拿不到 legacy render 也拿不到 createRoot,
会打印 "[Semi UI] createRoot is not available" 并**静默不渲染**——表现为保存成功 / 失败的
Toast 与 Notification 全部消失、日志页 Spin 不显示。

按 Semi 官方文档(semi.design/zh-CN/ecosystem/react19)在根布局第一行导入
`@douyinfe/semi-ui/react19-adapter`,它把 `react-dom/client` 的 createRoot 注入
`semiGlobal.config`。验证:空间配置页保存成功 / 校验失败的 Toast 正常弹出,
日志页 console 0 error。

* fix(ui): 消除 React 19 开发模式下的水合属性与 Fragment props 警告

开发模式(StrictMode)逐页走查 13 个页面,React 19 报两类新错误,均为既有写法在新版本下
被更严格地校验,生产构建不受影响:

- 每页一条 "A tree hydrated but some attributes of the server rendered HTML didn't match":
  差异只有 `<html>` / `<body>` 上由 <head> 内联脚本在水合前写入的 theme-mode 属性
  (React 18 下同一原因表现为 "Extra attributes from the server: theme-mode")。
  这是首屏防闪烁的有意行为,按 React 官方做法给这两个元素加 suppressHydrationWarning,
  只屏蔽元素自身的属性差异,不影响子树校验。
- 直播管理页每张卡片一条 "Invalid prop `disabled` supplied to React.Fragment":
  操作按钮以 Fragment 形式作为 Semi ButtonGroup 的唯一子元素,ButtonGroup 会对直接子元素
  cloneElement 注入 disabled / size / type,React 19 起会对 Fragment 上的这些 props 报错。
  改为返回带 key 的元素数组,DOM 结构与样式不变(子元素均非 Button 类型,不会插入分隔线)。

处理后开发模式 13 页 console 仅剩 mock 环境有意返回的 500。
2026-09-22 06:42:07 +00:00
2025-07-16 03:43:05 +08:00
2023-12-08 23:37:40 +08:00
2023-12-31 23:56:40 +08:00
2024-01-17 22:15:17 +08:00
2024-01-27 07:46:15 +00:00
2019-07-28 17:54:29 +08:00
2021-01-20 02:54:06 +08:00

biliup

Python PyPI PyPI - Downloads License Telegram

GitHub Issues GitHub Stars GitHub Forks

🛠️ 功能

  • 提供 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

Linux 或 macOS

  1. 安装 uv
  2. 安装:uv tool install biliup
  3. 启动:biliup server --auth
  4. 访问 WebUI:http://127.0.0.1:19159(默认只监听本机,远程访问见下方说明)

Termux

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

  1. 确保 Node.js 版本 ≥ 18.17(Next.js 14 要求)
  2. 安装依赖:npm i
  3. 启动开发服务器:npm run dev
  4. 访问:http://localhost:3000

Python

  1. 安装依赖 maturin dev
  2. npm run build
  3. 启动 Biliup:python3 -m biliup

Rust-cli

  1. npm run build
  2. 构建 cargo build --release --bin biliup
  3. 开发启动 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, streamlink provides downloader.
  • Thanks THMonster/danmaku.

💴捐赠

爱发电 »

⭐Stars

Star History Chart

S
Description
自动直播录制、投稿、twitch、ytb频道搬运工具。命令行投稿(B站)和视频下载工具,提供多种登录方式,支持多p。边录边传0落盘
https://biliup.github.io/biliup/
Readme MIT
6.7 MiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 75.6%
TypeScript 16.6%
Python 4.8%
SCSS 2%
JavaScript 0.4%
Other 0.5%