bili-dl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,36 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.13"
17
+ cache: pip
18
+ - run: pip install ruff
19
+ - run: ruff check src tests
20
+ - run: ruff format --check src tests
21
+
22
+ test:
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ os: [ubuntu-latest, windows-latest, macos-latest]
27
+ python-version: ["3.9", "3.13"]
28
+ runs-on: ${{ matrix.os }}
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ cache: pip
35
+ - run: pip install -e . pytest
36
+ - run: pytest
@@ -0,0 +1,48 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distribution / packaging
8
+ build/
9
+ dist/
10
+ *.egg-info/
11
+ .eggs/
12
+ wheels/
13
+ *.egg
14
+ MANIFEST
15
+
16
+ # Virtual envs
17
+ .venv/
18
+ venv/
19
+ env/
20
+
21
+ # Test / coverage
22
+ .pytest_cache/
23
+ .mypy_cache/
24
+ .ruff_cache/
25
+ htmlcov/
26
+ .coverage
27
+ .coverage.*
28
+ coverage.xml
29
+
30
+ # IDE / editor
31
+ .idea/
32
+ .vscode/
33
+ *.swp
34
+ *.swo
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
39
+
40
+ # Cookies / runtime secrets — NEVER commit
41
+ cookies_all.txt
42
+ cookies_bilibili.txt
43
+ cookies_bilibili.txt.bak_*
44
+ *.m4a
45
+ *.mp4
46
+ *.mkv
47
+ *.webm
48
+ !tests/data/*.txt
@@ -0,0 +1 @@
1
+ 3.9
@@ -0,0 +1,168 @@
1
+ # AGENTS.md — bili-dl 项目记忆
2
+
3
+ > 本文件沉淀"读代码难悟到"的项目事实;与全局规则配合使用。
4
+
5
+ ## 1. 技术栈
6
+
7
+ - **语言**:Python 3.9+(目标 3.9 兼容,CI 跑 3.9/3.13)
8
+ - **构建**:hatchling(`pyproject.toml` 声明,`src/` 布局)
9
+ - **运行时依赖**:**零** —— 仅用标准库(`subprocess`、`urllib`、`pathlib`、`ctypes`、`argparse`、`json`、`shutil`)。
10
+ - **外部程序依赖**:`yt-dlp`(必需,找不到则报错退出)、`ffmpeg`(可选,缺失则降级跳过音频提取/容器修复)。
11
+ - **工具链**:ruff(lint+format)、pytest(测试)。无 mypy 强制。
12
+ - **分发**:目标 PyPI,包名 `bili-dl`,脚本入口 `bili-dl`。
13
+
14
+ ## 2. 已踩通的坑
15
+
16
+ ### 2.1 原 bd.ps1 的 PowerShell 作用域 bug(重构根因)
17
+ - **现象**:`bd.ps1` 在主流程定义 `$commonOpt`,在函数 `Invoke-DownloadVideo` 内用 `@commonOpt` 引用;PowerShell 函数默认读不到调用者作用域的普通变量,导致 Cookie/Referer 参数被静默丢弃,非会员 1080P 实际下载退化为匿名。
18
+ - **根因**:作用域隔离 + 静默失败。
19
+ - **解法**:Python 版把 common 选项作为显式 `list[str]` 传入 `downloader.download()`,参数显式 threading,该 bug 类无法复现。位置:`src/bili_dl/downloader.py` 的 `_common_args()` + `download()` 签名。
20
+ - **教训**:任何"胶水脚本把公共参数定义在一处、函数内隐式引用"的设计都要在移植时显式化。
21
+
22
+ ### 2.2 ffmpeg List[string].AddRange 在 PowerShell 不可用
23
+ - 原 bd.ps1 重构时用 `New-Object System.Collections.Generic.List[string]` + `.AddRange(@(...))` 会抛 `MethodException`:`Object[]` 无法转 `IEnumerable[string]`。
24
+ - 解法:改用 PowerShell 原生数组 `@(...) += @(...)`。Python 版无此问题。
25
+
26
+ ### 2.3 音频容器标准化(foobar2000 兼容)必须覆盖两条路径
27
+ - **现象**:B 站 DASH 音频原始容器 `moov` 在尾、`major_brand=M4A`,foobar2000 起播慢或比特率显示异常。
28
+ - **根因**:容器布局,非编码问题。
29
+ - **解法**:`ffmpeg -i in -map 0:a -c:a copy -map_metadata 0 -movflags +faststart out`,零损失重封装为 `moov` 前置的 isom 容器。
30
+ - **关键**:`all` 模式(从视频抽流)和 `a` 模式(直接下载音频)**两条路径都要走 `repair_audio_container`**。这是本项目的"产品差异化"。
31
+ - **验证**:ffprobe 看到 `compatible_brands=M4A isom iso2`,`moov` 在 offset 36(紧跟 ftyp)。
32
+ - **位置**:`src/bili_dl/ffmpeg.py` 的 `repair_audio_container()` / `extract_audio()`,后者提取后必调前者。
33
+
34
+ ### 2.4 Cookie 隐私模型(核心不变量)
35
+ - 永远只从 `cookies_all.txt` 提取含 `bilibili` 的行;其他站点 Cookie 不解析、不存储、不外发。这是项目隐私承诺,任何 PR 不得破坏。
36
+ - 位置:`src/bili_dl/cookies.py` 的 `import_bili_cookie_from_all()` —— 用列表推导显式过滤 `"bilibili" in line`。
37
+ - 测试锚定:`tests/test_cookies.py` 的 `test_extract_drops_other_sites()` 断言 `other_secret` 不泄漏。
38
+
39
+ ### 2.5 nav API 在线校验的降级策略
40
+ - **现象**:在线校验 Cookie 需调 `https://api.bilibili.com/x/web-interface/nav`,但网络/SSL 错误时不能因此阻断下载(本地格式可能仍有效)。
41
+ - **解法**:`_online_check()` 返回 `True`(已登录)/`False`(未登录)/`None`(网络错误);`None` 时降级为仅本地格式校验并打印警告,仍返回 True。
42
+ - 位置:`src/bili_dl/cookies.py::_online_check()` + `test_cookie_valid()`。
43
+
44
+ ### 2.6 nav API 必须伪装浏览器 User-Agent(412 根因,非 SSL)
45
+ - **现象**:Python 版上线后实测每次都走降级("无法在线验证 Cookie ... 降级为本地格式校验"),而原 bd.ps1 PowerShell 的 `Invoke-RestMethod` 一直能成功显示已登录用户名。曾被误判为本机证书链问题。
46
+ - **根因**:B 站 nav API 对 urllib 默认 UA `Python-urllib/3.x` 返回 **HTTP 412 Precondition Failed**(反爬)。`Invoke-RestMethod` 内部默认带 PowerShell/browser UA 故一直成功。代码的 `except Exception` 把 412 也吞进"网络/SSL 错误"分支,导致降级提示与真实根因描述不符——既误导用户也让 SSL 甩锅。
47
+ - **解法**:`config.py::USER_AGENT` 放一个固定 Chrome UA 字符串;`cookies.py` 的两处 `urllib.request.Request` header 都加 `"User-Agent": USER_AGENT`。仅 nav 探测用,yt-dlp 下载阶段自带 UA。
48
+ - **验证**:实跑 `bili-dl`(无 URL)开头即显示 `[OK] Cookie 有效 | 已登录: <uname>`。
49
+ - **启示**:`except Exception` 太宽会把业务级 HTTP 错误(412/403/404)混进"网络问题"分支。后续若想精确分级,可在 `_online_check` 单独 catch `urllib.error.HTTPError` 拿 status,把 412/403 报为"被风控"而非"网络错误"。当前简单加 UA 已解。
50
+ - **关联**:用户曾因 `-k` 也无效而怀疑证书;`-k` 只影响 yt-dlp 的 `--no-check-certificate`,**对 urllib 探测无影响**(urllib 默认就校验,且本机 CA 没问题)。下次有人误以为证书,先查 UA。
51
+
52
+ ### 2.7 TLS 证书校验默认开(安全硬化)
53
+ - 原 bd.ps1 无条件 `--no-check-certificate` 是降级项;Python 版默认启用校验,仅 `-k/--insecure` 显式关闭,用于自签证书环境。
54
+ - 位置:`cli.py` 的 `--insecure` 参数 → `downloader.py::_common_args()` 条件追加 `--no-check-certificate`。
55
+
56
+ ### 2.8 跨平台路径不绑平台 API
57
+ - 原 bd.ps1 用 `[Environment]::GetFolderPath('MyVideos')` 是 Windows 专属;Python 版 `paths.py` 用 `sys.platform` 分支 + XDG 约定,同一代码三平台通用。
58
+ - Windows 用 `%APPDATA%\bili-dl` 存 cookie,`~/Videos` 改为 `Path.home()/"Videos"`(避免 SHGetKnownFolderPath 零依赖约束)。
59
+
60
+ ### 2.9 Windows CJK 编码策略:绝不强制 UTF-8(踩坑沉淀)
61
+ - **现象**:初版曾加 `subprocess.run(..., encoding="utf-8")` 解码 yt-dlp 的 `--print filename` 输出 + `sys.stdout.reconfigure("utf-8")`。结果**任何非 ASCII 标题的下载都 `[失败]`**:phase1 预测路径与 phase2 yt-dlp 实际写入磁盘的文件名不一致,`out_path.exists()` 返回 False。
62
+ - **根因**:yt-dlp 在 Windows 默认按系统 locale(cp936/gbk)编码 stdout。强制用 UTF-8 解码 cp936 字节得到的是真·乱码 unicode,与 yt-dlp 用 Win32 UTF-16 写入磁盘的正确文件名不匹配;反之默认 locale 解码(yt-dlp encode 与 Python decode 同为 cp936)虽然 unicode 含 GBK 外字符会丢字符,但 predict 路径字符串与磁盘文件名(同样经 yt-dlp encode)完全一致,`exists()` 稳定 True。
63
+ - **解法(已固化)**:`src/bili_dl/cli.py` 顶部注释明确"**不** reconfigure stdio、**不**设 `PYTHONUTF8`",`downloader.py` predict 用 `subprocess.run(..., text=True)` **不传 encoding**,让两侧都用宿主默认 locale。
64
+ - **权衡**:常见汉字全在 GBK 范围内(99% 标题无丢失),少数生僻字/emoji 在 yt-dlp 内部已 replace,不影响路径定位。这是最少惊讶、跨平台最稳的方案。**任何"加 UTF-8 更现代"的 PR 都必须先验证 CJK 标题下载不破。**
65
+ - **测试锚定**:暂无单测(需真实 yt-dlp 子进程);集成验证靠实跑 BV1froEBxEcX(《春死诀》全 CJK 标题)确认 `[完成!]` 而非 `[失败]`。
66
+ - **环境差异**:bd.ps1 原版在 PowerShell 下靠 `chcp 65001 + PYTHONUTF8=1` 强制全程 UTF-8(PowerShell 调子进程的固有编码坑);纯 Python 跨平台版不沿用,因 Python 与终端/yt-dlp 默认 locale 自洽。
67
+
68
+ ### 2.10 `-v` 被占用,`--version` 用 `-V`(CLI 短选项冲突)
69
+ - **问题**:`-v` 已用于 `--video`(仅视频模式),再加 `-v/--version` 会被 argparse 拒绝(mutually exclusive group 与 version action 重复 dest)。
70
+ - **惯例**:`yt-dlp`、`curl`、`pip` 等都用大写 `-V` 表示 `--version`,与 `-v`/`--verbose` 区分。沿用此惯例用户零学习成本。
71
+ - **解法**:`cli.py::_build_parser()` 中 `--version` 用 `action="version"`,短选项 `-V`(不进 mutually exclusive group,否则会与 mode 冲突触发 `SystemExit(0)` 之外的限制验证)。
72
+ - **实测**:`bili-dl -V` → `bili-dl 0.1.0`;`bili-dl --version` 同效;`bili-dl -v <url>` 仍仅视频模式。
73
+ - **教训**:CLI 短选项是稀缺资源(26 字母),命名时优先排雷 `-V/--version`、`-v/--verbose`、`-h/--help` 这类高频占用。本工具占用清单:`-a/-v` 模式、`-k` insecure、`-V` version、`-h` help。
74
+
75
+ ## 3. 项目结构
76
+
77
+ ```
78
+ bili-dl/
79
+ ├── pyproject.toml # hatchling + ruff + pytest 配置(单文件)
80
+ ├── README.md # 宣传门面(务必随版本同步功能表)
81
+ ├── CHANGELOG.md # Keep a Changelog 格式
82
+ ├── LICENSE # MIT + 依赖合规说明
83
+ ├── .python-version # pyenv/uv 用,固定 3.9(向下兼容基准)
84
+ ├── .gitignore # 含 cookies_*.txt 与媒体文件,防泄密
85
+ ├── AGENTS.md # 本文件
86
+ ├── src/bili_dl/
87
+ │ ├── __init__.py # __version__
88
+ │ ├── __main__.py # python -m bili_dl
89
+ │ ├── cli.py # argparse + REPL + main()(主入口)
90
+ │ ├── config.py # 纯常量,无可变状态
91
+ │ ├── paths.py # 跨平台路径(Win/macOS/Linux)
92
+ │ ├── cookies.py # Netscape 提取 + 在线/本地校验
93
+ │ ├── ffmpeg.py # ffprobe/ffmpeg 探测 + 零损失重封装/提取
94
+ │ ├── downloader.py # yt-dlp 两阶段下载(预测路径 → 实下)
95
+ │ └── ui.py # ANSI 彩色输出(Win10+ 启用 VT)
96
+ ├── tests/
97
+ │ ├── test_cookies.py # 隐私核心测试(其他站点不泄漏)
98
+ │ ├── test_paths.py # 跨平台路径分支(mock platform)
99
+ │ ├── test_package.py # 包导入冒烟测试
100
+ │ └── data/sample_cookies_all.txt
101
+ └── .github/workflows/ci.yml # Win/macOS/Linux × Py3.9/3.13 矩阵
102
+ ```
103
+
104
+ ## 4. 关键约定
105
+
106
+ ### 4.1 依赖方向
107
+ `cli → downloader → ffmpeg`;`cli → cookies`;`cli → paths → config`;`ffmpeg/downloader → ui/config`。
108
+ - `config` 是叶节点(只导出常量),任何模块可依赖它,它不依赖任何内部模块。
109
+ - `ui` 也接近叶节点(仅 `mode_label` 懒导入 `config`)。
110
+ - 禁止反向依赖或循环导入。
111
+
112
+ ### 4.2 零运行时依赖(硬约束)
113
+ - 不可引入 `requests`/`colorama`/`rich` 等三方包。HTTP 用 `urllib.request`,彩色输出用 ANSI + `ctypes`,路径用 `pathlib`。
114
+ - 任何"加个依赖更方便"的提案都需先权衡"零依赖"这个卖点。
115
+
116
+ ### 4.3 命名
117
+ - Python 模块用 `snake_case`;CLI 旗帜沿袭 Unix 惯例(`--all/-v/-a` 模式、`--proxy`、`-k/--insecure`、`-V/--version`)。短选项占用清单见 §2.10。
118
+ - yt-dlp format 串集中放 `config.py`(`FMT_AV`/`FMT_AUDIO`),不散落。
119
+
120
+ ### 4.4 提交规范
121
+ - Conventional Commits 中文风格可接受(项目面向中文用户为主),但英文 commit 便于国际贡献者,建议英文。
122
+ - 任何改动到 cookies.py / ffmpeg.py 者必须跑 `tests/test_cookies.py` / 相关测试,不得破坏隐私断言。
123
+
124
+ ## 5. 常用命令
125
+
126
+ ```bash
127
+ # 开发安装
128
+ pip install -e .
129
+
130
+ # 检验
131
+ ruff check src tests
132
+ ruff format --check src tests
133
+ pytest # 或 pytest -q
134
+
135
+ # 实测下载(需 yt-dlp + ffmpeg,且 cookie 目录有可用 cookies_bilibili.txt)
136
+ bili-dl https://www.bilibili.com/video/BVxxxxx
137
+ bili-dl -a https://www.bilibili.com/video/BVxxxxx # 验证音频 faststart
138
+
139
+ # ffprobe 检查产物(验证 moov 在前 + isom 容器)
140
+ ffprobe -v error -show_entries format=format_name:format_tags=major_brand,compatible_brands path.m4a
141
+ ffmpeg -v trace -i path.m4a -f null - 2>&1 | findstr /R "moov mdat" # Win
142
+ ffmpeg -v trace -i path.m4a -f null - 2>&1 | grep -E "moov|mdat" # Unix
143
+
144
+ # 构建
145
+ pip install build
146
+ python -m build # dist/ 出 wheel + sdist
147
+ ```
148
+
149
+ ## 6. 环境特异事实(开发者备注)
150
+
151
+ - **gh / git SSL**:本机证书链存在问题,但实测 `gh api user` 与 SSH 协议 git 操作均正常(账号 Echoziness,git protocol=ssh)。若将来 gh API 失败,再考虑 `git config --global http.sslVerify false`(高风险,需用户批准)。当前无需关闭。
152
+ - **SSH 22 端口被封,走 443**:`git push` 实测 `ssh: connect to host github.com port 22: Connection refused`。解法:`git remote set-url origin ssh://git@ssh.github.com:443/Echoziness/bili-dl.git`,首次 push 用 `GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new"` 登记 `ssh.github.com:443` 的 host key(ED25519)。此 remote URL 已固化在本地仓库,后续 push 无需再处理。不改全局 config,避免影响其他仓库。
153
+ - **PowerShell 编码**:PowerShell 调子进程时需注入 `chcp 65001` + UTF8,但纯 Python 跨平台版不涉及此(Python3 默认 UTF-8)。此条仅对 bd.ps1 维护有意义。
154
+
155
+ ## 7. 发布 checklist
156
+
157
+ ### 已完成(v0.1.0 首发上线)
158
+ - [x] 替换 `pyproject.toml` 中 `authors`、`project.urls` 占位(handle=Echoziness,邮箱用 GitHub noreply)
159
+ - [x] 替换 README 徽章 / CHANGELOG 链接中的占位 handle
160
+ - [x] GitHub 建仓库 `Echoziness/bili-dl`(PUBLIC)、推送 main、CI 三平台全绿
161
+ - [x] 占位符清零扫描、敏感文件扫描(无 cookie/媒体进仓库)、lint/format/test 全绿
162
+
163
+ ### 待办(发 PyPI)
164
+ - [ ] `pip install build && python -m build` 生成 dist(wheel + sdist)
165
+ - [ ] `pip install twine && twine check dist/*` 通过
166
+ - [ ] `twine upload --repository testpypi dist/*` 先发 TestPyPI,验证 `pipx install -i https://test.pypi.org/simple/ bili-dl` 能装能跑
167
+ - [ ] 正式 `twine upload dist/*` 发 PyPI
168
+ - [ ] 打 `v0.1.0` tag 并推送,触发 GitHub Release(`gh release create v0.1.0 --notes-from-tag`)
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Initial public release.
12
+ - Three download modes: `all` (video+audio merged to MP4), `v` (video only),
13
+ `a` (audio only, M4A).
14
+ - Cookie management: extract only `.bilibili.com` entries from a
15
+ `cookies_all.txt` Netscape export into a dedicated `cookies_bilibili.txt`,
16
+ with on-disk backup before overwrite.
17
+ - Online Cookie validity check via the Bilibili `/x/web-interface/nav` API
18
+ with graceful degradation to local-only validation on network errors.
19
+ - Audio container standardization via ffmpeg zero-copy remux
20
+ (`-c:a copy -movflags +faststart`) for both `all` and `a` paths —
21
+ produces `moov`-first ISOM containers friendly to foobar2000 and others.
22
+ - Cross-platform path defaults (Windows Videos/Music, macOS Movies/Music,
23
+ XDG `~/.local/share/bili-dl` elsewhere).
24
+ - Interactive REPL mode plus one-shot non-interactive mode (`bili-dl <URL>`).
25
+ - CLI flags: `--all/-v/-a` modes, `--proxy`, `--insecure/-k`,
26
+ `--output-dir/--audio-dir`, `--cookie-dir`.
27
+ - TLS certificate verification enabled by default; opt-out via `-k`.
28
+ - MIT-licensed, zero runtime Python dependencies (stdlib only).
29
+
30
+ ### Fixed
31
+ - nav API probe now sends a browser User-Agent; previously Bilibili returned
32
+ HTTP 412 to `Python-urllib/x.y` and the broad `except` silently degraded to
33
+ local-only validation, masking the real cause as a "network/SSL error".
34
+
35
+ ### Changed
36
+ - `--version` is now exposed as `-V` (capital), since `-v` is taken by
37
+ `--video`. Matches yt-dlp / curl / pip convention.
38
+
39
+ [Unreleased]: https://github.com/Echoziness/bili-dl/releases/tag/v0.1.0
bili_dl-0.1.0/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bili-dl contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ NOTE: This tool is a thin wrapper around yt-dlp (Unlicense) and ffmpeg
24
+ (LGPL/GPL). Users must install those separately. Limitations or obligations
25
+ imposed by those licenses on the binaries (e.g. ffmpeg's GPL build) may apply
26
+ to the binaries you use but do not affect this wrapper's MIT license.
bili_dl-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.4
2
+ Name: bili-dl
3
+ Version: 0.1.0
4
+ Summary: Cross-platform Bilibili video/audio downloader built on yt-dlp + ffmpeg.
5
+ Project-URL: Homepage, https://github.com/Echoziness/bili-dl
6
+ Project-URL: Repository, https://github.com/Echoziness/bili-dl
7
+ Project-URL: Issues, https://github.com/Echoziness/bili-dl/issues
8
+ Project-URL: Changelog, https://github.com/Echoziness/bili-dl/blob/main/CHANGELOG.md
9
+ Author-email: Echoziness <217080389+Echoziness@users.noreply.github.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: audio,bilibili,downloader,ffmpeg,video,yt-dlp
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Multimedia :: Video
27
+ Classifier: Topic :: Utilities
28
+ Requires-Python: >=3.9
29
+ Description-Content-Type: text/markdown
30
+
31
+ # bili-dl
32
+
33
+ > Cross-platform Bilibili downloader — a thin, fast wrapper around `yt-dlp` + `ffmpeg`.
34
+
35
+ [![CI](https://github.com/Echoziness/bili-dl/actions/workflows/ci.yml/badge.svg)](https://github.com/Echoziness/bili-dl/actions/workflows/ci.yml)
36
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
37
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
38
+
39
+ `bili-dl` is a small command-line tool for downloading Bilibili videos and
40
+ audio with the best available quality (up to 1080p for non-premium accounts).
41
+ It started life as a Windows PowerShell script (`bd.ps1`) and was rewritten in
42
+ pure Python so the **same code runs unchanged on Windows, macOS and Linux**,
43
+ with **zero runtime dependencies** (standard library only).
44
+
45
+ It does one thing well: pick the right `yt-dlp` format, manage your Bilibili
46
+ cookie safely, and standardise every audio file into a foobar2000-friendly
47
+ container — all without re-encoding.
48
+
49
+ ---
50
+
51
+ ## Why
52
+
53
+ - **Cookie-safe.** Drop a full-browser `cookies_all.txt` export next to the
54
+ tool and it extracts *only* the `.bilibili.com` entries into a dedicated
55
+ file. Cookies for every other site are never parsed, stored, or sent
56
+ anywhere.
57
+ - **Cookie-verified.** Before downloading it pokes the Bilibili `nav` API to
58
+ confirm your session is actually logged in, falling back to a local check
59
+ when the network is unavailable.
60
+ - **Audio you can actually play.** Both the "audio only" download and the
61
+ audio extracted from a video are routed through `ffmpeg -c:a copy
62
+ -movflags +faststart` — a zero-loss remux that produces a `moov`-first
63
+ ISOM container. foobar2000 and friends play it instantly without bitrate
64
+ quirks from Bilibili's raw mux.
65
+ - **No quality downgrade bugs.** A scoping bug in the original PowerShell
66
+ version meant the cookie/referer args were silently dropped inside the
67
+ download function, quietly capping quality. The rewrite passes all options
68
+ explicitly, so this class of bug can't recur.
69
+ - **Zero install footprint beyond the tools you already have.** No `pip`
70
+ dependencies, no `requests`, no `colorama` — just the standard library. If
71
+ you have Python installed, `pipx install bili-dl` is enough.
72
+
73
+ ---
74
+
75
+ ## Install
76
+
77
+ `bili-dl` needs `yt-dlp` and `ffmpeg` on your `PATH`. Both are one-liners:
78
+
79
+ ```bash
80
+ pip install -U yt-dlp # or: winget install yt-dlp / brew install yt-dlp / pipx install yt-dlp
81
+ # ffmpeg:
82
+ winget install ffmpeg # Windows
83
+ brew install ffmpeg # macOS
84
+ sudo apt install ffmpeg # Debian/Ubuntu
85
+ ```
86
+
87
+ Then install bili-dl itself:
88
+
89
+ ```bash
90
+ pipx install bili-dl # recommended: isolated, exposes the `bili-dl` command
91
+ # or, if you must:
92
+ pip install bili-dl
93
+ ```
94
+
95
+ Verify:
96
+
97
+ ```bash
98
+ bili-dl --help
99
+ bili-dl -V # show version
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Quick start
105
+
106
+ ### Cookies (one-time)
107
+
108
+ Bilibili returns HTTP 412 to anonymous requests, so you need a login cookie.
109
+ On Windows `yt-dlp --cookies-from-browser` can't read Chromium's App-Bound
110
+ Encryption, so export manually:
111
+
112
+ 1. Install the [Get cookies.txt LOCALLY](https://microsoftedge.microsoft.com/addons/detail/get-cookies-txt-locally/ccpbcjjkcbiojbicneopklbjmhklbpca) Edge/Chrome extension.
113
+ 2. Log in to [bilibili.com](https://www.bilibili.com).
114
+ 3. Export **All Cookies** in Netscape format.
115
+ 4. Save as `cookies_all.txt` in the cookie directory (see below).
116
+ 5. Run `bili-dl` once — it extracts only the `.bilibili.com` entries into
117
+ `cookies_bilibili.txt` and reuses it thereafter.
118
+
119
+ Cookie directory defaults to:
120
+
121
+ | OS | Path |
122
+ |---|---|
123
+ | Windows | `%APPDATA%\bili-dl` |
124
+ | macOS | `~/Library/Application Support/bili-dl` |
125
+ | Linux | `~/.config/bili-dl` |
126
+
127
+ Override with `--cookie-dir`.
128
+
129
+ ### Download
130
+
131
+ ```bash
132
+ # interactive REPL — paste URLs, switch modes with all/v/a, quit with q
133
+ bili-dl
134
+
135
+ # one-shot
136
+ bili-dl https://www.bilibili.com/video/BVxxxxxxxx
137
+
138
+ # audio only → standalone, foobar2000-friendly M4A
139
+ bili-dl -a https://www.bilibili.com/video/BVxxxxxxxx
140
+
141
+ # video only (single-file MP4 when available)
142
+ bili-dl -v https://www.bilibili.com/video/BVxxxxxxxx
143
+
144
+ # through a proxy, skipping TLS verification for a self-signed environment
145
+ bili-dl --proxy http://127.0.0.1:7890 -k https://www.bilibili.com/video/BVxxxxxxxx
146
+ ```
147
+
148
+ Download destinations default to:
149
+
150
+ | OS | Videos | Audio |
151
+ |---|---|---|
152
+ | Windows | `~/Videos/bilibili_videos` | `~/Music/bilibili_audio` |
153
+ | macOS | `~/Movies/bilibili_videos` | `~/Music/bilibili_audio` |
154
+ | Linux | `~/Downloads/bilibili_videos` | `~/.local/share/bili-dl/audio` |
155
+
156
+ Override with `--output-dir` / `--audio-dir`.
157
+
158
+ ---
159
+
160
+ ## Modes
161
+
162
+ | Mode | Flag | What it downloads | Container |
163
+ |---|---|---|---|
164
+ | **all** | `--all` (default) | best video + best audio, merged | MP4 + extracted M4A |
165
+ | **video** | `-v` | video (single-file MP4 if a combined stream exists) | MP4 |
166
+ | **audio** | `-a` | best audio stream | M4A (faststart ISOM) |
167
+
168
+ In `all` mode the merged MP4 is kept in the videos directory **and** a
169
+ zero-loss M4A is extracted to the audio directory. Both audio paths (direct
170
+ download *and* extraction) go through the same ffmpeg remux, so they're
171
+ byte-layout-identical.
172
+
173
+ ---
174
+
175
+ ## How audio standardisation works
176
+
177
+ Bilibili serves DASH audio with `moov` at the tail and an `M4A` major brand.
178
+ Some players (notably foobar2000) read bitrate metadata incorrectly or show
179
+ a noticeable start delay. `bili-dl` runs:
180
+
181
+ ```
182
+ ffmpeg -i in.m4a -map 0:a -c:a copy -map_metadata 0 -movflags +faststart out.m4a
183
+ ```
184
+
185
+ `-c:a copy` means **no re-encode** — the AAC frames are copied verbatim, so
186
+ bitrate (e.g. 201 kbps for the 203k stream) is preserved exactly. Only the
187
+ container layout changes: `moov` moves to the front for instant playback, and
188
+ the compatible brand set becomes `M4A isom iso2`. Net effect: same audio,
189
+ friendlier file.
190
+
191
+ ---
192
+
193
+ ## Privacy
194
+
195
+ - The cookie importer reads `cookies_all.txt` line by line and keeps **only**
196
+ lines containing `bilibili`. Lines for other domains are discarded in
197
+ memory; they're never written to disk or sent over the network.
198
+ - The only network requests `bili-dl` makes go to `api.bilibili.com` (the
199
+ login probe) and the URLs you give it (via `yt-dlp`). No telemetry, no
200
+ analytics, no "phone home".
201
+ - `cookies_bilibili.txt` is created with restrictive handling and a timestamped
202
+ backup is kept before any overwrite.
203
+
204
+ ---
205
+
206
+ ## Project layout
207
+
208
+ ```
209
+ bili-dl/
210
+ ├── src/bili_dl/
211
+ │ ├── __init__.py # version
212
+ │ ├── __main__.py # python -m bili_dl
213
+ │ ├── cli.py # argparse + REPL entry point
214
+ │ ├── config.py # constants (endpoints, format strings, labels)
215
+ │ ├── paths.py # cross-platform path resolution (Win/mac/Linux)
216
+ │ ├── cookies.py # Netscape extraction + (online) validity check
217
+ │ ├── ffmpeg.py # ffmpeg discovery + zero-loss audio remux/extract
218
+ │ ├── downloader.py # yt-dlp two-phase download (predict + fetch)
219
+ │ └── ui.py # ANSI-colored output (enables VT on Windows)
220
+ ├── tests/ # pytest: cookies, paths, package smoke
221
+ ├── pyproject.toml # hatchling build, ruff, pytest config
222
+ └── .github/workflows/ci.yml
223
+ ```
224
+
225
+ ---
226
+
227
+ ## Contributing
228
+
229
+ `bili-dl` is MIT-licensed and welcomes contributions. The project uses
230
+ `ruff` for lint/format and `pytest` for tests. Before sending a PR:
231
+
232
+ ```bash
233
+ pip install -e . pytest ruff
234
+ ruff check src tests
235
+ ruff format --check src tests
236
+ pytest
237
+ ```
238
+
239
+ CI runs the same checks on Windows, macOS and Linux across Python 3.9–3.13.
240
+
241
+ ---
242
+
243
+ ## License
244
+
245
+ [MIT](LICENSE). Note that `bili-dl` is a *wrapper*; the actual downloading is
246
+ done by [`yt-dlp`](https://github.com/yt-dlp/yt-dlp) (Unlicense) and
247
+ [`ffmpeg`](https://ffmpeg.org) (LGPL/GPL). You must install those separately,
248
+ and any obligations imposed by their licenses apply to those binaries — not
249
+ to this wrapper's MIT license.