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.
- bili_dl-0.1.0/.github/workflows/ci.yml +36 -0
- bili_dl-0.1.0/.gitignore +48 -0
- bili_dl-0.1.0/.python-version +1 -0
- bili_dl-0.1.0/AGENTS.md +168 -0
- bili_dl-0.1.0/CHANGELOG.md +39 -0
- bili_dl-0.1.0/LICENSE +26 -0
- bili_dl-0.1.0/PKG-INFO +249 -0
- bili_dl-0.1.0/README.md +219 -0
- bili_dl-0.1.0/pyproject.toml +83 -0
- bili_dl-0.1.0/src/bili_dl/__init__.py +9 -0
- bili_dl-0.1.0/src/bili_dl/__main__.py +8 -0
- bili_dl-0.1.0/src/bili_dl/cli.py +221 -0
- bili_dl-0.1.0/src/bili_dl/config.py +42 -0
- bili_dl-0.1.0/src/bili_dl/cookies.py +175 -0
- bili_dl-0.1.0/src/bili_dl/downloader.py +132 -0
- bili_dl-0.1.0/src/bili_dl/ffmpeg.py +124 -0
- bili_dl-0.1.0/src/bili_dl/paths.py +68 -0
- bili_dl-0.1.0/src/bili_dl/ui.py +83 -0
- bili_dl-0.1.0/tests/__init__.py +0 -0
- bili_dl-0.1.0/tests/data/sample_cookies_all.txt +10 -0
- bili_dl-0.1.0/tests/test_cookies.py +87 -0
- bili_dl-0.1.0/tests/test_package.py +24 -0
- bili_dl-0.1.0/tests/test_paths.py +75 -0
|
@@ -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
|
bili_dl-0.1.0/.gitignore
ADDED
|
@@ -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
|
bili_dl-0.1.0/AGENTS.md
ADDED
|
@@ -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
|
+
[](https://github.com/Echoziness/bili-dl/actions/workflows/ci.yml)
|
|
36
|
+
[](https://www.python.org/downloads/)
|
|
37
|
+
[](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.
|