feat(host/web): per-scanner library toggles in the console
apple / swift (push) Successful in 1m16s
apple / screenshots (push) Successful in 6m29s
ci / web (push) Successful in 1m1s
ci / docs-site (push) Failing after 24s
ci / bench (push) Successful in 5m31s
deb / build-publish (push) Successful in 9m21s
decky / build-publish (push) Successful in 24s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 13s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 43s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
android / android (push) Successful in 19m41s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
deb / build-publish-host (push) Successful in 9m44s
arch / build-publish (push) Successful in 18m17s
ci / rust (push) Successful in 19m0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m14s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 25m20s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 25m10s
windows-host / package (push) Successful in 17m47s
docker / deploy-docs (push) Successful in 10s
apple / swift (push) Successful in 1m16s
apple / screenshots (push) Successful in 6m29s
ci / web (push) Successful in 1m1s
ci / docs-site (push) Failing after 24s
ci / bench (push) Successful in 5m31s
deb / build-publish (push) Successful in 9m21s
decky / build-publish (push) Successful in 24s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 13s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 43s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
android / android (push) Successful in 19m41s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 10s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
deb / build-publish-host (push) Successful in 9m44s
arch / build-publish (push) Successful in 18m17s
ci / rust (push) Successful in 19m0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9m14s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 25m20s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 25m10s
windows-host / package (push) Successful in 17m47s
docker / deploy-docs (push) Successful in 10s
Every installed-store scanner (Steam; Lutris+Heroic on Linux; Epic/GOG/
Xbox on Windows) was hardwired on. New library-scanners.json persists the
operator's disabled set (default all on; absent/malformed = all on);
all_games() gates each provider, so disabling one hides its titles from
every surface (console grid, native clients, GameStream app list, launch
resolve). GET /library/scanners lists this platform's scanners + state;
PUT /library/scanners/{id} toggles and emits library.changed — admin lane
only (the cert allowlist's exact-path /library match keeps both off the
LAN surface). The console's Library page grows a "Game sources" card with
one chip per scanner (platform-shaped by the API), EN+DE strings, story.
The scanners are slated to become plugins; the stable per-scanner ids are
the migration seam.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,72 @@ pub(crate) async fn get_library(
|
||||
Json(games)
|
||||
}
|
||||
|
||||
/// Request body for `setLibraryScanner`.
|
||||
#[derive(Deserialize, ToSchema)]
|
||||
pub(crate) struct ScannerToggle {
|
||||
/// Whether the scanner should run on this host.
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
/// List the library scanners
|
||||
///
|
||||
/// The installed-store scanners this host supports — the list is platform-dependent (Steam
|
||||
/// everywhere; Lutris + Heroic on Linux; Epic, GOG, and Xbox/Game Pass on Windows), so the console
|
||||
/// renders a toggle only for scanners that can do anything here. Scanners default to enabled;
|
||||
/// disabling one hides its titles from every library surface from the next read. The user-curated
|
||||
/// custom store is not a scanner and is always on.
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/library/scanners",
|
||||
tag = "library",
|
||||
operation_id = "listLibraryScanners",
|
||||
responses(
|
||||
(status = OK, description = "This host's scanners with their enable state", body = [crate::library::ScannerInfo]),
|
||||
(status = UNAUTHORIZED, description = "Missing or invalid bearer token", body = ApiError),
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn list_library_scanners() -> Json<Vec<crate::library::ScannerInfo>> {
|
||||
Json(crate::library::list_scanners())
|
||||
}
|
||||
|
||||
/// Enable or disable a library scanner
|
||||
///
|
||||
/// Persists the toggle and applies it from the next library read (no restart). Disabling a scanner
|
||||
/// hides its titles everywhere — the console grid, native clients, and the GameStream app list —
|
||||
/// and re-enabling brings them straight back (nothing is deleted; the scan just runs again). Emits
|
||||
/// `library.changed` with the scanner id as `source` when the state changed.
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/library/scanners/{id}",
|
||||
tag = "library",
|
||||
operation_id = "setLibraryScanner",
|
||||
params(("id" = String, Path, description = "The scanner id (e.g. `steam`)")),
|
||||
request_body = ScannerToggle,
|
||||
responses(
|
||||
(status = OK, description = "Toggle stored; the full scanner list", body = [crate::library::ScannerInfo]),
|
||||
(status = UNAUTHORIZED, description = "Missing or invalid bearer token", body = ApiError),
|
||||
(status = NOT_FOUND, description = "No such scanner on this platform", body = ApiError),
|
||||
(status = INTERNAL_SERVER_ERROR, description = "Could not persist the settings", body = ApiError),
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn set_library_scanner(
|
||||
Path(id): Path<String>,
|
||||
ApiJson(toggle): ApiJson<ScannerToggle>,
|
||||
) -> Response {
|
||||
match crate::library::set_scanner_enabled(&id, toggle.enabled) {
|
||||
Ok(Some(scanners)) => {
|
||||
tracing::info!(
|
||||
scanner = %id,
|
||||
enabled = toggle.enabled,
|
||||
"management API: library scanner toggled"
|
||||
);
|
||||
Json(scanners).into_response()
|
||||
}
|
||||
Ok(None) => api_error(StatusCode::NOT_FOUND, "no such scanner on this platform"),
|
||||
Err(e) => api_error(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a custom library entry
|
||||
///
|
||||
/// Creates a user-curated title (e.g. a non-Steam game, an emulator, a ROM) with caller-supplied
|
||||
|
||||
Reference in New Issue
Block a user