dexverse-cli 0.0.2__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.
- dexverse_cli-0.0.2/.dockerignore +14 -0
- dexverse_cli-0.0.2/.gitignore +41 -0
- dexverse_cli-0.0.2/648584.hdf5 +0 -0
- dexverse_cli-0.0.2/Dockerfile +37 -0
- dexverse_cli-0.0.2/PKG-INFO +198 -0
- dexverse_cli-0.0.2/README.md +168 -0
- dexverse_cli-0.0.2/dexverse/__init__.py +54 -0
- dexverse_cli-0.0.2/dexverse/cli.py +422 -0
- dexverse_cli-0.0.2/dexverse/client.py +128 -0
- dexverse_cli-0.0.2/dexverse/config.py +85 -0
- dexverse_cli-0.0.2/dexverse/convert/__init__.py +38 -0
- dexverse_cli-0.0.2/dexverse/convert/hdf5_to_raw.py +498 -0
- dexverse_cli-0.0.2/dexverse/convert/image_video_encode.py +449 -0
- dexverse_cli-0.0.2/dexverse/downloader/__init__.py +26 -0
- dexverse_cli-0.0.2/dexverse/downloader/base.py +101 -0
- dexverse_cli-0.0.2/dexverse/downloader/http_backend.py +508 -0
- dexverse_cli-0.0.2/dexverse/downloader/manager.py +365 -0
- dexverse_cli-0.0.2/dexverse/downloader/python_s3_backend.py +137 -0
- dexverse_cli-0.0.2/dexverse/downloader/s5cmd_backend.py +170 -0
- dexverse_cli-0.0.2/dexverse/downloader/state.py +263 -0
- dexverse_cli-0.0.2/dexverse/downloader/verify.py +114 -0
- dexverse_cli-0.0.2/dexverse/manifest.py +284 -0
- dexverse_cli-0.0.2/dexverse/mock/__init__.py +2 -0
- dexverse_cli-0.0.2/dexverse/mock/mock_tasks.json +22 -0
- dexverse_cli-0.0.2/dexverse/mock/server.py +211 -0
- dexverse_cli-0.0.2/docker_run.sh +105 -0
- dexverse_cli-0.0.2/docs/DexVerse_Downloader_S3_Final_/345/256/236/346/226/275/346/226/271/346/241/210_/345/216/237/350/267/257/345/276/204/350/220/275/347/233/230/347/211/210.md +1090 -0
- dexverse_cli-0.0.2/docs/PROMPT_copy_and_download_command.md +130 -0
- dexverse_cli-0.0.2/docs/Python_vs_Node_CLI/345/257/271/346/257/224/345/210/206/346/236/220.md +167 -0
- dexverse_cli-0.0.2/docs/Python_vs_Node_/346/216/245/345/205/245C++/345/257/271/346/257/224/345/210/206/346/236/220.md +197 -0
- dexverse_cli-0.0.2/docs//345/210/206/345/217/221/347/273/231/347/256/227/346/263/225/345/233/242/351/230/237_/346/225/231/347/250/213.md +315 -0
- dexverse_cli-0.0.2/docs//346/223/215/344/275/234/346/214/207/345/215/227--/345/234/250Dexechain/344/270/255/350/277/233/350/241/214/346/250/241/344/273/277/345/255/246/344/271/240.md +120 -0
- dexverse_cli-0.0.2/docs//346/234/215/345/212/241/345/231/250/344/275/277/347/224/250/346/211/213/345/206/214.md +64 -0
- dexverse_cli-0.0.2/examples/download (7).json +55 -0
- dexverse_cli-0.0.2/examples/local_demo.py +80 -0
- dexverse_cli-0.0.2/examples/manifest.json +45 -0
- dexverse_cli-0.0.2/examples/manifest_presigned.json +34 -0
- dexverse_cli-0.0.2/pyproject.toml +53 -0
- dexverse_cli-0.0.2/set-creds.ps1 +3 -0
- dexverse_cli-0.0.2/tests/conftest.py +19 -0
- dexverse_cli-0.0.2/tests/test_cli.py +134 -0
- dexverse_cli-0.0.2/tests/test_convert_task_instruction.py +87 -0
- dexverse_cli-0.0.2/tests/test_http_backend.py +405 -0
- dexverse_cli-0.0.2/tests/test_http_resilience.py +203 -0
- dexverse_cli-0.0.2/tests/test_manager_integration.py +202 -0
- dexverse_cli-0.0.2/tests/test_manifest.py +138 -0
- dexverse_cli-0.0.2/tests/test_manifest_aliases.py +134 -0
- dexverse_cli-0.0.2/tests/test_mock_server.py +72 -0
- dexverse_cli-0.0.2/tests/test_presigned_fixes.py +189 -0
- dexverse_cli-0.0.2/tests/test_review_fixes.py +265 -0
- dexverse_cli-0.0.2/tests/test_s5cmd_generation.py +72 -0
- dexverse_cli-0.0.2/tests/test_state.py +73 -0
- dexverse_cli-0.0.2/tests/test_verify.py +67 -0
- dexverse_cli-0.0.2/tests/test_version.py +13 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.whl
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Test / coverage
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# Secrets — NEVER commit (see doc section 13.1)
|
|
21
|
+
.env
|
|
22
|
+
.env.*
|
|
23
|
+
config.toml
|
|
24
|
+
dexverse/config.toml
|
|
25
|
+
|
|
26
|
+
# Real manifests with presigned URLs / S3 creds (regenerated per download).
|
|
27
|
+
# examples/manifest*.json are tracked templates with placeholder URLs.
|
|
28
|
+
/manifest.json
|
|
29
|
+
/manifest*.json
|
|
30
|
+
|
|
31
|
+
# Local download state / artifacts
|
|
32
|
+
.dexverse/
|
|
33
|
+
*.s5cmd
|
|
34
|
+
download-state.json
|
|
35
|
+
download.log
|
|
36
|
+
|
|
37
|
+
# Editor / OS
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/
|
|
40
|
+
.DS_Store
|
|
41
|
+
Thumbs.db
|
|
Binary file
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
# DexVerse downloader — bundles Python + dexverse + s5cmd (doc §19).
|
|
3
|
+
# Supports all three backends: s5cmd (default), python (boto3), http (presigned URLs).
|
|
4
|
+
#
|
|
5
|
+
# docker build -t dexverse/dexverse-dl:latest .
|
|
6
|
+
# docker run --rm -v /data:/out dexverse/dexverse-dl:latest \
|
|
7
|
+
# download --manifest /manifest.json --out /out --backend http
|
|
8
|
+
#
|
|
9
|
+
# Or with a task id (manifest fetched from the backend inside the container):
|
|
10
|
+
# docker run --rm -v /data:/out -e DEXVERSE_TOKEN=xxx \
|
|
11
|
+
# dexverse/dexverse-dl:latest download --task dl_xxx --out /out \
|
|
12
|
+
# --server https://dexverse.example.com --backend http
|
|
13
|
+
|
|
14
|
+
FROM python:3.11-slim AS base
|
|
15
|
+
|
|
16
|
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
17
|
+
PYTHONUNBUFFERED=1 \
|
|
18
|
+
PIP_NO_CACHE_DIR=1
|
|
19
|
+
|
|
20
|
+
# s5cmd (for the high-throughput s5cmd backend; harmless if you only use http/python)
|
|
21
|
+
ARG S5CMD_VERSION=v2.3.0
|
|
22
|
+
ADD https://github.com/peak/s5cmd/releases/download/${S5CMD_VERSION}/s5cmd_${S5CMD_VERSION}_Linux-64bit.tar.gz /tmp/s5cmd.tar.gz
|
|
23
|
+
RUN tar -xzf /tmp/s5cmd.tar.gz -C /usr/local/bin s5cmd && rm /tmp/s5cmd.tar.gz
|
|
24
|
+
|
|
25
|
+
# The package version comes from pyproject.toml. Keep exactly one built wheel
|
|
26
|
+
# in dist/ so Docker never needs a duplicated, hard-coded version number.
|
|
27
|
+
COPY dist/dexverse-*.whl /tmp/wheels/
|
|
28
|
+
RUN set -- /tmp/wheels/dexverse-*.whl; \
|
|
29
|
+
if [ "$#" -ne 1 ]; then \
|
|
30
|
+
echo "expected exactly one dexverse wheel in dist/, found $#"; \
|
|
31
|
+
exit 1; \
|
|
32
|
+
fi; \
|
|
33
|
+
pip install --no-cache-dir "${1}[s3]"
|
|
34
|
+
|
|
35
|
+
WORKDIR /work
|
|
36
|
+
ENTRYPOINT ["dexverse-dl"]
|
|
37
|
+
CMD ["--help"]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dexverse-cli
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: DexVerse dataset downloader for S3-compatible robot training datasets
|
|
5
|
+
Author: DexVerse
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: dataset,dexverse,downloader,minio,robotics,s3,s5cmd
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Requires-Dist: av>=12
|
|
10
|
+
Requires-Dist: h5py>=3.10
|
|
11
|
+
Requires-Dist: httpx>=0.27
|
|
12
|
+
Requires-Dist: numpy>=1.26
|
|
13
|
+
Requires-Dist: pillow>=10
|
|
14
|
+
Requires-Dist: pydantic>=2.0
|
|
15
|
+
Requires-Dist: rich>=13.0
|
|
16
|
+
Requires-Dist: tomlkit>=0.12
|
|
17
|
+
Requires-Dist: typer>=0.12
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: boto3>=1.34; extra == 'dev'
|
|
20
|
+
Requires-Dist: fastapi>=0.110; extra == 'dev'
|
|
21
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
22
|
+
Requires-Dist: moto[s3]>=5.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
24
|
+
Provides-Extra: mock
|
|
25
|
+
Requires-Dist: fastapi>=0.110; extra == 'mock'
|
|
26
|
+
Requires-Dist: uvicorn>=0.27; extra == 'mock'
|
|
27
|
+
Provides-Extra: s3
|
|
28
|
+
Requires-Dist: boto3>=1.34; extra == 's3'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# DexVerse Downloader (`dexverse`)
|
|
32
|
+
|
|
33
|
+
Presigned-URL batch downloader for robot-training datasets.
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
manifest -> dexverse-dl -> httpx -> presigned URLs -> local file landing
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The primary input is the DexVerse backend's raw presigned-URL JSON. For those
|
|
40
|
+
payloads, `storage_path` is the remote object key and `file_name` is the local
|
|
41
|
+
landing path under `--out`.
|
|
42
|
+
|
|
43
|
+
Canonical manifests are still supported. Their local paths come from
|
|
44
|
+
`relativePath`, falling back to `originalPath`, then `objectKey`:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
localPath = out / (file.relativePath || file.originalPath || file.objectKey)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install "dexverse[s3,mock]" # boto3 + fastapi mock server
|
|
54
|
+
# optional legacy S3 backend:
|
|
55
|
+
# install s5cmd (https://github.com/peak/s5cmd) and put it on PATH
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Development:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
62
|
+
uv pip install -e ".[dev]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quick Start
|
|
66
|
+
|
|
67
|
+
If your backend issues short-lived presigned URLs per file, save that response
|
|
68
|
+
as `download.json` and run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
dexverse-dl download --manifest ./download.json --out D:\DexVerse
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No login, no `AWS_*` credentials, no s5cmd, no backend flag, and no TLS flag are
|
|
75
|
+
required for the common internal/self-signed endpoint case. The default backend
|
|
76
|
+
is `http`, and TLS verification is disabled by default for http/presigned
|
|
77
|
+
downloads. Use `--secure` when the endpoint has a valid certificate and you want
|
|
78
|
+
certificate validation.
|
|
79
|
+
|
|
80
|
+
Raw backend output is accepted directly:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
[{
|
|
84
|
+
"download_url": "https://host/bucket/datasets/.../20260429_180359.hdf5?X-Amz-Signature=...",
|
|
85
|
+
"storage_bucket": "dexverse",
|
|
86
|
+
"storage_path": "datasets/.../20260429_180359.hdf5",
|
|
87
|
+
"file_name": "20260429_180359.hdf5",
|
|
88
|
+
"file_size": 14994636,
|
|
89
|
+
"task_instruction": {
|
|
90
|
+
"aaa": "111",
|
|
91
|
+
"aaaa": 222,
|
|
92
|
+
"ewrwrwe": 1.1111111111111112e+54
|
|
93
|
+
}
|
|
94
|
+
}]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Result:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
D:\DexVerse\20260429_180359.hdf5
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`task_instruction` is optional and must be a JSON object. If multiple manifest
|
|
104
|
+
items resolve to the same local path (for example, repeated `file_name`
|
|
105
|
+
values), the first item wins and later duplicates are ignored.
|
|
106
|
+
|
|
107
|
+
Include `file_size` (and preferably `sha256`) for large files. The HTTP backend
|
|
108
|
+
also learns the total size from `Content-Length`/`Content-Range`, but manifest
|
|
109
|
+
metadata lets the CLI verify an existing local file before making a request.
|
|
110
|
+
|
|
111
|
+
Transient timeouts and HTTP 408/425/429/5xx responses are retried five times by
|
|
112
|
+
default. Bytes already received stay in a sibling `<file>.part`; retries and
|
|
113
|
+
later CLI runs request the remaining bytes with HTTP Range, while the final
|
|
114
|
+
destination path only appears after the size check succeeds. The object server
|
|
115
|
+
must support Range requests for efficient resume.
|
|
116
|
+
|
|
117
|
+
Presigned URLs expire, often after 1 hour. Have the backend regenerate the
|
|
118
|
+
manifest right before each download; expired URLs surface as per-file `HTTP 403`
|
|
119
|
+
errors. After generating a fresh `download.json`, run the same command with the
|
|
120
|
+
same `--out` path to continue from the retained `.part`.
|
|
121
|
+
|
|
122
|
+
## CLI
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
dexverse-dl login --server https://dexverse.xxx.com [--token TOKEN]
|
|
126
|
+
dexverse-dl download --manifest ./download.json --out /data/dexverse [--parallel 16] [--secure] [--backend http|python|s5cmd]
|
|
127
|
+
dexverse-dl download --manifest ./download.json --out /data/dexverse [--http-read-timeout 120] [--http-retries 5]
|
|
128
|
+
dexverse-dl download --task dl_xxx --out /data/dexverse [--server URL]
|
|
129
|
+
dexverse-dl verify --manifest PATH --out /data/dexverse [--deep]
|
|
130
|
+
dexverse-dl repair --manifest PATH --out /data/dexverse [--backend http|python|s5cmd]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 还原 HDF5 → 可训练数据
|
|
134
|
+
|
|
135
|
+
下载完成后,把落盘的 `.hdf5` 还原为原始录制结构(逐帧 JPEG + `pose_record_*.json`
|
|
136
|
+
+ `metadata.jsonl`)。如果对应 manifest 带有 `task_instruction`,还会自动生成
|
|
137
|
+
`task_instruction.json`:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# 单文件
|
|
141
|
+
dexverse-dl convert -i /data/dexverse/20260429_180359.hdf5 -o /data/dexverse/restored
|
|
142
|
+
# 批量(递归处理目录下所有 *.hdf5,保持目录结构)
|
|
143
|
+
dexverse-dl convert --input-dir /data/dexverse -o /data/dexverse/restored
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
还原输出形如:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
/data/dexverse/restored/20260429_180359/
|
|
150
|
+
head/left/000000.jpg ...
|
|
151
|
+
head/right/000000.jpg ...
|
|
152
|
+
hand/left/000000.jpg ...
|
|
153
|
+
hand/right/000000.jpg ...
|
|
154
|
+
pose_record_20260429_180359.json
|
|
155
|
+
metadata.jsonl
|
|
156
|
+
task_instruction.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`task_instruction.json` 的内容就是 manifest 中的对象本身,不增加外层包装。
|
|
160
|
+
下载器会把转换所需的映射保存在 `.dexverse/task-instructions/`,无需为
|
|
161
|
+
`convert` 增加额外参数。独立来源且没有该隐藏元数据的 HDF5 仍按原行为还原。
|
|
162
|
+
|
|
163
|
+
该命令依赖 `h5py` / `numpy` / `av` / `pillow`,已列入核心依赖,安装时随包装上。
|
|
164
|
+
|
|
165
|
+
## Python SDK
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from dexverse import DexVerseClient, download_manifest
|
|
169
|
+
|
|
170
|
+
client = DexVerseClient(server="https://dexverse.xxx.com", token="xxx")
|
|
171
|
+
local_root = client.download_task(task_id="dl_xxx", out="/data/dexverse", parallel=16)
|
|
172
|
+
|
|
173
|
+
# or from a local manifest file
|
|
174
|
+
download_manifest(manifest_path="./download.json", out="/data/dexverse")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Layout
|
|
178
|
+
|
|
179
|
+
State files live under `<out>/.dexverse/` and never mix into the dataset tree:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
/data/dexverse/
|
|
183
|
+
.dexverse/task-instructions/
|
|
184
|
+
20260429_180359.hdf5.json
|
|
185
|
+
.dexverse/tasks/<taskId>/
|
|
186
|
+
manifest.json
|
|
187
|
+
download-state.json
|
|
188
|
+
download.log
|
|
189
|
+
20260429_180359.hdf5
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Backends
|
|
193
|
+
|
|
194
|
+
| backend | engine | notes |
|
|
195
|
+
|----------|-------------------|-----------------------------------------|
|
|
196
|
+
| `http` | httpx | default; presigned URLs, no creds/login |
|
|
197
|
+
| `python` | boto3 | legacy S3 fallback |
|
|
198
|
+
| `s5cmd` | peak/s5cmd binary | legacy S3 high-throughput backend |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# DexVerse Downloader (`dexverse`)
|
|
2
|
+
|
|
3
|
+
Presigned-URL batch downloader for robot-training datasets.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
manifest -> dexverse-dl -> httpx -> presigned URLs -> local file landing
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The primary input is the DexVerse backend's raw presigned-URL JSON. For those
|
|
10
|
+
payloads, `storage_path` is the remote object key and `file_name` is the local
|
|
11
|
+
landing path under `--out`.
|
|
12
|
+
|
|
13
|
+
Canonical manifests are still supported. Their local paths come from
|
|
14
|
+
`relativePath`, falling back to `originalPath`, then `objectKey`:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
localPath = out / (file.relativePath || file.originalPath || file.objectKey)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install "dexverse[s3,mock]" # boto3 + fastapi mock server
|
|
24
|
+
# optional legacy S3 backend:
|
|
25
|
+
# install s5cmd (https://github.com/peak/s5cmd) and put it on PATH
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Development:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
32
|
+
uv pip install -e ".[dev]"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
If your backend issues short-lived presigned URLs per file, save that response
|
|
38
|
+
as `download.json` and run:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
dexverse-dl download --manifest ./download.json --out D:\DexVerse
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
No login, no `AWS_*` credentials, no s5cmd, no backend flag, and no TLS flag are
|
|
45
|
+
required for the common internal/self-signed endpoint case. The default backend
|
|
46
|
+
is `http`, and TLS verification is disabled by default for http/presigned
|
|
47
|
+
downloads. Use `--secure` when the endpoint has a valid certificate and you want
|
|
48
|
+
certificate validation.
|
|
49
|
+
|
|
50
|
+
Raw backend output is accepted directly:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
[{
|
|
54
|
+
"download_url": "https://host/bucket/datasets/.../20260429_180359.hdf5?X-Amz-Signature=...",
|
|
55
|
+
"storage_bucket": "dexverse",
|
|
56
|
+
"storage_path": "datasets/.../20260429_180359.hdf5",
|
|
57
|
+
"file_name": "20260429_180359.hdf5",
|
|
58
|
+
"file_size": 14994636,
|
|
59
|
+
"task_instruction": {
|
|
60
|
+
"aaa": "111",
|
|
61
|
+
"aaaa": 222,
|
|
62
|
+
"ewrwrwe": 1.1111111111111112e+54
|
|
63
|
+
}
|
|
64
|
+
}]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Result:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
D:\DexVerse\20260429_180359.hdf5
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`task_instruction` is optional and must be a JSON object. If multiple manifest
|
|
74
|
+
items resolve to the same local path (for example, repeated `file_name`
|
|
75
|
+
values), the first item wins and later duplicates are ignored.
|
|
76
|
+
|
|
77
|
+
Include `file_size` (and preferably `sha256`) for large files. The HTTP backend
|
|
78
|
+
also learns the total size from `Content-Length`/`Content-Range`, but manifest
|
|
79
|
+
metadata lets the CLI verify an existing local file before making a request.
|
|
80
|
+
|
|
81
|
+
Transient timeouts and HTTP 408/425/429/5xx responses are retried five times by
|
|
82
|
+
default. Bytes already received stay in a sibling `<file>.part`; retries and
|
|
83
|
+
later CLI runs request the remaining bytes with HTTP Range, while the final
|
|
84
|
+
destination path only appears after the size check succeeds. The object server
|
|
85
|
+
must support Range requests for efficient resume.
|
|
86
|
+
|
|
87
|
+
Presigned URLs expire, often after 1 hour. Have the backend regenerate the
|
|
88
|
+
manifest right before each download; expired URLs surface as per-file `HTTP 403`
|
|
89
|
+
errors. After generating a fresh `download.json`, run the same command with the
|
|
90
|
+
same `--out` path to continue from the retained `.part`.
|
|
91
|
+
|
|
92
|
+
## CLI
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
dexverse-dl login --server https://dexverse.xxx.com [--token TOKEN]
|
|
96
|
+
dexverse-dl download --manifest ./download.json --out /data/dexverse [--parallel 16] [--secure] [--backend http|python|s5cmd]
|
|
97
|
+
dexverse-dl download --manifest ./download.json --out /data/dexverse [--http-read-timeout 120] [--http-retries 5]
|
|
98
|
+
dexverse-dl download --task dl_xxx --out /data/dexverse [--server URL]
|
|
99
|
+
dexverse-dl verify --manifest PATH --out /data/dexverse [--deep]
|
|
100
|
+
dexverse-dl repair --manifest PATH --out /data/dexverse [--backend http|python|s5cmd]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 还原 HDF5 → 可训练数据
|
|
104
|
+
|
|
105
|
+
下载完成后,把落盘的 `.hdf5` 还原为原始录制结构(逐帧 JPEG + `pose_record_*.json`
|
|
106
|
+
+ `metadata.jsonl`)。如果对应 manifest 带有 `task_instruction`,还会自动生成
|
|
107
|
+
`task_instruction.json`:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# 单文件
|
|
111
|
+
dexverse-dl convert -i /data/dexverse/20260429_180359.hdf5 -o /data/dexverse/restored
|
|
112
|
+
# 批量(递归处理目录下所有 *.hdf5,保持目录结构)
|
|
113
|
+
dexverse-dl convert --input-dir /data/dexverse -o /data/dexverse/restored
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
还原输出形如:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
/data/dexverse/restored/20260429_180359/
|
|
120
|
+
head/left/000000.jpg ...
|
|
121
|
+
head/right/000000.jpg ...
|
|
122
|
+
hand/left/000000.jpg ...
|
|
123
|
+
hand/right/000000.jpg ...
|
|
124
|
+
pose_record_20260429_180359.json
|
|
125
|
+
metadata.jsonl
|
|
126
|
+
task_instruction.json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`task_instruction.json` 的内容就是 manifest 中的对象本身,不增加外层包装。
|
|
130
|
+
下载器会把转换所需的映射保存在 `.dexverse/task-instructions/`,无需为
|
|
131
|
+
`convert` 增加额外参数。独立来源且没有该隐藏元数据的 HDF5 仍按原行为还原。
|
|
132
|
+
|
|
133
|
+
该命令依赖 `h5py` / `numpy` / `av` / `pillow`,已列入核心依赖,安装时随包装上。
|
|
134
|
+
|
|
135
|
+
## Python SDK
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from dexverse import DexVerseClient, download_manifest
|
|
139
|
+
|
|
140
|
+
client = DexVerseClient(server="https://dexverse.xxx.com", token="xxx")
|
|
141
|
+
local_root = client.download_task(task_id="dl_xxx", out="/data/dexverse", parallel=16)
|
|
142
|
+
|
|
143
|
+
# or from a local manifest file
|
|
144
|
+
download_manifest(manifest_path="./download.json", out="/data/dexverse")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Layout
|
|
148
|
+
|
|
149
|
+
State files live under `<out>/.dexverse/` and never mix into the dataset tree:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
/data/dexverse/
|
|
153
|
+
.dexverse/task-instructions/
|
|
154
|
+
20260429_180359.hdf5.json
|
|
155
|
+
.dexverse/tasks/<taskId>/
|
|
156
|
+
manifest.json
|
|
157
|
+
download-state.json
|
|
158
|
+
download.log
|
|
159
|
+
20260429_180359.hdf5
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Backends
|
|
163
|
+
|
|
164
|
+
| backend | engine | notes |
|
|
165
|
+
|----------|-------------------|-----------------------------------------|
|
|
166
|
+
| `http` | httpx | default; presigned URLs, no creds/login |
|
|
167
|
+
| `python` | boto3 | legacy S3 fallback |
|
|
168
|
+
| `s5cmd` | peak/s5cmd binary | legacy S3 high-throughput backend |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""DexVerse Downloader — S3-compatible dataset downloader.
|
|
2
|
+
|
|
3
|
+
Public API:
|
|
4
|
+
|
|
5
|
+
from dexverse import DexVerseClient, download_manifest
|
|
6
|
+
from dexverse import Manifest, FileSpec
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
import tomlkit
|
|
14
|
+
|
|
15
|
+
from .client import DexVerseClient, DexVerseError, download_manifest
|
|
16
|
+
from .manifest import (
|
|
17
|
+
FileSpec,
|
|
18
|
+
Manifest,
|
|
19
|
+
Storage,
|
|
20
|
+
StorageCredentials,
|
|
21
|
+
DownloadOptions,
|
|
22
|
+
resolve_dest,
|
|
23
|
+
resolve_relpath,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def _resolve_version() -> str:
|
|
27
|
+
"""Read the single source version in development, package metadata otherwise."""
|
|
28
|
+
pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
|
29
|
+
if pyproject.is_file():
|
|
30
|
+
document = tomlkit.parse(pyproject.read_text(encoding="utf-8"))
|
|
31
|
+
project_version = document.get("project", {}).get("version")
|
|
32
|
+
if isinstance(project_version, str):
|
|
33
|
+
return project_version
|
|
34
|
+
try:
|
|
35
|
+
return version("dexverse")
|
|
36
|
+
except PackageNotFoundError:
|
|
37
|
+
return "0.0.0+unknown"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__version__ = _resolve_version()
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"DexVerseClient",
|
|
44
|
+
"DexVerseError",
|
|
45
|
+
"download_manifest",
|
|
46
|
+
"Manifest",
|
|
47
|
+
"FileSpec",
|
|
48
|
+
"Storage",
|
|
49
|
+
"StorageCredentials",
|
|
50
|
+
"DownloadOptions",
|
|
51
|
+
"resolve_dest",
|
|
52
|
+
"resolve_relpath",
|
|
53
|
+
"__version__",
|
|
54
|
+
]
|