Parallel segmented downloader inspired by IDM, with CLI and Python API.
Project description
pdman — Python Parallel Download Manager
pdman 是一个使用 Python 实现的异步多段下载器,目标是提供接近 IDM / aria2c 使用体验的命令行下载工具,同时保留可嵌入 Python 项目的 API。
它适合处理大文件、批量 URL、弱网络续传、代理下载、限速下载、带 Cookie / Header / 认证的 HTTP 资源下载等场景。
主要特性
- 多连接分块下载:通过 HTTP Range 请求将同一 URL 拆分为多个分块并发下载。
- 断点续传:使用
.pdman.<sha>临时目录保存分块和元信息,可通过--continue恢复下载。 - 动态分块调度:下载过程中可继续拆分较大的空闲区间,提高连接利用率。
- 低速分片重启:通过
--chunk-retry-speed检测低速分块并自动重试,避免单个连接拖慢整体任务。 - 批量任务:支持从纯文本、JSON、YAML 文件读取多个下载任务。
- 并发控制:支持任务级并发、单 URL 分块并发、单服务器连接数限制。
- 限速控制:支持单任务限速和全局限速。
- 认证与代理:支持 HTTP Basic / Digest 认证、HTTP / HTTPS 代理和代理认证。
- Cookie 与请求头:支持 Netscape / Mozilla Cookie 文件、自定义 Header、Referer、User-Agent。
- 完整性校验:支持下载后 MD5 校验。
- SSL 控制:支持关闭证书校验或指定自定义 CA 证书。
- 完成回调:下载完成后可执行自定义 shell 命令。
- Python API:可在异步 Python 项目中直接调用
Manager。
安装
从 GitHub 安装:
pip install git+https://github.com/Akira-TL/pdman.git
本地开发安装:
git clone git@github.com:Akira-TL/pdman.git
cd pdman
uv sync
uv pip install -e .
也可以使用普通 venv:
python -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
python -m pip install -r requirements.txt
python -m pip install -e .
快速开始
单 URL 下载
pdman "https://example.com/file.bin"
默认输出目录为当前工作目录下的 pdman/。文件名优先从 Content-Disposition 解析;如果服务器没有提供文件名,则使用 URL 路径末尾;若仍无法判断,则使用 URL 哈希生成 .dat 文件名。
指定输出目录和文件名
pdman -d /data/downloads -o file.bin "https://example.com/file.bin"
--out 只适用于单 URL 下载。多个 URL 同时下载时,文件名由任务或 URL 自动推断。
断点续传
pdman --continue "https://example.com/file.bin"
断点续传依赖目标目录下的 .pdman.<sha> 临时目录和 .pdman 元数据。如果元数据与当前任务不匹配,pdman 会重新初始化对应任务。
批量下载
纯文本任务文件:
https://example.com/a.iso
https://example.com/b.zip
执行:
pdman -i urls.txt
JSON 任务文件:
{
"https://example.com/a.iso": {
"file_name": "linux.iso",
"dir_path": "/data/downloads",
"md5": "0123456789abcdef0123456789abcdef",
"log_path": "/data/logs/a.log"
}
}
YAML 任务文件:
https://example.com/a.iso:
file_name: linux.iso
dir_path: /data/downloads
md5: 0123456789abcdef0123456789abcdef
log_path: /data/logs/a.log
执行:
pdman -i tasks.json
pdman -i tasks.yaml
常用命令
并发与分块
pdman -N 4 -x 8 "https://example.com/file.bin"
-N, --max-downloads:同时下载的 URL 数量。-x, --max-concurrent-downloads:单个 URL 内部分块并发数。-k, --min-split-size:最小分块大小,例如1M、512K。--max-connection-per-server:单服务器最大连接数,0表示不限制。-Z, --force-sequential:强制顺序下载。
重试与超时
pdman --retry 5 --retry-wait 3 --timeout 120 --connect-timeout 30 --connect-progress-delay 5 "https://example.com/file.bin"
--retry:任务失败重试次数。--retry-wait:重试等待时间。--timeout:请求总超时。--connect-timeout:连接建立超时,默认 30 秒;超时后跳过该 URL。--connect-progress-delay:连接等待提示延迟,默认 5 秒;超过该时间仍未连通时显示不确定进度条和剩余时间。--chunk-timeout:分块下载超时。--chunk-retry-speed:分块低速阈值,例如100K。
限速
pdman --max-download-limit 5M --max-overall-download-limit 20M -i urls.txt
--max-download-limit:单任务限速。--max-overall-download-limit:全局限速。
认证、Cookie、代理和请求头
pdman \
--http-auth user:pass \
--cookie-file cookies.txt \
--proxy http://127.0.0.1:7890 \
--proxy-auth proxy_user:proxy_pass \
--header "Authorization: Bearer TOKEN" \
--referer "https://example.com" \
"https://example.com/file.bin"
SSL 与证书
pdman --no-check-certificate "https://example.com/file.bin"
pdman --ca-certificate /path/to/ca.pem "https://example.com/file.bin"
完整性校验
任务文件中提供 md5 字段后,可启用校验:
pdman --check-integrity -i tasks.yaml
md5 可以是 32 位 MD5 字符串、本地文件路径,或返回 MD5 文本的 URL。启用完整性校验后,MD5 不匹配会记录为 failed,并使 CLI 返回非零退出码。
Runtime 目录与历史记录
v0.4.0 开始,pdman 默认把分块临时文件放在系统临时目录下的 run 目录中,而不是下载目标目录:
/tmp/pdman/runs/<run-id>/chunks/<task-id>/
非 payload 元数据会写入 cache 目录:
~/.cache/pdman/history.jsonl
~/.cache/pdman/runs/<run-id>.json
~/.cache/pdman/active/<run-id>.json
active/<run-id>.json 只在运行中存在;运行结束后会写入最终 run summary,并追加任务历史记录。
临时目录策略:
pdman --tmp-policy auto "https://example.com/file.bin"
pdman --tmp-policy system "https://example.com/file.bin"
pdman --tmp-policy target "https://example.com/file.bin"
pdman --tmp /data/tmp "https://example.com/file.bin"
pdman --cache-dir /data/cache/pdman "https://example.com/file.bin"
pdman --keep-tmp "https://example.com/file.bin"
--tmp优先级最高。--tmp-policy auto是默认值,优先使用系统临时目录;已知大小且空间不足时回退到目标目录。--tmp-policy system强制使用系统临时目录;空间不足会把任务标记为failed。--tmp-policy target保留旧行为,在目标目录旁创建.pdman.<sha>。--tmp DIR是显式指定,不会在空间不足时偷偷回退。--cache-dir覆盖默认~/.cache/pdman。--keep-tmp会在 run failed 或中断时保留 runtime tmp 目录,便于 debug 和人工恢复;它不是稳定的 resume metadata 接口。
历史查询命令
v0.4.1 开始,可以直接查询 runtime history 和 run summary。
pdman history
pdman history --last 50
pdman history --failed
pdman history --status completed
pdman history --run-id <run-id>
pdman runs
pdman runs --last 10
pdman run <run-id>
这些命令只读取 ~/.cache/pdman 中的历史记录,不会启动下载任务。也可以用 --cache-dir DIR 查询自定义 cache 目录。
本地队列命令
v0.4.3 开始,可以把任务写入本地队列后再启动下载:
pdman queue add "https://example.com/file.bin"
pdman queue add --json "https://example.com/file.bin"
pdman queue add -i tasks.yaml
pdman queue add -d /data/downloads --file-name file.bin "https://example.com/file.bin"
pdman queue list
pdman queue list --last 0
pdman queue list --status pending
pdman queue list --status failed --attempts-ge 3
pdman queue list --json
pdman queue list --jsonl
pdman queue start
pdman queue start --limit 5
pdman queue retry-failed
pdman queue retry-failed --limit 5
pdman queue retry-failed --dry-run
pdman queue retry-failed --dry-run --json
pdman queue retry-failed --dry-run --jsonl
pdman queue retry-failed --max-attempts 3
pdman queue retry-failed --error-contains "HTTP 503"
pdman queue validate
pdman queue validate --json
pdman queue repair
pdman queue repair --json
pdman queue recover
pdman queue recover --json
pdman queue remove <queue-id>
pdman queue remove <queue-id> --json
pdman queue clear --status completed
pdman queue clear --status completed --json
pdman queue clear --all
pdman queue clear --all --json
队列文件位于 ~/.cache/pdman/queue.jsonl。queue start 会读取 pending 任务,使用真实 Manager 执行下载,并把队列状态更新为 completed、skipped 或 failed。
v0.4.5 开始,queue record 会记录 attempts。每次 queue start 或 queue retry-failed 真正取出任务执行时,attempts 会递增。queue retry-failed 会读取 failed 任务重新执行;成功后状态变成 completed 且 last_error=None,再次失败则保持 failed 并更新 last_error。
v0.4.6 开始,queue record 会记录 last_status_reason,用于保存最近一次 completed/skipped/failed 的原因。retry-failed --dry-run 只预览候选,不修改 queue;--max-attempts N 只重试 attempts < N 的失败记录;--error-contains TEXT 只重试 last_error 包含指定文本的失败记录,匹配不区分大小写。
v0.4.7 开始,queue 查询/预览/校验命令提供最小结构化输出:queue list --json 输出 {records, count},queue list --jsonl 每行输出一个 queue record;queue retry-failed --dry-run --json 输出 {candidates, count, dry_run},--jsonl 每行输出一个候选 record;queue validate --json 输出 {ok, valid, malformed, invalid, duplicate_ids, unsupported_schema, issues}。retry-failed --json/--jsonl 只允许和 --dry-run 搭配,普通下载和实际 retry 执行输出仍保持人类文本。
v0.4.8 开始,queue 的非下载类维护命令继续补齐 JSON 输出:queue add --json 输出 {added, records, count},queue repair --json 输出 repair stats,queue recover --json 输出 {recovered},queue remove --json 输出 {requested, removed},queue clear --json 输出 {cleared, status, all}。queue list --last 0 表示不限制数量,返回匹配的全部 queue records。
v0.4.4 开始,queue record 会写入 schema_version=1,queue 写操作会使用 ~/.cache/pdman/queue.lock。锁 backend 会按平台自动选择:Linux/macOS/BSD 使用 fcntl,Windows 使用 msvcrt,其他平台使用原子目录锁 fallback。
维护命令用于检查、修复和清理 queue:validate 只报告问题,repair 会重写为可用 queue,recover 会把 stale running 任务恢复为 pending,remove 按 ID 删除,clear 按状态或 --all 清理。
当前队列不提供自动 retry scheduler、backoff、per-record retry policy、优先级、daemon、SQLite、全局 JSON/JSONL 输出协议或 agent event stream,这些放到后续版本。
任务结果与退出码
每次运行结束后,pdman 会汇总任务结果:
Summary:
completed: 1
skipped: 1
failed: 0
downloaded: 128.0 MiB
skipped 只表示用户显式启用 --quit-if-exists 且目标文件已存在。网络错误、HTTP header 状态异常、连接超时、完整性校验失败等“没有完成下载目标”的情况都会记录为 failed;批量任务会继续执行,但最终 CLI 返回 1。
当前最小退出码规则:
| 退出码 | 含义 |
|---|---|
0 |
没有 failed 任务 |
1 |
一个或多个任务 failed |
130 |
用户中断 |
下载完成回调
pdman --on-download-complete "echo Downloaded {filename} to {filepath}" "https://example.com/file.bin"
可用占位符:{filename}、{filepath}、{url}、{dir}、{size}。
配置文件
--conf-path 支持 JSON / YAML 配置。配置文件中的键与 CLI 长参数名保持一致;命令行参数优先级高于配置文件。
max_downloads: 4
max_concurrent_downloads: 8
retry: 5
retry_wait: 3
timeout: 120
connect_timeout: 20
proxy: "http://127.0.0.1:7890"
max_overall_download_limit: "20M"
check_certificate: true
auto_file_renaming: true
summary_interval: 1.0
使用:
pdman --conf-path pdman-config.yaml -i urls.txt
Python API
import asyncio
from pdman import Manager
async def main():
async with Manager(
max_downloads=4,
max_concurrent_downloads=8,
proxy="http://127.0.0.1:7890",
max_overall_download_limit="20M",
on_download_complete="echo Downloaded {filename}",
) as pdman:
pdman.append("https://example.com/file.bin", dir_path="/data/downloads")
await pdman.wait()
asyncio.run(main())
开发与发布
本项目推荐使用 uv 管理本地开发环境:
uv sync
uv pip install -e .
uv pip install pytest build twine
测试:
uv run python -m pytest -q src/pdman/test.py
构建发布产物:
uv run python -m build
uv run python -m twine check dist/*
发布前只需要在 pyproject.toml 中更新 [project].version,CLI 的 pdman --version 会从安装包 metadata 自动读取该版本。随后确认 Git tag 与项目版本对应,例如 0.3.1 对应 v0.3.1。
仓库中的 .github/workflows/pypi.yml 会在 GitHub Release 发布后自动构建并发布到 PyPI。
仓库内容约定
README.md 面向用户,说明安装、常用命令和核心能力;docs/ 面向开发者和高级用户,记录功能细节、配置链路和发布检查事项。
discuss/ 仅用于本地讨论稿、规划草稿和临时设计内容,不应进入版本控制,也不作为正式文档发布。正式、可维护的内容应整理到 README.md 或 docs/ 后再提交。
.venv/、.vscode/ 配置、uv.lock*、构建产物和下载临时目录均不纳入仓库。
License
本项目使用 GPL-3.0-only 许可证,完整文本见 LICENSE。
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 pdman-0.4.8.tar.gz.
File metadata
- Download URL: pdman-0.4.8.tar.gz
- Upload date:
- Size: 73.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d83c90def3b5d77aa89e63bc0372ce4461f0b823f45177e4373332ab5fa2a474
|
|
| MD5 |
6bb9f3c7565d6b85de649a164cf5b6fc
|
|
| BLAKE2b-256 |
dc3ed1b3b95b6571e3163080dfa82c2aefb40f78ee55554f0d06f13ab3c13a08
|
Provenance
The following attestation bundles were made for pdman-0.4.8.tar.gz:
Publisher:
pypi.yml on Akira-TL/pdman
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdman-0.4.8.tar.gz -
Subject digest:
d83c90def3b5d77aa89e63bc0372ce4461f0b823f45177e4373332ab5fa2a474 - Sigstore transparency entry: 2019419617
- Sigstore integration time:
-
Permalink:
Akira-TL/pdman@80e5434a386f91e4246f0953b6cb0ac7ffc72104 -
Branch / Tag:
refs/tags/v0.4.8 - Owner: https://github.com/Akira-TL
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@80e5434a386f91e4246f0953b6cb0ac7ffc72104 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pdman-0.4.8-py3-none-any.whl.
File metadata
- Download URL: pdman-0.4.8-py3-none-any.whl
- Upload date:
- Size: 57.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c7c730064af109bed87f65de0cfc36dfa67d39f14a7d3a5230b791aa183bcf7
|
|
| MD5 |
ef33c12d9cbb6536cd4b2643067a39d0
|
|
| BLAKE2b-256 |
35c48d406694075d308dcb5ed3830c2c3c4c56caaca4592fcffb2d0016ef6969
|
Provenance
The following attestation bundles were made for pdman-0.4.8-py3-none-any.whl:
Publisher:
pypi.yml on Akira-TL/pdman
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pdman-0.4.8-py3-none-any.whl -
Subject digest:
3c7c730064af109bed87f65de0cfc36dfa67d39f14a7d3a5230b791aa183bcf7 - Sigstore transparency entry: 2019419727
- Sigstore integration time:
-
Permalink:
Akira-TL/pdman@80e5434a386f91e4246f0953b6cb0ac7ffc72104 -
Branch / Tag:
refs/tags/v0.4.8 - Owner: https://github.com/Akira-TL
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@80e5434a386f91e4246f0953b6cb0ac7ffc72104 -
Trigger Event:
release
-
Statement type: