feat(library): serve provider entries' local art via the host art proxy
deb / build-publish-host (push) Successful in 9m32s
deb / build-publish (push) Successful in 12m29s
windows-host / package (push) Successful in 16m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 8m48s
arch / build-publish (push) Successful in 19m57s
android / android (push) Successful in 22m41s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m30s
ci / rust (push) Successful in 29m59s
docker / deploy-docs (push) Successful in 23s
ci / web (push) Successful in 1m0s
apple / swift (push) Successful in 1m17s
ci / docs-site (push) Successful in 1m22s
decky / build-publish (push) Successful in 43s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
ci / bench (push) Successful in 6m34s
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 11s
apple / screenshots (push) Successful in 6m25s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 5m27s
deb / build-publish-host (push) Successful in 9m32s
deb / build-publish (push) Successful in 12m29s
windows-host / package (push) Successful in 16m0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Failing after 8m48s
arch / build-publish (push) Successful in 19m57s
android / android (push) Successful in 22m41s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 21m30s
ci / rust (push) Successful in 29m59s
docker / deploy-docs (push) Successful in 23s
ci / web (push) Successful in 1m0s
apple / swift (push) Successful in 1m17s
ci / docs-site (push) Successful in 1m22s
decky / build-publish (push) Successful in 43s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 9s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
ci / bench (push) Successful in 6m34s
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 11s
apple / screenshots (push) Successful in 6m25s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 5m27s
A provider plugin that runs on the host (e.g. the Playnite sync plugin) can now reconcile cover art as an on-host FILE PATH instead of an inlined data URL. `GET /library` rewrites such local-file art into `/library/art/<id>/ <kind>` proxy URLs (the same relative-proxy shape Steam art already uses), and the art proxy serves the bytes from the stored path. Moonlight's /appasset proxy reads the local file too. This is what lets a large Playnite library sync with covers: the reconcile payload carries paths, not bytes, so it no longer blows past the mgmt API's request-body limit and scales to thousands of titles. Cross-platform; local-path detection is Windows-shaped (Playnite is Windows-only). Unit-tested (compiles + 5/5 art tests on Linux). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,12 @@ pub(crate) async fn get_library(
|
||||
if let Some(provider) = q.provider.filter(|p| !p.is_empty()) {
|
||||
games.retain(|g| g.provider.as_deref() == Some(provider.as_str()));
|
||||
}
|
||||
// Rewrite provider entries' local-file art into host art-proxy URLs so a client fetches covers
|
||||
// from the host (a provider like Playnite stores on-host paths; the payload stays tiny at any
|
||||
// library size, and the client never sees an unreachable `C:\…`).
|
||||
for g in &mut games {
|
||||
crate::library::proxy_local_art(&g.id, &mut g.art);
|
||||
}
|
||||
Json(games)
|
||||
}
|
||||
|
||||
@@ -246,14 +252,31 @@ pub(crate) async fn get_library_art(Path((id, kind)): Path<(String, String)>) ->
|
||||
let Some(kind) = crate::library::ArtKind::parse(&kind) else {
|
||||
return api_error(StatusCode::NOT_FOUND, "unknown art kind");
|
||||
};
|
||||
let Some(appid) = id
|
||||
// Steam: CDN / local-cache proxy (id `steam:<appid>`).
|
||||
if let Some(appid) = id
|
||||
.strip_prefix("steam:")
|
||||
.and_then(|s| s.parse::<u32>().ok())
|
||||
else {
|
||||
return api_error(StatusCode::NOT_FOUND, "no art proxy for this store");
|
||||
};
|
||||
match tokio::task::spawn_blocking(move || crate::library::steam_art_bytes(appid, kind)).await {
|
||||
Ok(Some((bytes, ctype))) => ([(header::CONTENT_TYPE, ctype)], bytes).into_response(),
|
||||
_ => api_error(StatusCode::NOT_FOUND, "no art of that kind for this title"),
|
||||
{
|
||||
return match tokio::task::spawn_blocking(move || {
|
||||
crate::library::steam_art_bytes(appid, kind)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Some((bytes, ctype))) => ([(header::CONTENT_TYPE, ctype)], bytes).into_response(),
|
||||
_ => api_error(StatusCode::NOT_FOUND, "no art of that kind for this title"),
|
||||
};
|
||||
}
|
||||
// Custom/provider entry (id `custom:<id>`): serve its stored LOCAL art file — e.g. the Playnite
|
||||
// plugin's covers, reconciled as on-host paths rather than inlined bytes.
|
||||
if let Some(cid) = id.strip_prefix("custom:").map(str::to_owned) {
|
||||
return match tokio::task::spawn_blocking(move || {
|
||||
crate::library::custom_local_art_bytes(&cid, kind)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Some((bytes, ctype))) => ([(header::CONTENT_TYPE, ctype)], bytes).into_response(),
|
||||
_ => api_error(StatusCode::NOT_FOUND, "no art of that kind for this title"),
|
||||
};
|
||||
}
|
||||
api_error(StatusCode::NOT_FOUND, "no art proxy for this store")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user