资讯动态

BiliTools 机制解析:从 URL 解析到文件落盘的实现路径

发布时间:2026/10/17 2:16:59 来源:尧图企业网站定制
BiliTools 机制解析从 URL 解析到文件落盘的实现路径【免费下载链接】BiliTools本项目已停止维护。项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliToolsBiliTools 是一个基于Tauri v2的哔哩哔哩桌面工具箱当前版本 1.4.7按 README 约定处于稳定维护期前端 Vue 3 TypeScript后端 Rust内嵌 aria2c / ffmpeg / DanmakuFactory 三个 sidecar 二进制。读完本文能回答一次下载请求在前后端之间如何流转、aria2c 侧车的生命周期与断点续传如何实现、WBI 签名与风控回退的代码路径在哪里。技术底座一览模块选型在链路中的角色前端Vue 3 TypeScriptsrc/媒体解析、WBI 签名、风控回退、任务 UI 编排后端Rusttauri 2.9.5、tokio、sqlx SQLitesrc-tauri/src/队列状态机、aria2c 管理、sidecar 编排、本地持久化下载aria2c 侧车JSON-RPC实际字节下载、限速、会话续传后处理ffmpeg 侧车音视频合流、mp3/mp4 转码、元数据写入弹幕转换DanmakuFactory 侧车弹幕 XML → ASS支持特殊弹幕样式持久化SQLitesea-query sqlx配置、Cookie、任务与调度器状态各平台预编译二进制存放于 src-tauri/binaries/Windows / macOS / Linux 各架构默认按可执行文件同目录解析路径逻辑见 src-tauri/src/storage/config.rs 的SettingsSidecar::get_default。从 URL 到落盘主链路走读入口是搜索页。贴入链接后整条链路的调用顺序如下输入识别。parseId 按前缀av/BV/ep/ss/md/au/am/cv与bilibili.com、b23.tv、space.bilibili.com路径模式识别MediaType空间页 URL 进一步区分 UserVideo / UserOpus / UserAudio / Favoriteb23.tv短链先 302 跟随再递归解析。元信息拉取。getMediaInfo 按 11 种MediaType分发接口视频/x/web-interface/view含分 P、ugc_season 合集、互动视频分支、番剧/pgc/view/web/seasonmd前缀会先经/pgc/review/user把media_id换成season_id、课程/pugv/view/web/season、音乐music-service-c系列、收藏夹先取/x/v3/fav/folder/created/list-all再按 FID 拉资源。播放地址获取。getPlayUrl 按登录态取qn登录 127 / 未登录 64、固定fourk: 1并按所选格式切fnvalFLV0、MP41、DASH 登录时 4048、未登录 16。响应归一化为三种形态durlsMP4 多档、durlFLV多qn需并发补拉、dash音视频分离。DASH 模式下音频候选把dash.audio、dash.dolby.audio与dash.flac.audio合并进同一个候选集杜比全景声与无损 FLAC 就是从这里进入选择的。流协商与回退。handleMedia 用 getDefaultQuality 在候选集里找用户默认档res/abr/enc找不到取最高档编码档先精确匹配再按codecid顺序逐个回退。block_pcdn开启时urlFilter按 URL 的os参数把地址分为mirror/upos/bcache三类剔除 PCDN 节点并替换为官方镜像域名。非 DASH 格式下audioVideo子任务会被重分类为video单文件流不需要合流。提交与调度。submit 按勾选项经selectToSubTasks生成子任务列表Video、Audio、AudioVideo、Thumb、HistoryDanmaku、Subtitles、AISummary、NFO 等调backend.commands.submitTask落库并进 Backlog 队列。auto_download开启时processQueue继续调planScheduler一次性清空 Backlog建 Scheduler 进 Pending与processScheduler。后端 Scheduler::dispatch 用RUNTIME.semaphoremax_conc个许可默认 3控并发任务完成后 Scheduler 移入 Complete 并发系统通知。任务执行。Task::process 为任务建临时目录{temp_dir}/{task_id}handle_task 先向 Vue 请求PrepareTask回传videoUrls/audioUrls/subtasks/subFolder与每个子任务的GetFilename再按类型路由Video/Audio走 aria2 下载 后处理AudioVideo从共享的OnceCell取两条流路径做 ffmpeg 合流其余子任务向前端要内容后写文件。每个子任务都包在 try_join 里实现取消 / 暂停 / 重试。下载与落盘。aria2c::download 以 subtask id 作为 aria2 gidaddUri指定临时目录与文件名随后每秒tellStatus轮询经subtask.send(content, chunk)推进度到前端并写库完成后post_media依abr决定扩展名30250 →eac330251/30252 →flac其余m4a合流档对应mkv/mp4按需走convert_mp3/convert_mp4/add_meta最后fs::copy进目标文件夹get_unique_path 处理重名。弹幕、NFO、SRT、AI 摘要等文件也落同一文件夹。前后端之间的反向调用值得单独说明。frontend::request 的工作方式Rust 侧开一个 oneshot channel、用app.once(endpoint, ...)注册一次性监听emitQueueEvent::Request后挂起等待Vue 侧 handleEvent 收到request事件后按RequestAction分发到getPlayUrl、getDanmaku、getSubtitle等实现结果经app.emit(endpoint, result)回传。所有 B 站 API 调用、弹幕 XML 拼装、SRT 重排、NFO 生成都走这条通道而不是 Rust 直接发 HTTP。原因见下一节。关键实现决策为什么解析与签名留在 webviewRust 只管进程和 IORust 侧完全可以用 reqwest 复刻全部 B 站接口但有三件事只能在 webview 里做WBI 签名需要 canvas 的 WebGL 指纹getWebGLFingerPrint风控回退需要 Geetest 在 DOM 里交互window.initGeetest弹幕解析与 NFO 生成依赖DOMParser/XMLSerializer。反过来让 Rust 承担 HTTP 栈就要再实现一遍风控回退逻辑。所以划分是webview 当解析与签名执行器Rust 当队列状态机 进程编排器frontend::request事件通道就是两者之间的 RPC。代价是调试链路变长——一次下载请求要跨进程往返两次PrepareTask与GetFilename。aria2c 侧车生命周期、断点续传与孤儿进程启动时 aria2c::init 用TcpListener::bind(127.0.0.1: 0)抢一个空闲端口生成 8 位随机rpc-secret以 sidecar 方式拉起进程。固定参数包括--refererhttps://www.bilibili.com/与Origin头规避防盗链、--user-agent写死为桌面 Chrome UA、--max-download-limit{speed_limit}K、--pausetrue所有任务默认暂停态由handle_media的CtrlEvent::Resume分支逐个unpause、--input-file/--save-session指向数据目录下aria2.session且--save-session-interval5。续传机制的关键是gid 稳定gid 直接取 subtask id前端randomString(8)生成入库后不再变。任务重跑retry时先tellStatus命中complete直接跳过、paused直接unpause、任务不存在才addUri。轮询循环里错误码 31connection refused被显式忽略等待重试。配合每 5 秒落盘的会话文件应用重启后未完成任务可以基于同一会话恢复。其他细节RPC 客户端单独no_proxy用户代理不应作用于本机回环、3 秒超时、HTTP/1 onlydaemon协程每 3 秒getGlobalStat保活并监视 stdout/stderr进程意外退出会经process_err上报前端Windows 上额外起一个 PowerShell 轮询宿主 PID宿主先退出时强杀 aria2c防止孤儿进程。取消子任务时依次purgeDownloadResult/removeDownloadResult/forceRemove所有取消时该杀什么统一注册为CtrlHandle上的 cleaner杀子进程、删临时目录、清 aria2 任务取消与 retry 时由clean_all批量执行。WBI 签名、风控回退与 Cookie 生命周期签名实现在 wbi()每次请求都从/x/web-interface/nav取wbi_img的 img / sub key经固定 64 位置换表混出 32 位mixinKeyquery 按键名排序、值过滤[!()*]后拼接w_rid md5(query mixinKey)。签名同时注入wts与 WebGL 指纹字段dm_img_str/dm_cover_img_strcanvas WebGL version 与 unmasked renderer 的 base64 截断。注意这意味着每次 WBI 请求都会附带一次nav调用。风控回退集中在 tryFetch响应code -352时取data.v_voucher连同 Cookie 里的bili_jct作 csrf 调/x/gaia-vgate/v1/register拿 geetest 参数弹 Geetest 验证后经/x/gaia-vgate/v1/validate取得grisk_id随机延迟 100~500ms 后带gaia_vtoken参数重试默认最多 3 轮。弹幕分段拉取里也有同样的 100~500ms 随机间隔。票据与登录态在后端 login.rsget_bili_ticket用 HMAC-SHA256keyXgwSnGZ1p消息ts{ts}生成hexsign调GenWebTicket换取bili_ticketcookie扫码登录后端以 1 秒间隔轮询qrcode/poll经 TauriChannel把 code 推给前端code 0时把Set-Cookie与refresh_token写入cookies表并HEADERS.refresh()刷新全局请求头refresh_cookie是两步提交——cookie/refresh换发后再confirm/refresh确认。前端checkRefresh在每次拉用户信息前检查/x/passport-login/web/cookie/info的refresh标志需要刷新时先从/correspond/1/{path}页面提取refresh_csrfrefresh_{timestamp}经 RSA-OAEP 加密得到路径段。Cookie 统一存cookies表name 主键insert解析path/domain/expires/httponly/secure内存中由HEADERS静态结构维护并同步给前端。配置与数据足迹数据根目录README 与代码一致WORKING_PATH app_data_dir()平台路径Windows%AppData%\com.btjawa.bilitoolsmacOS$HOME/Library/Application Support/com.btjawa.bilitoolsLinux$HOME/.local/share/com.btjawa.bilitools该目录下的Storage子目录存放 SQLite 库含config、cookies、tasks、schedulers、queue五张表建表语句见 src-tauri/src/storage/aria2.session与DanmakuFactory.json也在数据目录aria2 日志在平台 log 目录。任务临时文件位于temp_dir/com.btjawa.bilitools/{task_id}任务结束即删。主要配置项默认值定义于 src-tauri/src/shared.rs 的CONFIG运行时存config表并按 key 覆盖默认max_conc并发任务数默认 3写入时经 RUNTIME.new_conc 即时调整信号量down_dir下载根目录默认桌面speed_limitaria2 限速KB/s默认 0 不限启动时写入--max-download-limit改动后需重启生效block_pcdn默认 truePCDN 地址过滤add_metadata默认 trueffmpeg 写入标题 / 简介 / 封面 /original_url等convert.danmaku/convert.mp3/convert.mp4默认 true / false / falsedefault.res/abr/enc默认 80 / 30280 / 7auto_download默认 false提交任务后不自动开跑organize.auto_rename默认 true重名文件自动加_1、_2序号format.series/item/file三级命名模板支持downtime:YYYY-MM-DD_HH-mm-ss这类 strftime 后缀边界项目以 GPL-3.0-or-later 开源再分发必须保持开源、沿用相同许可证并保留全部原始版权信息见 LICENSE 与 README_EN.md。不绕过鉴权、不破解付费资源VIP / 付费内容仅对应已订阅账号与 Bilibili 无隶属、合作、授权或背书关系哔哩哔哩商标归上海幻电信息科技获取内容的版权归原权利方项目定位为学习与测试用途使用后果由用户自行承担设计取向BiliTools 把易碎的部分放在 webview、把稳定的部分放在 Rust解析与签名依赖 DOM 和风控环境留在 Vue 侧进程编排、SQLite 状态机与 sidecar 生命周期下沉 Rust且每处状态变更都是内存 前端事件 数据库三写换来崩溃或重启后队列可恢复interrupted判定 restore重新 dispatch。延伸阅读见 README_EN.md 与 docs/guide/ 下的安装、解析、登录等章节。【免费下载链接】BiliTools本项目已停止维护。项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑