Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,58 @@ today's whole-series behaviour. The host requests only the missing seasons of a
series it already has from plugins that declare `supports_seasons`, because
any other plugin would add the whole series again.

A plugin that can read the downstream download queue reports progress from
`CheckStatus` in `TargetStatus.progress`, and declares it in its manifest:

```json
{
"type": "request_router.v1",
"id": "arr",
"request_router": { "supports_seasons": true, "reports_download_progress": true }
}
```

The host notices a target's progress on its regular reconcile pass. From then
on it asks a declaring plugin about that target every minute, as long as the
target is `downloading` and the last answer carried `progress`. A target whose
`progress` comes back unset drops back to the regular cadence. The host ignores
`progress` from plugins that do not declare the flag, including every plugin
built before the flag existed.

Set `progress` only while the target's status is `queued` or `downloading` and
the service has something in its download queue for it. Leave it unset
otherwise, including for failed downloads; the host then clears the progress it
last stored. One `DownloadProgress` covers all of a target's downloads:

- `bytes_total` and `bytes_left` sum the target's distinct downloads. Count a
season pack once, even when the service lists it once per episode.
Both are 0 whenever any of those downloads has an unknown size, so the host
shows no percentage rather than an overstated one. Otherwise `bytes_left`
stays between 0 and `bytes_total`.
- `estimated_completion` is the latest estimate across the downloads. Leave it
unset when no download has one.
- `downloads` counts the distinct downloads in flight.
- `phase` is one of the values below, and is required whenever `progress` is
set, including while the size is unknown. The host counts a `progress` with
an empty `phase` and a `bytes_total` of 0 as unset, and clears the progress
it last stored. When downloads differ, report the phase that ranks first in
`import_blocked` > `stalled` > `downloading` > `importing` > `paused` >
`queued`. If any download needs attention, the phase says so; otherwise it
stays `downloading` while anything is still downloading.

| Phase | Meaning |
|---|---|
| `import_blocked` | Downloaded, but the import needs manual attention |
| `stalled` | Downloading, but the service reports a warning or error |
| `downloading` | Transferring |
| `importing` | Downloaded and waiting for or running the import |
| `paused` | Paused in the download client |
| `queued` | Waiting to start, delayed, or waiting for an unavailable download client |

`phase` is an open vocabulary. Hosts treat values they do not know as
`downloading`, so a phase added later still shows as a download in progress on
hosts that predate it.

## Watch sync providers

`watch_sync_provider.v1` lets external plugins participate in Silo's host-owned
Expand Down
23 changes: 23 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ with a `default` arm rather than an exhaustive match, and do not assume the
constants shipped in any given SDK tag are the complete set. The same rule
applies to any future open vocabulary added to `v1`.

`DownloadProgress.phase` runs the other way: plugins send it and the host reads
it. The current values are `queued`, `downloading`, `paused`, `stalled`,
`importing`, and `import_blocked`. Hosts treat a value they do not recognize as
`downloading`, so a phase added later degrades to a plain progress display
rather than an error. An empty `phase` is not a new value: plugins must always
set one, as the `TargetStatus.progress` rules below describe.

## Presence-Sensitive Optional Fields

Some contract fields use proto3 `optional` because absence and zero have
Expand All @@ -76,6 +83,22 @@ wire, the host decides who may receive a season-only request from the
manifest's `RequestRouterDescriptor.supports_seasons` flag instead. An absent
descriptor means the flag is false.

`TargetStatus.progress` is a message field, so its presence is the `nil` check.
Unset means the target has nothing in flight, and the host clears the progress
it stored for it. A set `progress` must carry a `phase`: the host counts one
with an empty `phase` and a `bytes_total` of 0 as unset. With a `phase`, a
`bytes_total` of 0 means at least one of the target's distinct downloads has no
known size yet. `bytes_left` is then 0 too, so the host shows no percentage
rather than an overstated one.
`estimated_completion` is unset when no download has an estimate. Plugins
built before the field existed never set it, so the host refreshes progress
every minute only when the manifest declares
`RequestRouterDescriptor.reports_download_progress`, and then only for a
`downloading` target whose last status carried `progress`. The host ignores
`progress` from a plugin that does not declare the flag. The regular reconcile
pass notices progress first, and a target
whose `progress` comes back unset returns to that cadence.

A season-scoped `GetImagesRequest` is a scope, not a guarantee. Plugins that
can filter by season should do so, and plugins should populate
`ImageRecord.season_number` whenever the season is known. Hosts must bucket and
Expand Down
Loading
Loading