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
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,9 @@ The `auth_provider.v1` capability also exposes OAuth-flow RPCs (`InitAuthorize`,
watch-provider pipeline. The host owns encrypted per-profile credentials,
authorization-code and device-code flow state, durable desired-state events,
retries, ordering, and reconciliation. Plugins are stateless protocol adapters:
they receive secrets only for the duration of an RPC, map rich movie/episode
identity to an upstream service, and return typed apply or retry outcomes.
they receive secrets only for the duration of an RPC, map rich movie, episode,
and series identity to an upstream service, and return typed apply or retry
outcomes.

Watch-sync plugins must not persist or log credentials, authorization codes,
provider flow state, or secret configuration. `ApplyEvents` is an at-least-once
Expand Down Expand Up @@ -189,17 +190,35 @@ the fault, temporary retries use `TEMPORARY`, rate limits use `RATE_LIMITED`
with an optional delay, and rejected events use a non-retryable fault code.
Connection-wide faults such as invalid credentials belong on the RPC response.

A `SERIES` media item describes the show itself: `external_ids`, `title`, and
`year` identify the series, and the `series_*`, season, and episode fields are
unused.

Ratings are integers from 1 to 10 in every rating field; the host owns
conversion to its own display scale. Plugins convert between the provider's
native scale and 1–10 by rounding half up and clamping to the valid range.
`import_ratings` means `ListRemoteState` returns `RATING` states, and
`export_ratings` means `ApplyEvents` handles both `SET_RATING` and
`REMOVE_RATING`; there is no separate removal flag. `SET_RATING` carries the
value in the event's `rating` field, where zero is never valid. Both operations
are convergent desired-state writes: resending the same value, or removing a
rating that is already absent, must return `APPLIED` or `NO_CHANGE`, never a
fault. The host sends rating events only for media types listed in
`supported_media_types`, and manifest validation requires that list to include
`MOVIE` or `SERIES` when either ratings flag is set.

`ListRemoteState` returns provider-neutral typed subrecords. `watched` carries a
play count and last-watched time; `progress` carries a fractional percentage and
paused time; `favorite` and `watchlist` carry list membership. An item may
contain multiple state families. The host requests only the state families a
sync phase needs, keeps that phase's `cursor` fixed while following ephemeral
page tokens, commits each successful page, and only then persists the final
`next_cursor`. `complete_snapshot=true` means the traversal is authoritative;
when false, missing items are not deletions. An incremental favorite or
watchlist removal is an item whose corresponding list state has `removed=true`;
it may omit `media` when `provider_item_key` identifies a record previously
returned to the host. When
paused time; `favorite` and `watchlist` carry list membership; `rating` carries
a 1–10 rating and when it was set. An item may contain multiple state families.
The host requests only the state families a sync phase needs, keeps that phase's
`cursor` fixed while following ephemeral page tokens, commits each successful
page, and only then persists the final `next_cursor`. `complete_snapshot=true`
means the traversal is authoritative; when false, missing items are not
deletions. In a complete `RATING` traversal, an item absent from the snapshot is
unrated. An incremental favorite, watchlist, or rating removal is an item whose
corresponding state has `removed=true`; it may omit `media` when
`provider_item_key` identifies a record previously returned to the host. When
`provides_watchlist_order=true`, watchlist traversals must be complete snapshots
and the order of returned watchlist states is the remote list order. Event
`list_position` is presence-aware: an explicit zero means the first position,
Expand Down
6 changes: 6 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ APIs), never infer it from the numeric value. Calling
`GetSeasonNumber() != 0` silently conflates "no season scope" with "Specials
(season zero) requested."

Watch-sync rating fields (`WatchSyncEvent.rating` and
`WatchSyncRemoteRatingState.rating`) are deliberately plain `int32`. Valid
ratings run from 1 to 10, so zero never carries a rating and no presence check
is needed.

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 All @@ -73,6 +78,7 @@ verify images by the per-image field rather than assume a filtered response.
- `silo_api_version` is the coarse runtime compatibility gate between Silo and a plugin binary.
- Host installs should reject incompatible API versions before runtime startup.
- A plugin binary should return the same manifest shape that Silo installs, except that binaries may compute their checksum dynamically at runtime.
- From this version, `convert.DecodeCapability` ignores fields and enum values it does not know, so a server node built on this SDK or later, in a mixed-version cluster sharing one database, still loads capability metadata written by a newer SDK; it only loses the parts added after its own SDK version. Nodes built on an earlier SDK decode strictly and reject such metadata, so upgrade every node past them before installing plugins that publish newer fields.

## Go Support

Expand Down
Loading
Loading