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
1 change: 1 addition & 0 deletions cmd/silo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"github.com/jackc/pgx/v5/pgxpool"
"github.com/redis/go-redis/v9"

pluginv1 "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1"

Check failure on line 35 in cmd/silo/main.go

View workflow job for this annotation

GitHub Actions / Go DB pins

github.com/Silo-Server/silo-plugin-sdk@v0.18.1-0.20260928194839-1abd582d0304: invalid version: unknown revision 1abd582d0304
sdkcapability "github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/capability"

Check failure on line 36 in cmd/silo/main.go

View workflow job for this annotation

GitHub Actions / Go DB pins

github.com/Silo-Server/silo-plugin-sdk@v0.18.1-0.20260928194839-1abd582d0304: invalid version: unknown revision 1abd582d0304

"github.com/Silo-Server/silo-server/internal/access"
"github.com/Silo-Server/silo-server/internal/activitylog"
Expand Down Expand Up @@ -2979,6 +2979,7 @@
requestReconcileSvc.SetFulfillmentNotifier(notifications.NewRequestFulfillmentNotifier(notificationSystem))
}
taskMgr.Register(tasks.NewReconcileRequestsTask(requestReconcileSvc, 100, deps.DB))
taskMgr.Register(tasks.NewRefreshRequestDownloadsTask(requestReconcileSvc, 200, deps.DB))
if deps.FolderRepo != nil && deps.LibraryScanQueue != nil && pluginService != nil && pluginInstallationStore != nil {
autoscanRepo := autoscan.NewRepository(deps.DB, deps.SecretCipher)
if err := autoscanRepo.MarkInterruptedEvents(appCtx); err != nil {
Expand Down
18 changes: 18 additions & 0 deletions contracts/api/v2/fixtures/admin_requests_ok.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@
{
"id": "42",
"request_id": "r-3",
"integration_id": "integration-1",
"integration_kind": "radarr",
"instance_name": "Radarr",
"quality": "1080p",
"is_anime": false,
"external_id": "7",
"external_status": "queued",
"status": "queued",
"route_name": "Movies",
"created_at": "2026-01-02T03:04:05.678Z",
"updated_at": "2026-01-02T03:04:05.678Z"
}
Expand Down Expand Up @@ -51,9 +57,15 @@
{
"id": "42",
"request_id": "r-2",
"integration_id": "integration-1",
"integration_kind": "radarr",
"instance_name": "Radarr",
"quality": "1080p",
"is_anime": false,
"external_id": "7",
"external_status": "queued",
"status": "queued",
"route_name": "Movies",
"created_at": "2026-01-02T03:04:05.678Z",
"updated_at": "2026-01-02T03:04:05.678Z"
}
Expand Down Expand Up @@ -82,9 +94,15 @@
{
"id": "42",
"request_id": "r-1",
"integration_id": "integration-1",
"integration_kind": "radarr",
"instance_name": "Radarr",
"quality": "1080p",
"is_anime": false,
"external_id": "7",
"external_status": "queued",
"status": "queued",
"route_name": "Movies",
"created_at": "2026-01-02T03:04:05.678Z",
"updated_at": "2026-01-02T03:04:05.678Z"
}
Expand Down
1 change: 0 additions & 1 deletion contracts/api/v2/fixtures/cancel_request_ok.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"season_progress": [],
"requested_by_user_id": "1",
"requested_by_profile_id": "p-owner",
"integration_kind": "radarr",
"is_anime": false,
"targets": [
{
Expand Down
1 change: 0 additions & 1 deletion contracts/api/v2/fixtures/create_request_ok.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"season_progress": [],
"requested_by_user_id": "1",
"requested_by_profile_id": "p-owner",
"integration_kind": "radarr",
"is_anime": false,
"targets": [],
"created_at": "2026-01-02T03:04:05.678Z",
Expand Down
2 changes: 1 addition & 1 deletion contracts/api/v2/fixtures/get_system_info_ok.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"server_version": "unavailable",
"api_major": 2,
"contract_digest": "6c85ebb8076748ccc20f36b779581f0eae17f93ef4ff655cbda73b70bab166ed",
"contract_digest": "2d062f1cc2421ddb2af274137f1c243c82eedfc881dce028c2f2f93def30a6ad",
"links": {
"openapi": "/api/v2/openapi.json",
"capabilities": "/api/v2/capabilities",
Expand Down
30 changes: 23 additions & 7 deletions contracts/api/v2/fixtures/list_my_requests_ok.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"season_progress": [],
"requested_by_user_id": "1",
"requested_by_profile_id": "p-owner",
"integration_kind": "radarr",
"is_anime": false,
"targets": [
{
Expand All @@ -38,29 +37,46 @@
"tmdb_id": 949,
"title": "Heat",
"year": 1995,
"status": "approved",
"status": "downloading",
"outcome": "active",
"state": "approved",
"state": "processing",
"seasons": [],
"season_progress": [],
"requested_by_user_id": "1",
"requested_by_profile_id": "p-owner",
"integration_kind": "radarr",
"is_anime": false,
"targets": [
{
"id": "42",
"request_id": "r-1",
"quality": "1080p",
"is_anime": false,
"status": "queued",
"status": "downloading",
"created_at": "2026-01-02T03:04:05.678Z",
"updated_at": "2026-01-02T03:04:05.678Z"
"updated_at": "2026-01-02T03:04:05.678Z",
"download": {
"phase": "downloading",
"percent": 43,
"bytes_total": 4294967296,
"bytes_left": 2448131358,
"estimated_completion_at": "2026-01-02T03:16:05.678Z",
"downloads": 1,
"updated_at": "2026-01-02T03:04:05.678Z"
}
}
],
"created_at": "2026-01-02T03:04:05.678Z",
"updated_at": "2026-01-02T03:04:05.678Z",
"approved_at": "2026-01-02T03:04:05.678Z"
"approved_at": "2026-01-02T03:04:05.678Z",
"download": {
"phase": "downloading",
"percent": 43,
"bytes_total": 4294967296,
"bytes_left": 2448131358,
"estimated_completion_at": "2026-01-02T03:16:05.678Z",
"downloads": 1,
"updated_at": "2026-01-02T03:04:05.678Z"
}
}
],
"page": {
Expand Down
5 changes: 3 additions & 2 deletions contracts/api/v2/fixtures/request_status_ok.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"revision": "2eed5bf1bef8a5c20e02b089577b35d088cd5442fb8af1b85156ff2772b2037a",
"revision": "dc6458db8af9354952b69baa1805dcb12a4cee23a40517a0dea2c3f02e441bb2",
"state": "available",
"allowed": true,
"requests_enabled": true,
"rating_restrictions_enforced": true,
"follow_supported": true,
"season_requests_supported": true,
"missing_seasons_requestable": false
"missing_seasons_requestable": false,
"download_progress_supported": true
}
105 changes: 103 additions & 2 deletions contracts/api/v2/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -24669,6 +24669,10 @@
"description": "Whether the current principal may use the capability",
"type": "boolean"
},
"download_progress_supported": {
"description": "Whether the server reports download progress (download on requests, their targets, and the title detail's request state). Whether a given request has any depends on its download server's request plugin.",
"type": "boolean"
},
"follow_supported": {
"type": "boolean"
},
Expand Down Expand Up @@ -24706,6 +24710,7 @@
"follow_supported",
"season_requests_supported",
"missing_seasons_requestable",
"download_progress_supported",
"revision",
"state",
"allowed"
Expand Down Expand Up @@ -28534,10 +28539,16 @@
"patternDescription": "a non-zero RFC 3339 instant",
"type": "string"
},
"download": {
"$ref": "#/components/schemas/RequestDownload",
"description": "How far the request's downloads are over all its servers (1080p and 4K together), while any reports them: bytes summed, the phase that needs the most attention, the latest estimate, and the oldest report's time"
},
"external_id": {
"description": "Admins only: the integration's own identifier",
"type": "string"
},
"external_status": {
"description": "Admins only: the status as the download server reports it",
"type": "string"
},
"id": {
Expand All @@ -28555,6 +28566,7 @@
"type": "string"
},
"integration_kind": {
"description": "Admins only: the download server's kind",
"examples": [
"radarr"
],
Expand All @@ -28564,6 +28576,7 @@
"type": "boolean"
},
"last_error": {
"description": "Admins only: why the last submission to a download server failed. It can name servers and routing rules",
"type": "string"
},
"library_content_id": {
Expand Down Expand Up @@ -36324,6 +36337,81 @@
},
"type": "object"
},
"RequestDownload": {
"additionalProperties": false,
"properties": {
"bytes_left": {
"description": "Bytes still to download; present whenever bytes_total is",
"examples": [
2448131358
],
"format": "int64",
"minimum": 0,
"type": "integer"
},
"bytes_total": {
"description": "Size of the downloads in bytes; absent while unknown",
"examples": [
4294967296
],
"format": "int64",
"minimum": 1,
"type": "integer"
},
"downloads": {
"description": "Distinct downloads in flight; a season pack counts once",
"examples": [
1
],
"format": "int64",
"minimum": 0,
"type": "integer"
},
"estimated_completion_at": {
"description": "When the download server expects the downloads to finish; absent when it cannot tell",
"examples": [
"2026-01-02T03:16:05.000Z"
],
"format": "date-time",
"pattern": "^(?:[1-9][0-9]{3}|0[1-9][0-9]{2}|00[1-9][0-9]|000[2-9])-",
"patternDescription": "a non-zero RFC 3339 instant",
"type": "string"
},
"percent": {
"description": "How much has downloaded, rounded down; absent while the size is unknown",
"examples": [
43
],
"format": "int64",
"maximum": 100,
"minimum": 0,
"type": "integer"
},
"phase": {
"description": "queued, downloading, paused, stalled, importing or import_blocked. More values may be added: read an unknown one as downloading, without a percentage",
"examples": [
"downloading"
],
"type": "string"
},
"updated_at": {
"description": "When the server last heard from the download server. A client may hide figures older than about ten minutes",
"examples": [
"2026-01-02T03:04:05.000Z"
],
"format": "date-time",
"pattern": "^(?:[1-9][0-9]{3}|0[1-9][0-9]{2}|00[1-9][0-9]|000[2-9])-",
"patternDescription": "a non-zero RFC 3339 instant",
"type": "string"
}
},
"required": [
"phase",
"downloads",
"updated_at"
],
"type": "object"
},
"RequestMediaCastMember": {
"additionalProperties": false,
"properties": {
Expand Down Expand Up @@ -36741,6 +36829,10 @@
"RequestMediaState": {
"additionalProperties": false,
"properties": {
"download": {
"$ref": "#/components/schemas/RequestDownload",
"description": "How far the active request's downloads are, while its download server reports them. Only the title detail (getRequestMediaDetail) carries it"
},
"following": {
"description": "Whether the viewer will be notified when the media becomes available: they requested it or follow it",
"examples": [
Expand Down Expand Up @@ -36846,11 +36938,16 @@
"patternDescription": "a non-zero RFC 3339 instant",
"type": "string"
},
"download": {
"$ref": "#/components/schemas/RequestDownload",
"description": "How far this target's downloads are, while its download server reports them"
},
"external_id": {
"description": "The integration's own identifier",
"description": "Admins only: the integration's own identifier",
"type": "string"
},
"external_status": {
"description": "Admins only: the status as the download server reports it",
"type": "string"
},
"id": {
Expand All @@ -36862,12 +36959,15 @@
"type": "string"
},
"instance_name": {
"description": "Admins only: the download server's name",
"type": "string"
},
"integration_id": {
"description": "Admins only: the download server holding this target",
"type": "string"
},
"integration_kind": {
"description": "Admins only: the download server's kind",
"examples": [
"radarr"
],
Expand All @@ -36877,6 +36977,7 @@
"type": "boolean"
},
"last_error": {
"description": "Admins only: why the download server failed this target",
"type": "string"
},
"quality": {
Expand All @@ -36894,7 +36995,7 @@
"type": "string"
},
"route_name": {
"description": "The routing rule that sent this target to its server, as named when it was sent",
"description": "Admins only: the routing rule that sent this target to its server, as named when it was sent",
"type": "string"
},
"status": {
Expand Down
7 changes: 7 additions & 0 deletions docs/architecture/api-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,13 @@ series detail lists the regular seasons with availability and request coverage;
requests carry `seasons`, `season_progress` and the `partially_available` state;
`GET /api/v2/requests/status` advertises `season_requests_supported`.

While a request downloads, requests, their targets and the title detail's
request state carry `download` (phase, percent, bytes, estimated completion,
and when the server last heard from the download server), and
`GET /api/v2/requests/status` advertises `download_progress_supported`. The
phase is an open set: clients render an unknown one like `downloading`. See
[Media requests](media-requests.md#download-progress).

A profile can follow a title another profile already requested, to be notified
when it becomes available, with `PUT` and `DELETE
/api/v2/requests/follows/{media_type}/{tmdb_id}`. Both are naturally idempotent.
Expand Down
Loading
Loading