A from-scratch Bilibili downloader learning project.
Project description
bili-downloader-from-scratch
A from-scratch learning project for building a Bilibili downloader step by step.
This repository is intentionally scoped as an MVP path, not a clone of a full desktop app. The goal is to grow the downloader in layers: parsing first, stream selection second, downloading third, merging fourth, and GUI last.
Current status
Implemented:
- Runnable CLI entrypoint
inspectcommand with real metadata parsing for normal Bilibili video URLs and Bangumi episode/season URLsstreamscommand with real DASH stream manifest parsing for normal videos and Bangumi episodesplancommand that writes metadata + stream based JSON plansdownloadcommand that downloads media files, with optional subtitle and danmaku sidecarsbatch-downloadcommand for multi-link downloads, with configurable--jobsconcurrency, list-source expansion, and optional multi-page expansionhistory list/history showfor querying saved records and mutable run stateresumecommand for restarting failed or partialplan/download/batch-downloadrunspausecommand for pausing active runs, queued runs, or one batch item by indexretry-failedcommand for retrying failed items in a saved batch runcancelcommand for requesting cancellation of active or queued runsapp_servicefacade for GUI/CLI task creation, listing, detail lookup, pause, cancellation, retry, and resumeauth-checkcommand for validating whether configured Bilibili cookies are loaded and logged in- Lightweight
tkinterGUI MVP with task list, new download, task detail, Cookie, and settings pages - Support for
BV...,av..., Bangumiss.../ep.../md..., full video URLs, andb23.tvshort URLs - Batch expansion for favorites, uploader collections, Bangumi seasons, and UP space video lists
- Optional subtitle sidecar download in
srtor rawjsonformat - Optional danmaku XML sidecar download
- WBI signing flow for the live video detail API
- Best-available quality selection, with cookie-enabled higher tiers when the account can access them
- Premium audio parsing for Dolby and Hi-Res lossless tracks
- Built-in Windows
ffmpegruntime for merging, with config/PATH overrides and automatic fallback to separate audio/video files whenffmpegis unavailable - CLI progress output during downloads
--overwritesupport for repeated downloads- Cookie file loading for authenticated requests
- Immutable per-run JSON records saved under the local
history_dir, including failures - Mutable run snapshots saved under the local
runs_dirfor task resume and future GUI polling - Batch item phase/progress snapshots in
run.jsonfor active GUI polling - Request-style pause checkpoints for active download and batch runs
- Request-style cancellation checkpoints for active download and batch runs
- Structured error categories for
auth,network,risk,quality,ffmpeg,io,parser,cancelled,paused, andunknown - Retry/backoff for retryable metadata, manifest, and media download failures
- Range-based resume from retained
.partfiles - Config loading and log initialization
pytest,ruff, and GitHub Actions CI configuration- Unit and integration-style tests for parser, stream, downloader, task, and real-scenario flows
GUI MVP:
- Task list with status, phase, progress, speed, ETA, cancel, resume, and open-folder actions
- New download page for single URL or multi-line batch downloads
- Task detail page with raw
run.jsonand batch item rows - Cookie configuration page with save and login-check actions
- Settings page for output directory, default concurrency, and ffmpeg path
Support scope
Current backend coverage is no longer limited to plain single-video downloads:
- normal videos, including optional multi-page expansion via
batch-download --expand-pages - Bangumi episode URLs and season/media URLs
- favorites, uploader collections, and UP space video lists as batch sources
- optional subtitle and danmaku sidecar downloads during
plan/download/batch-download
Still not positioned as a full product-grade desktop downloader for every Bilibili surface. Live streams, paid courses, broader GUI controls for the new sidecar/list features, and deeper real-device compatibility work remain future work.
Structure
bili-downloader-from-scratch/
|-- main.py
|-- pyproject.toml
|-- requirements.txt
|-- LICENSE
|-- THIRD_PARTY_NOTICES.md
|-- docs/
|-- src/
| `-- bili_downloader/
| |-- __init__.py
| |-- __main__.py
| |-- app_service.py
| |-- auth.py
| |-- cli.py
| |-- config.py
| |-- downloader.py
| |-- errors.py
| |-- logging_setup.py
| |-- models.py
| |-- network.py
| |-- parser.py
| |-- records.py
| |-- services/
| | |-- __init__.py
| | |-- history.py
| | `-- tasks.py
| `-- streams.py
|-- tools/
| `-- ffmpeg-8.1-essentials_build/
| |-- LICENSE
| |-- README.txt
| `-- bin/
| `-- ffmpeg.exe
`-- tests/
|-- test_app_service.py
|-- test_auth.py
|-- test_cli.py
|-- test_downloader.py
|-- test_errors.py
|-- test_history.py
|-- test_network.py
|-- test_parser.py
|-- test_records.py
|-- test_streams.py
`-- test_tasks.py
Run
cd D:\pycode\bili-downloader-from-scratch
python -m pip install -e ".[dev]"
bili-downloader --help
python .\main.py --help
python .\main.py inspect "https://www.bilibili.com/video/BV1xx411c7mD"
python .\main.py inspect "https://www.bilibili.com/bangumi/play/ss1129"
python .\main.py streams "BV1xx411c7mD"
python .\main.py plan "BV1xx411c7mD" --with-subtitles --with-danmaku
python .\main.py download "BV1xx411c7mD" --audio-only --overwrite
python .\main.py download "https://www.bilibili.com/bangumi/play/ep2001" --with-subtitles --with-danmaku
python .\main.py batch-download BV1xx411c7mD av170001 --audio-only --overwrite
python .\main.py batch-download BV1xx411c7mD av170001 --jobs 2 --overwrite
python .\main.py batch-download BV1xx411c7mD --expand-pages
python .\main.py batch-download "https://www.bilibili.com/list/ml123456"
python .\main.py batch-download "https://space.bilibili.com/2/video" --with-subtitles
python .\main.py history list --limit 10
python .\main.py history show <record-id-or-run-id>
python .\main.py resume <run-id> --jobs 2
python .\main.py pause <run-id> --reason "pause from GUI"
python .\main.py pause <run-id> --item 1 --reason "hold one item"
python .\main.py retry-failed <run-id> --jobs 2
python .\main.py cancel <run-id> --reason "stop from GUI"
python .\main.py gui
python .\main.py config
python .\main.py auth-check
python .\main.py --cookie-file .\cookies.txt streams "BV1xx411c7mD"
python -m ruff check .
python -m pytest
The bili-downloader console script is available after installing the project. python .\main.py ...
remains useful when running directly from a source checkout.
FFmpeg policy
Releases include a bundled Windows ffmpeg.exe under tools/ffmpeg-8.1-essentials_build/bin.
The downloader resolves ffmpeg in this order:
BILI_DOWNLOADER_FFMPEG, set from the config file or GUI settings page- the bundled
ffmpeg.exefrom an installed package or source checkout ffmpegfound onPATH
If no usable ffmpeg is found, downloads still succeed by saving separate video and audio files. The
release does not auto-download ffmpeg on first start.
License
Project source code is MIT licensed. Release artifacts also include a bundled Windows FFmpeg build under
GPL v3; see THIRD_PARTY_NOTICES.md and tools/ffmpeg-8.1-essentials_build/LICENSE.
Cookie files can be provided as:
- raw header text such as
SESSDATA=...; bili_jct=... - JSON object such as
{"SESSDATA": "...", "bili_jct": "..."} - JSON array with
name/valueentries - Netscape
cookies.txt
By default the CLI looks for cookies at the cookie_file path printed by:
python .\main.py config
Create that file with your own Bilibili browser cookies, then verify login state with:
python .\main.py auth-check
For a one-off cookie file, use:
python .\main.py --cookie-file .\cookies.txt auth-check
python .\main.py --cookie-file .\cookies.txt download "BV1xx411c7mD" --quality 1080p --overwrite
auth-check only prints cookie names and account status. It intentionally does not print cookie values.
Each plan, download, and batch-download run saves two local artifacts:
- a final JSON record in
history_dir, withstatus,exit_code, request context, result payload, and any error details - a mutable
run.jsonsnapshot inruns_dir, withrun_id, phase, attempts, item-level batch state, active progress, and links to emitted history records
This split keeps immutable audit records and resumable task state separate, which is a better base for GUI task lists and interrupted-run recovery.
Errors are serialized with a stable category, retryable flag, and stage. Retryable metadata and manifest failures are retried before the command fails. Media track downloads retry each URL with exponential backoff and still fall through to backup URLs when available. Bilibili HTTP 412 and request-frequency failures are reported as risk, while expired or missing login cookies are reported as auth.
Batch concurrency is capped at 3 jobs, and all network opens pass through a small per-host rate limiter to reduce request bursts. Media downloads preflight known Content-Length values against available disk space, reject illegal Windows path components, keep .part files for resumable failures, and remove completed merge work directories after successful ffmpeg output.
Batch resume skips successful items, reruns failed or pending items, and resumes paused items. resume --jobs N can override the stored concurrency for a resumed batch. retry-failed <run-id> only requeues failed batch items and leaves successful or paused items untouched.
Pause is request-based. pause <run-id> writes pause_requested and moves active runs to pause_requested until the running process reaches a checkpoint. Active runs check this flag during phase transitions, progress updates, and media chunk downloads, then finalize as paused while keeping .part files for Range resume. pause <run-id> --item N pauses one batch item without pausing the rest of the batch.
Cancellation is request-based. cancel <run-id> writes cancel_requested into the run state. Active runs check this flag during phase transitions, progress updates, and media chunk downloads, then finalize as cancelled with exit code 130.
GUI and other application clients should use bili_downloader.app_service instead of depending on CLI argument parsing. The facade exposes create_download_task, create_batch_download_task, list_tasks, get_task_detail, pause_task, cancel_task, retry_failed_task, and resume_task; the CLI now uses the same facade.
The GUI saves its settings to the default app config file under app_home. Use the settings page for output_dir, default batch concurrency, and an optional ffmpeg executable path to override the bundled runtime. The newly added subtitle / danmaku / list-expansion controls are currently exposed through the CLI and app_service API first.
Next step
- Expose subtitle, danmaku, and list-expansion options in the GUI.
- Expand real-device regression coverage across more account types, regions, member-only videos, and larger list sources.
- Package the GUI as a standalone executable.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bili_downloader_from_scratch-0.1.0.tar.gz.
File metadata
- Download URL: bili_downloader_from_scratch-0.1.0.tar.gz
- Upload date:
- Size: 36.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d3d4a8fa9ec3c6f8caa8fa7fe91b211172dd6ace10aa48579fef417cfc38206c
|
|
| MD5 |
318c9bb398eb9dfcff0b2f4463932f36
|
|
| BLAKE2b-256 |
4c3a5bfa90422b5ed70393ed83af0f1274904743a931ee83aa01443fdfd0d280
|
File details
Details for the file bili_downloader_from_scratch-0.1.0-py3-none-any.whl.
File metadata
- Download URL: bili_downloader_from_scratch-0.1.0-py3-none-any.whl
- Upload date:
- Size: 37.9 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8fd0923a2dfb840fee6597879fa61af6eb18e76a94a5f63c7175edeb652d93e
|
|
| MD5 |
97f50f8f00aea3165fe3884de7981a83
|
|
| BLAKE2b-256 |
d87f2de671bd0ed99466cdfb12c98b3ce877b3ecb31bcf5b04e64c03f9ee50c4
|