dexverse-cli 0.0.4.post1__tar.gz → 0.1.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.
- dexverse_cli-0.1.0/.gitignore +12 -0
- dexverse_cli-0.1.0/.python-version +1 -0
- dexverse_cli-0.1.0/PKG-INFO +112 -0
- dexverse_cli-0.1.0/pyproject.toml +67 -0
- dexverse_cli-0.1.0/src/dexverse_cli/README.md +97 -0
- dexverse_cli-0.1.0/src/dexverse_cli/__init__.py +1 -0
- dexverse_cli-0.1.0/src/dexverse_cli/alignment.py +45 -0
- dexverse_cli-0.1.0/src/dexverse_cli/cli.py +125 -0
- dexverse_cli-0.1.0/src/dexverse_cli/download.py +464 -0
- dexverse_cli-0.1.0/src/dexverse_cli/hdf5_schema.py +519 -0
- dexverse_cli-0.1.0/src/dexverse_cli/http.py +416 -0
- dexverse_cli-0.1.0/src/dexverse_cli/jetson_encoder.py +168 -0
- dexverse_cli-0.1.0/src/dexverse_cli/manifest.py +239 -0
- dexverse_cli-0.1.0/src/dexverse_cli/pack.py +157 -0
- dexverse_cli-0.1.0/src/dexverse_cli/raw_schema.py +418 -0
- dexverse_cli-0.1.0/src/dexverse_cli/state.py +141 -0
- dexverse_cli-0.1.0/src/dexverse_cli/transaction.py +126 -0
- dexverse_cli-0.1.0/src/dexverse_cli/unpack.py +170 -0
- dexverse_cli-0.1.0/src/dexverse_cli/unversioned_hdf5.py +321 -0
- dexverse_cli-0.1.0/uv.lock +1010 -0
- dexverse_cli-0.0.4.post1/.dockerignore +0 -14
- dexverse_cli-0.0.4.post1/.gitignore +0 -41
- dexverse_cli-0.0.4.post1/Dockerfile +0 -37
- dexverse_cli-0.0.4.post1/PKG-INFO +0 -201
- dexverse_cli-0.0.4.post1/README.md +0 -171
- dexverse_cli-0.0.4.post1/decision-traces/2026-08-12_1613_pypi_0_0_4_release_trace.md +0 -84
- dexverse_cli-0.0.4.post1/dexverse/__init__.py +0 -54
- dexverse_cli-0.0.4.post1/dexverse/cli.py +0 -422
- dexverse_cli-0.0.4.post1/dexverse/client.py +0 -128
- dexverse_cli-0.0.4.post1/dexverse/config.py +0 -85
- dexverse_cli-0.0.4.post1/dexverse/convert/__init__.py +0 -38
- dexverse_cli-0.0.4.post1/dexverse/convert/hdf5_to_raw.py +0 -500
- dexverse_cli-0.0.4.post1/dexverse/convert/image_video_encode.py +0 -449
- dexverse_cli-0.0.4.post1/dexverse/downloader/__init__.py +0 -26
- dexverse_cli-0.0.4.post1/dexverse/downloader/base.py +0 -101
- dexverse_cli-0.0.4.post1/dexverse/downloader/http_backend.py +0 -508
- dexverse_cli-0.0.4.post1/dexverse/downloader/manager.py +0 -365
- dexverse_cli-0.0.4.post1/dexverse/downloader/python_s3_backend.py +0 -137
- dexverse_cli-0.0.4.post1/dexverse/downloader/s5cmd_backend.py +0 -170
- dexverse_cli-0.0.4.post1/dexverse/downloader/state.py +0 -269
- dexverse_cli-0.0.4.post1/dexverse/downloader/verify.py +0 -114
- dexverse_cli-0.0.4.post1/dexverse/manifest.py +0 -313
- dexverse_cli-0.0.4.post1/dexverse/mock/__init__.py +0 -2
- dexverse_cli-0.0.4.post1/dexverse/mock/mock_tasks.json +0 -22
- dexverse_cli-0.0.4.post1/dexverse/mock/server.py +0 -211
- dexverse_cli-0.0.4.post1/docker_run.sh +0 -105
- dexverse_cli-0.0.4.post1/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 +0 -1090
- dexverse_cli-0.0.4.post1/docs/PROMPT_copy_and_download_command.md +0 -130
- dexverse_cli-0.0.4.post1/docs/Python_vs_Node_CLI/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -167
- dexverse_cli-0.0.4.post1/docs/Python_vs_Node_/346/216/245/345/205/245C++/345/257/271/346/257/224/345/210/206/346/236/220.md +0 -197
- dexverse_cli-0.0.4.post1/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 +0 -315
- dexverse_cli-0.0.4.post1/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 +0 -120
- dexverse_cli-0.0.4.post1/docs//346/234/215/345/212/241/345/231/250/344/275/277/347/224/250/346/211/213/345/206/214.md +0 -64
- dexverse_cli-0.0.4.post1/examples/download (7).json +0 -55
- dexverse_cli-0.0.4.post1/examples/local_demo.py +0 -80
- dexverse_cli-0.0.4.post1/examples/manifest.json +0 -45
- dexverse_cli-0.0.4.post1/examples/manifest_presigned.json +0 -34
- dexverse_cli-0.0.4.post1/pyproject.toml +0 -59
- dexverse_cli-0.0.4.post1/set-creds.ps1 +0 -3
- dexverse_cli-0.0.4.post1/tests/conftest.py +0 -19
- dexverse_cli-0.0.4.post1/tests/test_cli.py +0 -134
- dexverse_cli-0.0.4.post1/tests/test_convert_additional_info.py +0 -101
- dexverse_cli-0.0.4.post1/tests/test_http_backend.py +0 -426
- dexverse_cli-0.0.4.post1/tests/test_http_resilience.py +0 -203
- dexverse_cli-0.0.4.post1/tests/test_manager_integration.py +0 -202
- dexverse_cli-0.0.4.post1/tests/test_manifest.py +0 -138
- dexverse_cli-0.0.4.post1/tests/test_manifest_aliases.py +0 -180
- dexverse_cli-0.0.4.post1/tests/test_mock_server.py +0 -72
- dexverse_cli-0.0.4.post1/tests/test_presigned_fixes.py +0 -189
- dexverse_cli-0.0.4.post1/tests/test_review_fixes.py +0 -265
- dexverse_cli-0.0.4.post1/tests/test_s5cmd_generation.py +0 -72
- dexverse_cli-0.0.4.post1/tests/test_state.py +0 -73
- dexverse_cli-0.0.4.post1/tests/test_verify.py +0 -67
- dexverse_cli-0.0.4.post1/tests/test_version.py +0 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dexverse-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Download, pack, and unpack DexVerse datasets
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: av<17,>=14
|
|
7
|
+
Requires-Dist: h5py<4,>=3.11
|
|
8
|
+
Requires-Dist: httpx<1,>=0.28
|
|
9
|
+
Requires-Dist: numpy<3,>=1.26
|
|
10
|
+
Requires-Dist: pillow<13,>=10
|
|
11
|
+
Requires-Dist: typer<1,>=0.16
|
|
12
|
+
Provides-Extra: jetson
|
|
13
|
+
Requires-Dist: pygobject<4,>=3.42; (platform_system == 'Linux') and extra == 'jetson'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# dexverse-cli
|
|
17
|
+
|
|
18
|
+
`dexverse-cli` downloads DexVerse dataset manifests over HTTP(S), packs strict
|
|
19
|
+
raw sessions into versioned HDF5 files, and unpacks HDF5 sessions. It requires
|
|
20
|
+
Python 3.10 or newer.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```console
|
|
25
|
+
pip install dexverse-cli
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Download
|
|
29
|
+
|
|
30
|
+
Read a manifest from a file:
|
|
31
|
+
|
|
32
|
+
```console
|
|
33
|
+
dexverse-dl download --manifest manifest.json --out ./dataset
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or pipe a short-lived manifest directly to stdin:
|
|
37
|
+
|
|
38
|
+
```console
|
|
39
|
+
type manifest.json | dexverse-dl download --manifest - --out ./dataset
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A manifest is either a JSON file list or an object containing `files`. Each
|
|
43
|
+
file requires `download_url` and `file_name`. Optional fields are
|
|
44
|
+
`storage_path`, `file_size`, `sha256`, and `meta_data`. Downloaded metadata is
|
|
45
|
+
stored in a versioned sidecar and becomes `task_instruction.json` when the
|
|
46
|
+
corresponding HDF5 file is unpacked; only top-level `data_protocol` is removed
|
|
47
|
+
from that algorithm-facing projection.
|
|
48
|
+
|
|
49
|
+
Downloads use bounded parallelism, retries, and compatible partial-file resume.
|
|
50
|
+
When `sha256` is present it is always checked before the final file is replaced.
|
|
51
|
+
TLS certificate verification is disabled by default for internal endpoints;
|
|
52
|
+
pass `--secure` to enable certificate verification.
|
|
53
|
+
|
|
54
|
+
## Pack
|
|
55
|
+
|
|
56
|
+
Pack one raw session, or the immediate session directories under a parent:
|
|
57
|
+
|
|
58
|
+
```console
|
|
59
|
+
dexverse-dl pack --input ./raw/session --output ./packed --format jpeg
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
JPEG is the platform-independent default. Packing validates the complete raw
|
|
63
|
+
inventory, every image, all camera stream relationships, and pose data before
|
|
64
|
+
transactionally replacing an output. Camera and pose counts may differ; pose is
|
|
65
|
+
linearly aligned to the deterministic reference camera timeline.
|
|
66
|
+
|
|
67
|
+
Jetson MP4 packing is optional:
|
|
68
|
+
|
|
69
|
+
```console
|
|
70
|
+
pip install "dexverse-cli[jetson]"
|
|
71
|
+
dexverse-dl pack --input ./raw/session --output ./packed --format video
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The Python extra does not install native Jetson GStreamer components. Video
|
|
75
|
+
packing reports an explicit error unless the required hardware pipeline is
|
|
76
|
+
available. JPEG packing, download, help, and unpack do not import GStreamer.
|
|
77
|
+
|
|
78
|
+
## Unpack
|
|
79
|
+
|
|
80
|
+
Unpack one HDF5 file:
|
|
81
|
+
|
|
82
|
+
```console
|
|
83
|
+
dexverse-dl unpack --input ./packed/session.hdf5 --output ./restored
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or recursively unpack `*.hdf5` files while preserving their relative paths:
|
|
87
|
+
|
|
88
|
+
```console
|
|
89
|
+
dexverse-dl unpack --input-dir ./packed --output ./restored
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Unpacking validates the complete versioned schema before transactionally
|
|
93
|
+
publishing a restored session. It also retains the supported unversioned HDF5
|
|
94
|
+
read path.
|
|
95
|
+
|
|
96
|
+
## Convert
|
|
97
|
+
|
|
98
|
+
Convert one supported HDF5 file:
|
|
99
|
+
|
|
100
|
+
```console
|
|
101
|
+
dexverse-dl convert --input ./dataset/session.hdf5 --output ./restored
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or recursively convert `*.hdf5` files while preserving their relative paths:
|
|
105
|
+
|
|
106
|
+
```console
|
|
107
|
+
dexverse-dl convert --input-dir ./dataset --output ./restored
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`convert` is the compatibility alias for `unpack`; it retains the same options,
|
|
111
|
+
target mapping, legacy HDF5 support, exit codes, and `converted=...` summary.
|
|
112
|
+
Batches continue after an item failure but exit with status 1 if any item fails.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dexverse-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Download, pack, and unpack DexVerse datasets"
|
|
9
|
+
readme = "src/dexverse_cli/README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"av>=14,<17",
|
|
13
|
+
"h5py>=3.11,<4",
|
|
14
|
+
"httpx>=0.28,<1",
|
|
15
|
+
"numpy>=1.26,<3",
|
|
16
|
+
"pillow>=10,<13",
|
|
17
|
+
"typer>=0.16,<1",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
jetson = [
|
|
22
|
+
"PyGObject>=3.42,<4; platform_system == 'Linux'",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
dexverse-dl = "dexverse_cli.cli:app"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"mypy>=1.15,<2",
|
|
31
|
+
"pytest>=8.3,<10",
|
|
32
|
+
"ruff>=0.11,<1",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/dexverse_cli"]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.sdist]
|
|
39
|
+
include = [
|
|
40
|
+
"/pyproject.toml",
|
|
41
|
+
"/uv.lock",
|
|
42
|
+
"/.python-version",
|
|
43
|
+
"/src/dexverse_cli",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
addopts = "-ra"
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py310"
|
|
52
|
+
line-length = 120
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "UP", "B", "SIM", "RUF", "TID252"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
58
|
+
ban-relative-imports = "all"
|
|
59
|
+
|
|
60
|
+
[tool.ruff.lint.per-file-ignores]
|
|
61
|
+
"tests/**/*.py" = ["TID252"]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
python_version = "3.10"
|
|
65
|
+
strict = true
|
|
66
|
+
warn_unreachable = true
|
|
67
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# dexverse-cli
|
|
2
|
+
|
|
3
|
+
`dexverse-cli` downloads DexVerse dataset manifests over HTTP(S), packs strict
|
|
4
|
+
raw sessions into versioned HDF5 files, and unpacks HDF5 sessions. It requires
|
|
5
|
+
Python 3.10 or newer.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```console
|
|
10
|
+
pip install dexverse-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Download
|
|
14
|
+
|
|
15
|
+
Read a manifest from a file:
|
|
16
|
+
|
|
17
|
+
```console
|
|
18
|
+
dexverse-dl download --manifest manifest.json --out ./dataset
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or pipe a short-lived manifest directly to stdin:
|
|
22
|
+
|
|
23
|
+
```console
|
|
24
|
+
type manifest.json | dexverse-dl download --manifest - --out ./dataset
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A manifest is either a JSON file list or an object containing `files`. Each
|
|
28
|
+
file requires `download_url` and `file_name`. Optional fields are
|
|
29
|
+
`storage_path`, `file_size`, `sha256`, and `meta_data`. Downloaded metadata is
|
|
30
|
+
stored in a versioned sidecar and becomes `task_instruction.json` when the
|
|
31
|
+
corresponding HDF5 file is unpacked; only top-level `data_protocol` is removed
|
|
32
|
+
from that algorithm-facing projection.
|
|
33
|
+
|
|
34
|
+
Downloads use bounded parallelism, retries, and compatible partial-file resume.
|
|
35
|
+
When `sha256` is present it is always checked before the final file is replaced.
|
|
36
|
+
TLS certificate verification is disabled by default for internal endpoints;
|
|
37
|
+
pass `--secure` to enable certificate verification.
|
|
38
|
+
|
|
39
|
+
## Pack
|
|
40
|
+
|
|
41
|
+
Pack one raw session, or the immediate session directories under a parent:
|
|
42
|
+
|
|
43
|
+
```console
|
|
44
|
+
dexverse-dl pack --input ./raw/session --output ./packed --format jpeg
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
JPEG is the platform-independent default. Packing validates the complete raw
|
|
48
|
+
inventory, every image, all camera stream relationships, and pose data before
|
|
49
|
+
transactionally replacing an output. Camera and pose counts may differ; pose is
|
|
50
|
+
linearly aligned to the deterministic reference camera timeline.
|
|
51
|
+
|
|
52
|
+
Jetson MP4 packing is optional:
|
|
53
|
+
|
|
54
|
+
```console
|
|
55
|
+
pip install "dexverse-cli[jetson]"
|
|
56
|
+
dexverse-dl pack --input ./raw/session --output ./packed --format video
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The Python extra does not install native Jetson GStreamer components. Video
|
|
60
|
+
packing reports an explicit error unless the required hardware pipeline is
|
|
61
|
+
available. JPEG packing, download, help, and unpack do not import GStreamer.
|
|
62
|
+
|
|
63
|
+
## Unpack
|
|
64
|
+
|
|
65
|
+
Unpack one HDF5 file:
|
|
66
|
+
|
|
67
|
+
```console
|
|
68
|
+
dexverse-dl unpack --input ./packed/session.hdf5 --output ./restored
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or recursively unpack `*.hdf5` files while preserving their relative paths:
|
|
72
|
+
|
|
73
|
+
```console
|
|
74
|
+
dexverse-dl unpack --input-dir ./packed --output ./restored
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Unpacking validates the complete versioned schema before transactionally
|
|
78
|
+
publishing a restored session. It also retains the supported unversioned HDF5
|
|
79
|
+
read path.
|
|
80
|
+
|
|
81
|
+
## Convert
|
|
82
|
+
|
|
83
|
+
Convert one supported HDF5 file:
|
|
84
|
+
|
|
85
|
+
```console
|
|
86
|
+
dexverse-dl convert --input ./dataset/session.hdf5 --output ./restored
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Or recursively convert `*.hdf5` files while preserving their relative paths:
|
|
90
|
+
|
|
91
|
+
```console
|
|
92
|
+
dexverse-dl convert --input-dir ./dataset --output ./restored
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`convert` is the compatibility alias for `unpack`; it retains the same options,
|
|
96
|
+
target mapping, legacy HDF5 support, exit codes, and `converted=...` summary.
|
|
97
|
+
Batches continue after an item failure but exit with status 1 if any item fails.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import numpy.typing as npt
|
|
7
|
+
|
|
8
|
+
from dexverse_cli.raw_schema import RawSessionV1
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class AlignedPoseV1:
|
|
13
|
+
data: npt.NDArray[np.float32]
|
|
14
|
+
timestamps: npt.NDArray[np.uint64]
|
|
15
|
+
columns: tuple[str, ...]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PackAlignmentPolicyV1:
|
|
19
|
+
"""Linearly align pose values to the deterministic reference camera timeline."""
|
|
20
|
+
|
|
21
|
+
name = "linear_pose_to_reference_camera_v1"
|
|
22
|
+
|
|
23
|
+
def align(self, session: RawSessionV1) -> AlignedPoseV1:
|
|
24
|
+
target = np.asarray(
|
|
25
|
+
[record.timestamp for record in session.records_by_camera[session.reference_camera]],
|
|
26
|
+
dtype=np.float64,
|
|
27
|
+
)
|
|
28
|
+
timestamps = np.asarray(
|
|
29
|
+
[record.timestamp_ns for record in session.records_by_camera[session.reference_camera]],
|
|
30
|
+
dtype=np.uint64,
|
|
31
|
+
)
|
|
32
|
+
columns = session.pose_columns
|
|
33
|
+
result = np.empty((target.size, len(columns)), dtype=np.float32)
|
|
34
|
+
source_times = np.asarray([frame.timestamp for frame in session.pose_frames], dtype=np.float64)
|
|
35
|
+
for column_index, column in enumerate(columns):
|
|
36
|
+
values = np.asarray(
|
|
37
|
+
[frame.data[column] for frame in session.pose_frames],
|
|
38
|
+
dtype=np.float64,
|
|
39
|
+
)
|
|
40
|
+
if values.size == 1:
|
|
41
|
+
aligned = np.full(target.shape, values[0], dtype=np.float64)
|
|
42
|
+
else:
|
|
43
|
+
aligned = np.interp(target, source_times, values)
|
|
44
|
+
result[:, column_index] = aligned.astype(np.float32)
|
|
45
|
+
return AlignedPoseV1(result, timestamps, columns)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Annotated, Literal
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from dexverse_cli.download import download_files
|
|
9
|
+
from dexverse_cli.manifest import ManifestError, read_manifest
|
|
10
|
+
from dexverse_cli.pack import pack_batch
|
|
11
|
+
from dexverse_cli.unpack import unpack_batch
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(no_args_is_help=True, add_completion=False)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@app.command()
|
|
17
|
+
def download(
|
|
18
|
+
manifest: Annotated[str, typer.Option("--manifest", "-m", help="Manifest JSON path or '-'")],
|
|
19
|
+
out: Annotated[Path, typer.Option("--out", "-o", help="Local download root")],
|
|
20
|
+
parallel: Annotated[int, typer.Option("--parallel", "-p", min=1, max=32)] = 4,
|
|
21
|
+
http_read_timeout: Annotated[float, typer.Option("--http-read-timeout", min=0.1)] = 120.0,
|
|
22
|
+
http_retries: Annotated[int, typer.Option("--http-retries", min=0)] = 5,
|
|
23
|
+
secure: Annotated[bool, typer.Option("--secure", help="Verify server TLS certificates")] = False,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Download all files in an HTTP(S) manifest."""
|
|
26
|
+
try:
|
|
27
|
+
files = read_manifest(manifest)
|
|
28
|
+
if not secure:
|
|
29
|
+
typer.echo(
|
|
30
|
+
"warning: TLS certificate verification is disabled; pass --secure to enable it",
|
|
31
|
+
err=True,
|
|
32
|
+
)
|
|
33
|
+
report = download_files(
|
|
34
|
+
files,
|
|
35
|
+
out,
|
|
36
|
+
parallel=parallel,
|
|
37
|
+
secure=secure,
|
|
38
|
+
read_timeout=http_read_timeout,
|
|
39
|
+
max_retries=http_retries,
|
|
40
|
+
)
|
|
41
|
+
except (ManifestError, OSError) as exc:
|
|
42
|
+
typer.echo(f"error: {exc}", err=True)
|
|
43
|
+
raise typer.Exit(code=1) from exc
|
|
44
|
+
|
|
45
|
+
typer.echo(f"downloaded={report.downloaded} skipped={report.skipped} failed={len(report.failures)}")
|
|
46
|
+
for failure in report.failures:
|
|
47
|
+
typer.echo(f"error: {failure}", err=True)
|
|
48
|
+
if not report.ok:
|
|
49
|
+
raise typer.Exit(code=1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.command()
|
|
53
|
+
def pack(
|
|
54
|
+
input_path: Annotated[Path, typer.Option("--input", "-i")],
|
|
55
|
+
output: Annotated[Path, typer.Option("--output", "-o")],
|
|
56
|
+
output_format: Annotated[Literal["jpeg", "video"], typer.Option("--format")] = "jpeg",
|
|
57
|
+
skip_existing: Annotated[bool, typer.Option("--skip-existing")] = False,
|
|
58
|
+
workers: Annotated[int | None, typer.Option("--workers", min=1, max=32)] = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Pack one or more raw dataset sessions into Hdf5SchemaV1 files."""
|
|
61
|
+
try:
|
|
62
|
+
report = pack_batch(
|
|
63
|
+
input_path,
|
|
64
|
+
output,
|
|
65
|
+
output_format=output_format,
|
|
66
|
+
skip_existing=skip_existing,
|
|
67
|
+
workers=workers,
|
|
68
|
+
)
|
|
69
|
+
except (OSError, ValueError) as exc:
|
|
70
|
+
typer.echo(f"error: {exc}", err=True)
|
|
71
|
+
raise typer.Exit(code=1) from exc
|
|
72
|
+
typer.echo(f"packed={report.packed} skipped={report.skipped} failed={len(report.failures)}")
|
|
73
|
+
for failure in report.failures:
|
|
74
|
+
typer.echo(f"error: {failure}", err=True)
|
|
75
|
+
if not report.ok:
|
|
76
|
+
raise typer.Exit(code=1)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@app.command()
|
|
80
|
+
def unpack(
|
|
81
|
+
output: Annotated[Path, typer.Option("--output", "-o")],
|
|
82
|
+
input_file: Annotated[Path | None, typer.Option("--input", "-i")] = None,
|
|
83
|
+
input_dir: Annotated[Path | None, typer.Option("--input-dir")] = None,
|
|
84
|
+
strategy: Annotated[str, typer.Option("--strategy")] = "default",
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Unpack HDF5 sessions to the restored raw dataset layout."""
|
|
87
|
+
run_unpack(output, input_file, input_dir, strategy, verb="unpacked")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@app.command()
|
|
91
|
+
def convert(
|
|
92
|
+
output: Annotated[Path, typer.Option("--output", "-o")],
|
|
93
|
+
input_file: Annotated[Path | None, typer.Option("--input", "-i")] = None,
|
|
94
|
+
input_dir: Annotated[Path | None, typer.Option("--input-dir")] = None,
|
|
95
|
+
strategy: Annotated[str, typer.Option("--strategy")] = "default",
|
|
96
|
+
) -> None:
|
|
97
|
+
"""Convert HDF5 sessions to the restored dataset layout."""
|
|
98
|
+
run_unpack(output, input_file, input_dir, strategy, verb="converted")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run_unpack(
|
|
102
|
+
output: Path,
|
|
103
|
+
input_file: Path | None,
|
|
104
|
+
input_dir: Path | None,
|
|
105
|
+
strategy: str,
|
|
106
|
+
*,
|
|
107
|
+
verb: Literal["unpacked", "converted"],
|
|
108
|
+
) -> None:
|
|
109
|
+
if strategy != "default":
|
|
110
|
+
typer.echo("error: --strategy must be 'default'", err=True)
|
|
111
|
+
raise typer.Exit(code=2)
|
|
112
|
+
if (input_file is None) == (input_dir is None):
|
|
113
|
+
typer.echo("error: pass exactly one of --input or --input-dir", err=True)
|
|
114
|
+
raise typer.Exit(code=2)
|
|
115
|
+
source = input_file if input_file is not None else input_dir
|
|
116
|
+
assert source is not None
|
|
117
|
+
if not source.exists():
|
|
118
|
+
typer.echo(f"error: input path does not exist: {source}", err=True)
|
|
119
|
+
raise typer.Exit(code=1)
|
|
120
|
+
report = unpack_batch(source, output)
|
|
121
|
+
typer.echo(f"{verb}={report.unpacked} frames={report.total_frames} failed={len(report.failures)}")
|
|
122
|
+
for failure in report.failures:
|
|
123
|
+
typer.echo(f"error: {failure}", err=True)
|
|
124
|
+
if not report.ok:
|
|
125
|
+
raise typer.Exit(code=1)
|