gdown2 0.2.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.
- gdown2-0.2.0/LICENSE +22 -0
- gdown2-0.2.0/PKG-INFO +456 -0
- gdown2-0.2.0/README.md +414 -0
- gdown2-0.2.0/gdown2/__init__.py +27 -0
- gdown2-0.2.0/gdown2/__main__.py +9 -0
- gdown2-0.2.0/gdown2/application/__init__.py +31 -0
- gdown2-0.2.0/gdown2/application/crawler.py +128 -0
- gdown2-0.2.0/gdown2/application/exporter.py +40 -0
- gdown2-0.2.0/gdown2/application/planner.py +193 -0
- gdown2-0.2.0/gdown2/application/ports.py +85 -0
- gdown2-0.2.0/gdown2/application/reporter.py +285 -0
- gdown2-0.2.0/gdown2/application/resolver.py +50 -0
- gdown2-0.2.0/gdown2/application/scheduler.py +150 -0
- gdown2-0.2.0/gdown2/application/verifier.py +81 -0
- gdown2-0.2.0/gdown2/application/worker.py +307 -0
- gdown2-0.2.0/gdown2/domain/__init__.py +89 -0
- gdown2-0.2.0/gdown2/domain/errors.py +151 -0
- gdown2-0.2.0/gdown2/domain/models.py +254 -0
- gdown2-0.2.0/gdown2/domain/path_policy.py +138 -0
- gdown2-0.2.0/gdown2/domain/policies.py +117 -0
- gdown2-0.2.0/gdown2/domain/tree.py +113 -0
- gdown2-0.2.0/gdown2/domain/url_parser.py +148 -0
- gdown2-0.2.0/gdown2/infrastructure/__init__.py +12 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/__init__.py +16 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/anonymous.py +21 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/api_key.py +25 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/base.py +21 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/oauth.py +128 -0
- gdown2-0.2.0/gdown2/infrastructure/auth/service_account.py +92 -0
- gdown2-0.2.0/gdown2/infrastructure/cache.py +131 -0
- gdown2-0.2.0/gdown2/infrastructure/config.py +182 -0
- gdown2-0.2.0/gdown2/infrastructure/drive/__init__.py +20 -0
- gdown2-0.2.0/gdown2/infrastructure/drive/metadata.py +105 -0
- gdown2-0.2.0/gdown2/infrastructure/drive/source_api.py +204 -0
- gdown2-0.2.0/gdown2/infrastructure/drive/source_composite.py +70 -0
- gdown2-0.2.0/gdown2/infrastructure/drive/source_web.py +666 -0
- gdown2-0.2.0/gdown2/infrastructure/http/__init__.py +14 -0
- gdown2-0.2.0/gdown2/infrastructure/http/client.py +209 -0
- gdown2-0.2.0/gdown2/infrastructure/http/ratelimit.py +46 -0
- gdown2-0.2.0/gdown2/infrastructure/http/retry.py +114 -0
- gdown2-0.2.0/gdown2/infrastructure/storage/__init__.py +7 -0
- gdown2-0.2.0/gdown2/infrastructure/storage/fs_sink.py +87 -0
- gdown2-0.2.0/gdown2/infrastructure/storage/resume_state.py +94 -0
- gdown2-0.2.0/gdown2/interfaces/__init__.py +6 -0
- gdown2-0.2.0/gdown2/interfaces/cli/__init__.py +6 -0
- gdown2-0.2.0/gdown2/interfaces/cli/app.py +405 -0
- gdown2-0.2.0/gdown2/interfaces/cli/render.py +247 -0
- gdown2-0.2.0/gdown2/interfaces/json_events.py +27 -0
- gdown2-0.2.0/gdown2/interfaces/tui/__init__.py +6 -0
- gdown2-0.2.0/gdown2/interfaces/tui/app.py +205 -0
- gdown2-0.2.0/gdown2/py.typed +0 -0
- gdown2-0.2.0/gdown2/sdk.py +278 -0
- gdown2-0.2.0/gdown2.egg-info/PKG-INFO +456 -0
- gdown2-0.2.0/gdown2.egg-info/SOURCES.txt +58 -0
- gdown2-0.2.0/gdown2.egg-info/dependency_links.txt +1 -0
- gdown2-0.2.0/gdown2.egg-info/entry_points.txt +2 -0
- gdown2-0.2.0/gdown2.egg-info/requires.txt +17 -0
- gdown2-0.2.0/gdown2.egg-info/top_level.txt +1 -0
- gdown2-0.2.0/pyproject.toml +85 -0
- gdown2-0.2.0/setup.cfg +4 -0
gdown2-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gdown2 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
|
+
|
gdown2-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gdown2
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Fast, resumable Google Drive file and folder downloader with async I/O, Rich CLI, and Textual TUI.
|
|
5
|
+
Author: gdown2 contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/moustaph97/gdown2
|
|
8
|
+
Project-URL: Repository, https://github.com/moustaph97/gdown2
|
|
9
|
+
Project-URL: Issues, https://github.com/moustaph97/gdown2/issues
|
|
10
|
+
Keywords: google-drive,downloader,cli,tui,async,gdown
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: typer>=0.12.0
|
|
28
|
+
Requires-Dist: rich>=13.7.0
|
|
29
|
+
Requires-Dist: textual>=0.70.0
|
|
30
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
31
|
+
Requires-Dist: platformdirs>=4.0.0
|
|
32
|
+
Provides-Extra: service-account
|
|
33
|
+
Requires-Dist: google-auth>=2.0.0; extra == "service-account"
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
37
|
+
Requires-Dist: respx>=0.21.0; extra == "dev"
|
|
38
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
39
|
+
Requires-Dist: mypy>=1.10.0; extra == "dev"
|
|
40
|
+
Requires-Dist: google-auth>=2.0.0; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
|
|
43
|
+
# gdown2
|
|
44
|
+
|
|
45
|
+
**Fast, crash-safe Google Drive downloads for files, folders, and Google Workspace documents.**
|
|
46
|
+
|
|
47
|
+
`gdown2` is a ground-up rewrite of [`gdown`](https://github.com/wkentaro/gdown) built on
|
|
48
|
+
`asyncio` and `httpx`. It keeps the zero-config convenience of public Drive links while
|
|
49
|
+
adding a resumable transfer engine, adaptive rate limiting, recursive folder crawling,
|
|
50
|
+
checksum verification, three user interfaces, and a clean Python API.
|
|
51
|
+
|
|
52
|
+
[](https://github.com/moustaph97/gdown2/actions/workflows/ci.yml)
|
|
53
|
+
[](https://www.python.org/)
|
|
54
|
+
[](LICENSE)
|
|
55
|
+
[](https://github.com/astral-sh/ruff)
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Why gdown2?
|
|
60
|
+
|
|
61
|
+
The original `gdown` is excellent for quick scripts. It becomes fragile when you pull
|
|
62
|
+
multi-gigabyte checkpoints, deeply nested folders with hundreds of files, or unstable
|
|
63
|
+
connections that drop mid-transfer.
|
|
64
|
+
|
|
65
|
+
`gdown2` was designed for those cases:
|
|
66
|
+
|
|
67
|
+
| Capability | What it means in practice |
|
|
68
|
+
|---|---|
|
|
69
|
+
| **Crash-safe resume** | Data is streamed to `<filename>.part`; completed files are promoted with an atomic `os.replace`. Interrupted transfers resume from the last byte. |
|
|
70
|
+
| **Google Drive API v3 + public web** | Works out of the box on public links, then transparently switches to the official API when given an API key, OAuth token, or service account. |
|
|
71
|
+
| **Recursive folder crawling** | Full BFS traversal with pagination, shortcut-cycle protection, filename sanitization, and collision handling. |
|
|
72
|
+
| **Two-level concurrency** | Parallel file downloads (`--workers`) plus optional multi-Range chunking for large files (`--chunks`). |
|
|
73
|
+
| **Adaptive throttling** | Per-host token bucket (`--rate`) and AIMD backoff when Google returns `429` or transient `403` responses. |
|
|
74
|
+
| **Integrity verification** | Size and MD5 checks after every transfer, with automatic retry and clean restart on corruption. |
|
|
75
|
+
| **Workspace exports** | Google Docs, Sheets, Slides, and Drawings are exported on the fly to Office, PDF, CSV, and other formats. |
|
|
76
|
+
| **Three interfaces** | Rich CLI, interactive Textual TUI, and newline-delimited JSON for automation. |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Table of contents
|
|
81
|
+
|
|
82
|
+
- [Installation](#installation)
|
|
83
|
+
- [Quick start](#quick-start)
|
|
84
|
+
- [CLI overview](#cli-overview)
|
|
85
|
+
- [Python API](#python-api)
|
|
86
|
+
- [Authentication](#authentication)
|
|
87
|
+
- [Configuration](#configuration)
|
|
88
|
+
- [Reliability model](#reliability-model)
|
|
89
|
+
- [Exit codes](#exit-codes)
|
|
90
|
+
- [Documentation](#documentation)
|
|
91
|
+
- [Development](#development)
|
|
92
|
+
- [Project layout](#project-layout)
|
|
93
|
+
- [Contributing](#contributing)
|
|
94
|
+
- [License](#license)
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
Requires **Python 3.11 or newer**.
|
|
101
|
+
|
|
102
|
+
### From source
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git clone https://github.com/moustaph97/gdown2.git
|
|
106
|
+
cd gdown2
|
|
107
|
+
pip install .
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Editable development install
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git clone https://github.com/moustaph97/gdown2.git
|
|
114
|
+
cd gdown2
|
|
115
|
+
pip install -e ".[dev]"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Optional service-account support
|
|
119
|
+
|
|
120
|
+
Service-account authentication requires `google-auth`:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install "gdown2[service-account]"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Verify the installation:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
gdown2 --version
|
|
130
|
+
gdown2 --help
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Quick start
|
|
136
|
+
|
|
137
|
+
### 1. Inspect metadata
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gdown2 info "https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view?usp=sharing"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2. Preview a remote folder without downloading
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
gdown2 tree "https://drive.google.com/drive/folders/1FolderId_9876543210"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
Dataset/
|
|
151
|
+
├── train/
|
|
152
|
+
│ ├── part-000.parquet [412.5 MB]
|
|
153
|
+
│ └── part-001.parquet [410.1 MB]
|
|
154
|
+
├── Notes.docx [export]
|
|
155
|
+
└── README.md [4.2 KB]
|
|
156
|
+
|
|
157
|
+
1 folders, 4 files (1 docs), total known size: 822.6 MB
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 3. Download a file or an entire folder
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Single file or recursive folder download.
|
|
164
|
+
gdown2 get "https://drive.google.com/drive/folders/1FolderId_9876543210" -o ./downloads
|
|
165
|
+
|
|
166
|
+
# Six parallel files, four Range chunks per large file, five requests per second.
|
|
167
|
+
gdown2 get <url-or-id> -o ./downloads --workers 6 --chunks 4 --rate 5.0
|
|
168
|
+
|
|
169
|
+
# Export Google Docs/Sheets/Slides as PDF instead of their Office default.
|
|
170
|
+
gdown2 get "https://docs.google.com/document/d/1DocId_1234567890abc/edit" --export-format pdf
|
|
171
|
+
|
|
172
|
+
# Flatten the remote hierarchy into one directory.
|
|
173
|
+
gdown2 get <folder-url> -o ./downloads --flat
|
|
174
|
+
|
|
175
|
+
# Launch the interactive three-pane TUI.
|
|
176
|
+
gdown2 get <folder-url> -o ./downloads --tui
|
|
177
|
+
|
|
178
|
+
# Stream machine-readable NDJSON events for CI and automation.
|
|
179
|
+
gdown2 get <url-or-id> -o ./downloads --json
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 4. Resume an interrupted session
|
|
183
|
+
|
|
184
|
+
If a transfer is interrupted by `Ctrl+C`, a network drop, or a Drive quota limit:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
gdown2 resume ./downloads
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Resume state is stored in `<output>/.gdown2.state.json`; partial data lives in
|
|
191
|
+
`<filename>.part` files. Run the command from the same output directory to continue
|
|
192
|
+
exactly where the previous session stopped.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## CLI overview
|
|
197
|
+
|
|
198
|
+
| Command | Purpose |
|
|
199
|
+
|---|---|
|
|
200
|
+
| `gdown2 info <url-or-id>` | Resolve and display metadata for a file, folder, or document. |
|
|
201
|
+
| `gdown2 tree <folder-url-or-id>` | Crawl a remote folder and print its hierarchy without downloading. |
|
|
202
|
+
| `gdown2 get <url-or-id> [-o DIR]` | Download a file, folder, or Google Workspace document. |
|
|
203
|
+
| `gdown2 resume [DIR]` | Resume incomplete transfers recorded in a directory. |
|
|
204
|
+
| `gdown2 cache-clear` | Delete the local metadata cache. |
|
|
205
|
+
|
|
206
|
+
Common `get` options:
|
|
207
|
+
|
|
208
|
+
| Option | Description |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `-o, --output PATH` | Destination directory or file path. Defaults to the current directory. |
|
|
211
|
+
| `-w, --workers N` | Maximum concurrent file transfers. |
|
|
212
|
+
| `-c, --chunks N` | Parallel HTTP Range streams per large file. |
|
|
213
|
+
| `-r, --rate RPS` | Per-host request rate limit in requests per second. |
|
|
214
|
+
| `--flat` | Flatten the remote tree into the output directory. |
|
|
215
|
+
| `--overwrite` | Overwrite existing local files. |
|
|
216
|
+
| `--skip` | Skip files that already exist locally. |
|
|
217
|
+
| `--export-format FORMAT` | Export format for Google Workspace documents (`docx`, `xlsx`, `pptx`, `pdf`, `csv`, `txt`, ...). |
|
|
218
|
+
| `--verify / --no-verify` | Enable or disable MD5 verification. |
|
|
219
|
+
| `--api-key KEY` | Use Google Drive API v3 with an API key. |
|
|
220
|
+
| `--service-account FILE` | Use a GCP service-account JSON key. |
|
|
221
|
+
| `--tui` | Launch the interactive terminal UI. |
|
|
222
|
+
| `--json` | Emit newline-delimited JSON events. |
|
|
223
|
+
|
|
224
|
+
See the full [CLI reference](docs/cli.md) for every command and option.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Python API
|
|
229
|
+
|
|
230
|
+
`gdown2` exposes both `async` functions and a synchronous convenience wrapper.
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
import asyncio
|
|
234
|
+
import gdown2
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def main() -> None:
|
|
238
|
+
# Synchronous helper for scripts and notebooks.
|
|
239
|
+
tasks = gdown2.download_sync(
|
|
240
|
+
"https://drive.google.com/file/d/1A2B3C4D5E6F7G8H9I0J/view",
|
|
241
|
+
output="./data",
|
|
242
|
+
)
|
|
243
|
+
print(f"Downloaded {len(tasks)} task(s)")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
async def async_example() -> None:
|
|
247
|
+
info = await gdown2.get_info("1A2B3C4D5E6F7G8H9I0J")
|
|
248
|
+
print(f"Resolved: {info.name} ({info.size} bytes)")
|
|
249
|
+
|
|
250
|
+
tree = await gdown2.get_tree(
|
|
251
|
+
"https://drive.google.com/drive/folders/1FolderId_9876543210"
|
|
252
|
+
)
|
|
253
|
+
tasks = await gdown2.download(tree.item.id, output="./data")
|
|
254
|
+
print(f"Completed {sum(t.state == 'done' for t in tasks)} file(s)")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
main()
|
|
258
|
+
asyncio.run(async_example())
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
For custom observers, runtime configuration, and cancellation patterns, see
|
|
262
|
+
[Python API](docs/python-api.md).
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Authentication
|
|
267
|
+
|
|
268
|
+
Public links work with no configuration. Providing credentials switches `gdown2` from
|
|
269
|
+
web scraping to the official Drive API v3, which is recommended for large folders,
|
|
270
|
+
private files, and higher rate limits.
|
|
271
|
+
|
|
272
|
+
Credential sources can be combined with CLI flags, environment variables, and
|
|
273
|
+
`config.toml`. When more than one credential is available, the selected provider order
|
|
274
|
+
is:
|
|
275
|
+
|
|
276
|
+
1. Service account (`--service-account` / `GDOWN2_SERVICE_ACCOUNT_FILE`)
|
|
277
|
+
2. OAuth user file (`GDOWN2_OAUTH_FILE`)
|
|
278
|
+
3. OAuth bearer token (`GDOWN2_ACCESS_TOKEN`)
|
|
279
|
+
4. API key (`--api-key` / `GDOWN2_API_KEY` / `GOOGLE_API_KEY`)
|
|
280
|
+
5. Anonymous public access
|
|
281
|
+
|
|
282
|
+
Environment examples:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# API key
|
|
286
|
+
export GDOWN2_API_KEY="AIza..."
|
|
287
|
+
|
|
288
|
+
# OAuth bearer token
|
|
289
|
+
export GDOWN2_ACCESS_TOKEN="ya29..."
|
|
290
|
+
|
|
291
|
+
# OAuth authorized_user JSON file
|
|
292
|
+
export GDOWN2_OAUTH_FILE="$HOME/.config/gdown2/authorized_user.json"
|
|
293
|
+
|
|
294
|
+
# GCP service account
|
|
295
|
+
export GDOWN2_SERVICE_ACCOUNT_FILE="$HOME/.config/gdown2/service_account.json"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
See [Configuration](docs/configuration.md) for the complete authentication and settings
|
|
299
|
+
reference.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Configuration
|
|
304
|
+
|
|
305
|
+
Configuration is merged with the following precedence:
|
|
306
|
+
|
|
307
|
+
**CLI flags > environment variables > `config.toml` > built-in defaults**
|
|
308
|
+
|
|
309
|
+
Default config location: `~/.config/gdown2/config.toml` (or your OS user config
|
|
310
|
+
directory when that path does not exist).
|
|
311
|
+
|
|
312
|
+
```toml
|
|
313
|
+
[download]
|
|
314
|
+
workers = 6
|
|
315
|
+
chunks_per_file = 4
|
|
316
|
+
rate_limit_rps = 5.0
|
|
317
|
+
max_retries = 5
|
|
318
|
+
timeout_seconds = 30.0
|
|
319
|
+
verify_checksums = true
|
|
320
|
+
existing_policy = "resume" # "resume" | "skip" | "overwrite"
|
|
321
|
+
export_format = "pdf"
|
|
322
|
+
|
|
323
|
+
[auth]
|
|
324
|
+
# Supplying credentials switches gdown2 to Drive API v3.
|
|
325
|
+
api_key = "AIza..."
|
|
326
|
+
# oauth_token_file = "~/.config/gdown2/authorized_user.json"
|
|
327
|
+
# service_account_file = "~/.config/gdown2/service_account.json"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Reliability model
|
|
333
|
+
|
|
334
|
+
`gdown2` treats downloads as recoverable transactions rather than one-shot streams.
|
|
335
|
+
|
|
336
|
+
- **Never writes directly to the final filename.** Incoming bytes go to
|
|
337
|
+
`<filename>.part`. The final file appears only after verification succeeds.
|
|
338
|
+
- **Handles `206`, `200`, and `416` correctly.** If the server honours `Range`, the
|
|
339
|
+
transfer resumes. If it ignores `Range` and returns `200`, the partial file is reset
|
|
340
|
+
instead of being corrupted. If it returns `416`, the existing part is verified and
|
|
341
|
+
promoted when complete.
|
|
342
|
+
- **Protects against stale partial data.** When the remote ETag changes or overwrite mode
|
|
343
|
+
is requested, stale `.part` files are discarded before a new transfer starts.
|
|
344
|
+
- **Verifies before promoting.** Size and MD5 checks run before the atomic rename.
|
|
345
|
+
Checksum failures delete the corrupted part and retry from byte zero.
|
|
346
|
+
- **Survives cancellation.** `Ctrl+C` flushes resume state and leaves partial files
|
|
347
|
+
ready for `gdown2 resume`.
|
|
348
|
+
- **Rejects untrusted redirects.** Only HTTPS Google domains are followed during
|
|
349
|
+
redirects and initial download URLs are validated before use.
|
|
350
|
+
|
|
351
|
+
The complete design and edge-case walkthrough is in [Architecture](docs/architecture.md).
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Exit codes
|
|
356
|
+
|
|
357
|
+
| Code | Name | Meaning |
|
|
358
|
+
|---|---|---|
|
|
359
|
+
| `0` | `OK` | All requested files downloaded (or skipped) and verified. |
|
|
360
|
+
| `1` | `GENERAL_ERROR` | One or more transfers failed. |
|
|
361
|
+
| `2` | `INVALID_ARGUMENT` | Malformed URL or unrecognized Google Drive ID. |
|
|
362
|
+
| `3` | `AUTH_REQUIRED` | HTTP 401 or expired/missing credentials. |
|
|
363
|
+
| `4` | `PERMISSION_DENIED` | HTTP 403 permission error (private file or folder). |
|
|
364
|
+
| `5` | `NOT_FOUND` | HTTP 404 / 410 resource does not exist. |
|
|
365
|
+
| `6` | `QUOTA_EXCEEDED` | Per-file download quota or rate limit exhausted. |
|
|
366
|
+
| `7` | `NETWORK_ERROR` | Unrecoverable transport error or untrusted redirect. |
|
|
367
|
+
| `8` | `INTEGRITY_ERROR` | Size or MD5 checksum mismatch after retries. |
|
|
368
|
+
| `9` | `STORAGE_ERROR` | Insufficient disk space, permission error, or path traversal. |
|
|
369
|
+
| `130` | `CANCELLED` | Interrupted by the user (`Ctrl+C`). |
|
|
370
|
+
|
|
371
|
+
This allows shell scripts and CI jobs to distinguish transient network problems from
|
|
372
|
+
permission or quota failures.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Documentation
|
|
377
|
+
|
|
378
|
+
- [CLI reference](docs/cli.md)
|
|
379
|
+
- [Python API](docs/python-api.md)
|
|
380
|
+
- [Configuration](docs/configuration.md)
|
|
381
|
+
- [Architecture](docs/architecture.md)
|
|
382
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
383
|
+
- [Development guide](docs/development.md)
|
|
384
|
+
- [Releasing and PyPI publishing](docs/releasing.md)
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Development
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
# Create an isolated environment.
|
|
392
|
+
python -m venv .venv
|
|
393
|
+
.venv\Scripts\activate # Windows
|
|
394
|
+
# source .venv/bin/activate # macOS / Linux
|
|
395
|
+
|
|
396
|
+
# Install with development dependencies.
|
|
397
|
+
pip install -e ".[dev]"
|
|
398
|
+
|
|
399
|
+
# Run the test suite.
|
|
400
|
+
pytest
|
|
401
|
+
|
|
402
|
+
# Lint and type-check.
|
|
403
|
+
ruff check .
|
|
404
|
+
mypy gdown2
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
The test suite uses `respx` to mock Google Drive endpoints. It covers URL parsing,
|
|
408
|
+
cross-platform path sanitization, tree operations, retry/AIMD policies, virus-scan
|
|
409
|
+
interstitials, `206`/`200`/`416` resume behaviour, rate-limit backoff, checksum
|
|
410
|
+
recovery, folder pagination, and shortcut cycles.
|
|
411
|
+
|
|
412
|
+
Live end-to-end tests are opt-in:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
GDOWN2_E2E_URL="https://drive.google.com/file/d/<id>/view" pytest tests/e2e
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
See [Development guide](docs/development.md) for the full workflow.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Project layout
|
|
423
|
+
|
|
424
|
+
```text
|
|
425
|
+
gdown2/
|
|
426
|
+
├── domain/ # Pure models, URL parser, tree operations, path policy, errors
|
|
427
|
+
├── application/ # Resolver, crawler, planner, scheduler, worker, verifier, reporter
|
|
428
|
+
├── infrastructure/ # HTTP, Drive API/web adapters, auth, cache, config, storage
|
|
429
|
+
├── interfaces/ # Typer/Rich CLI, Textual TUI, NDJSON event writer
|
|
430
|
+
├── sdk.py # Public async and synchronous Python API
|
|
431
|
+
└── py.typed # PEP 561 typing marker
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
The `domain` layer has no network or filesystem dependencies. Application services
|
|
435
|
+
coordinate work through protocol interfaces, and infrastructure adapters can be replaced
|
|
436
|
+
without touching the engine. See [Architecture](docs/architecture.md) for details.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Contributing
|
|
441
|
+
|
|
442
|
+
Contributions are welcome.
|
|
443
|
+
|
|
444
|
+
1. Fork the repository and create a feature branch.
|
|
445
|
+
2. Install the development dependencies with `pip install -e ".[dev]"`.
|
|
446
|
+
3. Add or update tests for your change.
|
|
447
|
+
4. Run `pytest`, `ruff check .`, and `mypy gdown2`.
|
|
448
|
+
5. Open a pull request with a clear description of the problem and solution.
|
|
449
|
+
|
|
450
|
+
Please keep user-facing strings, comments, and documentation in English.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## License
|
|
455
|
+
|
|
456
|
+
MIT. See [LICENSE](LICENSE).
|