perf(plex): a card fetches its picture at a card's size, not the detail page's

Cards fetched the full cuts: a 400x600 poster (~38 KB) for a ~144px picture and the 1280x720
backdrop (~162 KB) for a ~212px shelf picture. The image proxy now also cuts thumb_sm (240x360)
and art_sm (480x270), cached apart; card pictures (grid, shelves, collection tiles, Up next) carry
both in srcset with sizes=auto, so the browser picks by laid-out width and DPR. Localdev:
17 KB a poster, 56 KB a backdrop. The detail page keeps the full cuts.
This commit is contained in:
2026-10-02 21:27:13 +02:00
parent d928d50686
commit fb2e3d8490
9 changed files with 178 additions and 8 deletions
+77
View File
@@ -0,0 +1,77 @@
"""A card asks the Plex image proxy for a small cut; a detail page keeps the full one (E31 S4e).
Cards fetched the 400×600 poster for a ~144px picture and the 1280×720 backdrop for a ~212px shelf
picture. The `_sm` variants are cut by Plex at a card's size and cached apart from the full ones; an
unknown variant still gets the full poster. DB lane (the proxy opens its own short session).
"""
from fastapi.testclient import TestClient
import app.state as app_state
from app.main import app
from app.models import PlexItem, PlexLibrary
from app.plex import imgcache
from app.routes.plex import playback
class _FakePlex:
calls: list[tuple[str, int | None, int | None]] = []
def __init__(self, db) -> None:
pass
def __enter__(self):
return self
def __exit__(self, *exc) -> None:
return None
def image_bytes(self, key, width=None, height=None):
_FakePlex.calls.append((key, width, height))
return b"\xff\xd8jpeg", "image/jpeg"
def _setup(db, make_user, monkeypatch, tmp_path) -> TestClient:
user = make_user()
lib = PlexLibrary(plex_key="90", title="Movies", kind="movie", enabled=True)
db.add(lib)
db.flush()
db.add(
PlexItem(
rating_key="pm1",
library_id=lib.id,
kind="movie",
title="A Movie",
thumb_key="/library/metadata/pm1/thumb/1",
art_key="/library/metadata/pm1/art/1",
)
)
db.commit()
monkeypatch.setattr(app_state, "_configured_cache", True)
monkeypatch.setattr(playback, "resolved_user_id", lambda request: (user.id, True))
monkeypatch.setattr(playback, "PlexClient", _FakePlex)
monkeypatch.setattr(imgcache, "CACHE_DIR", tmp_path)
_FakePlex.calls = []
return TestClient(app)
def test_each_variant_is_cut_at_its_own_size(db, make_user, monkeypatch, tmp_path):
client = _setup(db, make_user, monkeypatch, tmp_path)
for variant in ("thumb", "thumb_sm", "art", "art_sm"):
assert client.get(f"/api/plex/image/pm1?variant={variant}").status_code == 200
assert _FakePlex.calls == [
("/library/metadata/pm1/thumb/1", 400, 600),
("/library/metadata/pm1/thumb/1", 240, 360),
("/library/metadata/pm1/art/1", 1280, 720),
("/library/metadata/pm1/art/1", 480, 270),
]
# Each cut is cached under its own name: asking again reaches Plex no more.
for variant in ("thumb", "thumb_sm", "art", "art_sm"):
client.get(f"/api/plex/image/pm1?variant={variant}")
assert len(_FakePlex.calls) == 4
def test_an_unknown_variant_gets_the_full_poster(db, make_user, monkeypatch, tmp_path):
client = _setup(db, make_user, monkeypatch, tmp_path)
assert client.get("/api/plex/image/pm1?variant=huge").status_code == 200
assert _FakePlex.calls == [("/library/metadata/pm1/thumb/1", 400, 600)]