# Download a version

> Download a dataset version as LeRobot v3, LeRobot v2.1 or MCAP, for Physical Intelligence or Dream Labs, or the files as uploaded

For LeRobot and MCAP version downloads:

You download a saved version, never the live dataset. Saving a version gives
it a name, its tag (for example `1.0`), and pins its selected episodes and
revisions. A preview-made version includes the saved drawing windows you
published as immutable cut episodes. Later drawings, rulings or re-ingest do
not change those media windows, task labels or normalization. Head has no
download: publish a version first.

Without a version in the dataset reference, the command resolves the newest
saved version (`stable`). A fresh upload has none until you tag one, so it
takes `v0`, the upload as it arrived, and says so. `original` downloads the upload's stored files
without a version or rendering; it is described separately below.

| `--format`       | You get                                                  |
| ---------------- | -------------------------------------------------------- |
| `lerobot_v3`     | A LeRobot v3 directory (default)                         |
| `lerobot_v2_1`   | A LeRobot v2.1 directory, for loaders that predate v3    |
| `mcap_ros2`      | An MCAP file set with ROS 2 messages                     |
| `pi_mcap_v1`     | Files to upload to Physical Intelligence (Pi MCAP, π0.7) |
| `dreamlabs_v2_1` | Files to upload to Dream Labs (LeRobot v2.1 with H.264)  |
| `original`       | Every file uploaded to the dataset, byte for byte        |

A version with no kept episodes has no download.

## Dashboard

The dashboard gives you the CLI command for a version, ready to copy. The
download itself runs in the [CLI](#cli).

1. Open **Datasets**. On the dataset's card, click **…** in its top right
   corner, then **Download** and the version's name. Or open the dataset and
   click **Download** in its header. Both offer the newest version.
2. Choose **LeRobot v3.0**, **LeRobot v2.1**, **MCAP (ROS 2)**, **Pi MCAP
   (π0.7)** or **LeRobot v2.1 for Dream Labs**. MCAP gives one `.mcap` file
   per episode, for ROS 2 tools. A version with no robot state, or with a
   camera not stored as AV1 video, can't be made into MCAP. The last two are
   [partner layouts](#partner-layouts).
3. Copy the command and run it on the machine the dataset should go to.

The first time, [install the CLI](https://junction-docs.qualiastudios.dev/cli/overview.md) and run `qualia auth login`
with an API key from your workspace's **Settings → API keys**.

**Download** stays disabled until the dataset has a version. To save one on an
upload or merged dataset, click **Publish**, choose the include groups, and
use **Apply all** to freeze and measure its saved drawings. Review omissions,
then name it and choose **Create version**. Review need not be complete;
**Kept by a person** is the default selection. A mixture instead tags its
resolved head. See [Publish a version](https://junction-docs.qualiastudios.dev/global/guides/curation.md#versions).

For an older version, open the dataset's **versions** tab, which lists every
tag, newest first (**Current** is the head), and click the tag. Its version
page has the same **Download** section. Open **Version details** there to copy
the **DatasetVersion id** or read the version reference, such as
`my-dataset:1.0`. Head has no DatasetVersion id.

## CLI

`qualia dataset pin` saves the version and prints its DatasetVersion id. Use
the dataset slug or UUID before the colon in `qualia dataset download`:

```bash
qualia dataset pin <DATASET_UUID> 1.0 --note "first cut"
# Pinned DatasetVersion <DATASET_VERSION_UUID> (<DATASET>:1.0, 120 episode(s))

# LeRobot v3, the newest version (stable, or v0 when nothing is tagged yet)
qualia dataset download my-dataset

# The version an upload printed, by reference or by its DatasetVersion id
qualia dataset download my-dataset:v0
qualia dataset download my-dataset:<DATASET_VERSION_UUID>

# LeRobot v3, version 1.0
qualia dataset download my-dataset:1.0

# LeRobot v2.1
qualia dataset download my-dataset:1.0 --format lerobot_v2_1

# MCAP with ROS 2 messages
qualia dataset download my-dataset:1.0 --format mcap_ros2

# Files to upload to Physical Intelligence or Dream Labs
qualia dataset download my-dataset:1.0 --format pi_mcap_v1
qualia dataset download my-dataset:1.0 --format dreamlabs_v2_1

# Every file uploaded, byte for byte
qualia dataset download my-dataset --format original
```

The folder defaults to `./<slug>-<tag>`, with `-v21`, `-mcap`, `-pi` or
`-dreamlabs` added for those formats, and to `./<slug>-original` for `original`; pass a second argument to
choose it. A spinner shows while the server builds the version (or, for
`original`, lists the files), then a bar with bytes, speed and files. The
command ends with a line such as
`Saved my-dataset:1.0 as mcap_ros2 to /home/me/my-dataset-1.0-mcap (12 files, 1.2 GB)`.
Files are checked as they arrive, and running the command again after a stop
fetches only what is missing.

`original` is every file uploaded to the dataset, byte for byte, with no version
involved. It also holds what ingest stored beside those files: the annotations
under `meta/qds`, `meta/ingest_report.json` and, for an MCAP upload, the LeRobot
data and videos ingest made from the recordings. Trims and splits are not
applied: the takes are whole, and a split's pieces are only annotations under
`meta/qds`. When the dataset has any, the command says how many on stderr and
how to get the curated episodes (tag a version, then download it). Only an
upload has files of its own, so a merged dataset or a mixture is refused:
download one of its uploads, or a version. For now `original` needs an
organization key; a workspace key is refused.

An MCAP download keeps only what was mapped at upload, so `tf`, diagnostics and
custom topics do not come back; use `original` when you need them byte for
byte.

### What a LeRobot download holds

A LeRobot download (`lerobot_v3` or `lerobot_v2_1`) is the version's episodes,
numbered from 0 in the version's order, with their task labels. State and
action values equal the upload's, except in a handheld recording resolved at
the TCP (below).

It says where each episode came from:

- `robot_type` in `meta/info.json`: the one the upload named (the commonest
  among the version's episodes), or `unknown` when none named one.
- `qds_episode_id`, `qds_source_dataset_id` and `qds_take_episode_id` for each
  episode: the episode, the upload it was recorded in, and the take it was cut
  from (itself, unless it is a piece of a split take). They are keys in
  `meta/episodes.jsonl` (v2.1) and columns of
  `meta/episodes/chunk-000/file-000.parquet` (v3).

`qds_episode_id` is the id the API names the episode by.
`qualia dataset episodes <DATASET_UUID> --json` maps it back: an episode's
`index` there is its `episode_index` in the folder you uploaded, and a piece
carries its take's as `parentIndex`.

It holds no ruling, check finding, reason, moment, subtask or tag, and not the
version's tag or note. Read those from the API and keep them beside the
download.

Three things differ from the upload. Video is AV1: a camera recorded in another
codec is encoded again at the same size and frame rate, so its pixels are close
to the upload's but not equal. `timestamp` is a 64-bit float that starts at 0
in every episode. And `task_index` follows the task texts in byte order, so it
can differ from the upload's. For the upload's own bytes, download `original`.

A handheld (UMI) recording whose poses were resolved at the TCP downloads
differently. One `observation.state` of 20 values replaces the upload's state
and action columns. It holds the calibrated TCP poses: per hand, left then
right, six values for the rotation, then x, y, z and the gripper's finger
joint in radians. There are no action columns: training derives its targets
from later state rows. `meta/info.json` says so under `qds_handheld_state`.

### Partner layouts

`pi_mcap_v1` and `dreamlabs_v2_1` are files you upload to Physical Intelligence
or Dream Labs with your own account. They hold the version's episodes that are
not rejected; `dreamlabs_v2_1` holds only the successful ones. `pi_mcap_v1` is
one set of MCAP shards per episode, a `pi_manifest.json` and `upload_to_pi.py`;
`dreamlabs_v2_1` is LeRobot v2.1 with H.264 video and the embodiment file Dream
Labs reads. Each has a `README.md` that says how to upload it. An episode that
could not be converted is listed there with its error; the rest still
download. A version whose episodes are all rejected (for Dream Labs: none is
successful) has nothing to download in that layout. To send with the
organization's own partner key instead, use **Export** or **Send to** in the
dashboard.

Tagging and resolving `<dataset>:<tag>` act in the API key's own account. See
[`qualia dataset download`](https://junction-docs.qualiastudios.dev/cli/commands.md#qualia-dataset-download).

The first download of a version in a format starts its rendering, which needs
a key that can write to the dataset (`data:write`). Once it is rendered,
downloads of it in that format need read access only (`data:read`). The command
also accepts a full `accounts/<account>/datasets/<dataset>/versions/<tag>` name. The
dashboard's **Download** line names the version by its reference. `head` has no
download: pin a tag first.

That first download waits while the server renders: minutes, one to three for
10 to 24 episodes when measured. A rendering belongs to the renderer that made
it, and a new renderer starts over, so a version you downloaded before can
render again and need `data:write` again.
[`client.versions.get_materialization`](https://junction-docs.qualiastudios.dev/sdk/resources/versions.md#download-a-version)
reads a rendering's state and starts nothing.

## SDK

```python
from qualia import Qualia

client = Qualia()

result = client.data.download(
    "my-dataset:1.0",
    "./my-dataset-1.0",
    format="lerobot_v3",  # or "lerobot_v2_1", "mcap_ros2", "pi_mcap_v1", "dreamlabs_v2_1"
)
print(result.destination, result.file_count)

# every file uploaded, byte for byte (no version)
result = client.data.download("my-dataset", format="original")
```

See [Download a version](https://junction-docs.qualiastudios.dev/sdk/resources/versions.md#download-a-version).

## When a download is refused

A version that contains a blocked episode (a NaN, an infinity, a missing value
or a wrong width in its state or action rows) is refused, because a download
carries every episode. Publish a curated version without those episodes as a
new dataset, then download that version. See
[`qualia dataset curate`](https://junction-docs.qualiastudios.dev/cli/commands.md#qualia-dataset-curate).
`--format original` is not refused: it renders nothing, so it downloads the
upload's files, blocked episodes included.
