Reported directly ("l'api du contenu des catalogues est hyper lente").
Timed CatalogService's own listing calls directly against the real
server - a 300-item library listing came back in well under half a
second, so that wasn't it. Confirmed the real cost instead:
EmbyClient.proxyResponse (what every thumbnail goes through, via
CatalogService.proxyImage) was silently dropping every caching signal
Emby sent. Confirmed directly against the real server that an image
response already carries Cache-Control: public and a real ETag, but
this method only ever forwarded content-type/content-range/
accept-ranges. With no validator and no cache policy ever reaching the
browser, every one of potentially 300+ tiles in a library grid had to
be re-downloaded in full, from scratch, on every single visit.
Fixed both directions:
- EmbyClient.fetch now also accepts ifNoneMatch/ifModifiedSince, sent
as If-None-Match/If-Modified-Since - and a 304 (the whole point of
sending those) is explicitly excluded from the failure-logging path,
since !res.ok is true for it but it's Emby correctly saying
"unchanged", not an error.
- proxyResponse reads the browser's own If-None-Match/If-Modified-Since
request headers and forwards them to Emby, and forwards
cache-control/etag/last-modified back from Emby's response,
alongside the existing content-range/accept-ranges pair.
Confirmed end to end against the real server, not just reasoned about:
a plain image GET comes back 200 with Cache-Control: public + a real
ETag; the same request repeated with that ETag as If-None-Match comes
back a real 304 with a zero-byte body. Harmless for everything else
this method proxies (video/audio segments, playlists, subtitles) - it
only forwards whatever Emby itself decided, never invents a policy.
New EmbyClient.proxyResponse tests (never exercised directly before -
every existing test that touches it mocks it away entirely) cover
forwarding the cache headers on a real bodied response, forwarding the
browser's conditional headers to Emby and relaying a real 304 with no
body, and that a response with neither header doesn't choke or invent
one. New EmbyClient.fetch tests cover a 304 not hitting the failure
log and the two new headers being sent only when given. Full
typecheck/test/build green (101/101 tests). docs/architecture.md and
README.md updated.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>