Metadata-Version: 2.4
Name: lianzz-downloader
Version: 1.3.1
Summary: A fast, multi-threaded, segmented file downloader powered by HTTP Range.
Author: steph
License-Expression: MIT
Project-URL: Homepage, https://github.com/lianzhongzhe/lianzz-downloader
Project-URL: Issues, https://github.com/lianzhongzhe/lianzz-downloader/issues
Keywords: downloader,http,range,multi-thread,segmented,aria2,axel
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25
Dynamic: license-file

# lianzz-downloader

[简体中文](/README_zh.md)

> **Package distribution name**: `lianzz-downloader` (used by `pip install`)
> **Python import name**: `fastdownloader` (used by `import fastdownloader`)
> **CLI command**: `fastdownloader` / `fastdownloader-gui`

A fast, multi-threaded, segmented file downloader powered by HTTP Range requests. A file is split into N segments, each fetched concurrently by an independent thread, and written in-place to the same on-disk file — the same approach used by IDM, aria2, and axel.

Benchmark against a local server capped at 2 MB/s per connection, downloading an 80 MB file:

| Threads | Time   | Avg speed   | Speedup |
| ------- | ------ | ----------- | ------- |
| 1       | 41 s   | 1.93 MB/s   | 1.00×   |
| 8       | 5.3 s  | 15.2 MB/s   | **7.85×** |
| 16      | 2.8 s  | 28.3 MB/s   | **14.6×** |

> The 1-thread and 8-thread downloads of the same file produce identical SHA-1 hashes, confirming correct segment assembly.

## Features

- HTTP Range multi-thread segmented downloads
- HEAD probe with automatic Range-GET fallback
- Auto-retry with exponential backoff (up to 5 retries)
- Global token-bucket rate limiter
- Pause / resume / cancel at any time
- Graceful fallback to single-thread streaming when Range is unsupported
- Live progress callback (speed, ETA, active threads)
- Both a CLI and a clean Tkinter GUI
- Windows taskbar progress overlay in the GUI, like browsers (green / yellow-paused / indeterminate)
- Temp file pre-allocation to avoid disk fragmentation
- Python 3.7+ supported

## Installation

From PyPI (recommended):

```bash
pip install lianzz-downloader
```

From source:

```bash
git clone <repo-url>
cd lianzz-downloader
pip install .
```

The only runtime dependency is `requests`. Requires **Python 3.7 or newer**.

## Usage

### Command line

```bash
# Basic
fastdownloader https://nodejs.org/dist/v20.10.0/node-v20.10.0-win-x64.zip

# Or via module
python -m fastdownloader https://nodejs.org/dist/v20.10.0/node-v20.10.0-win-x64.zip

# With options
fastdownloader <URL> -o my.zip -n 16 --speed 2048 --header "Authorization: Bearer xxx"
```

Options:

| Option             | Description                                  | Default |
| ------------------ | -------------------------------------------- | ------- |
| `url`              | URL to download (positional)                 | —       |
| `-o / --output`    | Output path                                  | derived from URL |
| `-n / --threads`   | Number of threads (1–32)                     | 8       |
| `--chunk`          | Read chunk size in bytes                     | 64 KB   |
| `--speed`          | Global speed limit in bytes/s (0 = unlimited) | 0       |
| `--retries`        | Max retries per segment                      | 5       |
| `--no-resume`      | Disable resume support                       | —       |
| `--header`         | Extra request header `Key: Value` (repeatable) | —     |
| `-v`               | Verbose debug logging                        | —       |

### Accelerated pip installs

Download & install third-party PyPI packages with multi-threaded segmented downloads:

```bash
# Resolve deps with pip, download wheels in parallel segments, install locally
fastdownloader pip requests rich

# Pin versions, choose thread count, download only
fastdownloader pip six==1.16.0 -n 16 --no-install

# Keep the downloaded wheels for reuse
fastdownloader pip numpy -d ./wheels
```

How it works: `pip install --dry-run --report` resolves the dependency tree with
your configured index (no download), each wheel is fetched with the segmented
multi-thread downloader, then `pip install --no-index --find-links` installs
everything from the local files. Requires pip >= 22.2.

### GUI

```bash
fastdownloader-gui
```

or:

```bash
python -m fastdownloader.gui
```

A minimal Tkinter window: URL, save path, thread count, speed limit, progress bar, pause / cancel. On Windows the download progress is also shown as an overlay on the taskbar icon (via `ITaskbarList3` through plain ctypes — no extra dependencies).

### Library usage

```python
from fastdownloader.core import SegmentedDownloader, DownloadConfig

def on_progress(p):
    print(f"{p.percent:5.1f}%  {p.format_speed()}  ETA {p.format_eta()}")

cfg = DownloadConfig(
    url="https://example.com/big.zip",
    output_path="big.zip",
    threads=8,
    speed_limit=0,            # 0 = unlimited
    headers={"Referer": "..."},
)

dl = SegmentedDownloader(cfg, on_progress)
# dl.pause() / dl.resume() / dl.cancel()  available any time
path = dl.run()
```

## Running tests

```bash
# 1) Start the local test server (caps each connection at 2 MB/s, serves an 80 MB file)
python examples/test_server.py 18080 2048 80

# 2) In another terminal, run the comparison benchmark
python examples/benchmark_local.py

# 3) Feature tests (pause/resume, speed limit)
python examples/test_features.py
```

## Implementation notes

- **HTTP Range requests**: each segment issues an independent `Range: bytes=A-B` request and writes to the matching offset of a shared `.part` file
- **Thread pool**: `concurrent.futures.ThreadPoolExecutor`, one worker per segment
- **Pre-allocation**: `os.posix_fallocate` reserves the whole file up front so downloads can `seek+write` without disk fragmentation
- **Resume**: each segment tracks its `downloaded` offset; on retry, the request resumes from that offset
- **HEAD probe**: `HEAD` first to get `Content-Length` and `Accept-Ranges`; falls back to a `Range: bytes=0-0` GET when HEAD is blocked
- **Fallback path**: when the server ignores Range (returns 200), the downloader transparently switches to single-thread `iter_content` streaming
- **Rate limiting**: token-bucket algorithm — a 1 MB/s limit shared across 4 threads yields exactly 1 MB/s aggregate
- **Progress reporting**: all segments share a single `_downloaded_total` (Lock-protected); the main thread samples every 250 ms to compute instantaneous speed and ETA
- **Python 3.7 compatibility**: avoids `pool.shutdown(cancel_futures=...)` (3.9+) by cancelling pending futures manually; relies on `from __future__ import annotations` for PEP 585/604 style hints to stay as strings

## Directory layout

```
lianzz-downloader/
├── fastdownloader/        # Python import name
│   ├── __init__.py
│   ├── __main__.py        # python -m fastdownloader entry
│   ├── core.py            # segmented downloader engine
│   ├── cli.py             # argparse CLI (incl. `pip` subcommand)
│   ├── pypi.py            # accelerated pip install flow
│   ├── gui.py             # Tkinter GUI
│   └── taskbar.py         # Windows taskbar progress (ITaskbarList3 via ctypes)
├── examples/
│   ├── test_server.py     # local test HTTP server
│   ├── benchmark.py       # external-source comparison
│   ├── benchmark_local.py # local-server comparison
│   ├── test_features.py   # pause / speed-limit / thread-count tests
│   └── taskbar_demo.py    # GUI taskbar progress demo
├── LICENSE
├── pyproject.toml         # distribution name: lianzz-downloader
└── README.md
```

> Note: the PyPI distribution name (`lianzz-downloader`) is intentionally decoupled from the import name (`fastdownloader`) to avoid a name collision on PyPI.

## License

MIT
