feat(plex): redesign browse tiles — 2-poster montage top, readable label band

The full-bleed 4-poster collage drowned the on-tile text. Split PosterTile in two: the top
half shows up to 2 montage posters, the bottom half is a solid colour-hash band that carries
the label, so the text is always readable. Genre tiles drop the count (just the label now);
collection tiles show the name on one truncated line plus a sub-line of item count · total
runtime. A group with no art falls back to an all-colour tile.

Backend: `/collections` cards gain `total_runtime_s` (a new `runtime_by_key` grouped sum) and
their montage art is capped at 2. Frontend: PosterTile reworked; a `collectionMeta` helper
builds the count·runtime sub-line via the existing formatRuntime.

Verified live: collection and genre tiles render 2 posters over a readable colour band with
the right sub-lines; colour fallback intact for art-less groups.

Gate: tsc/lint/knip/prettier clean, 423 vitest; backend ruff clean, collections tests green
(+ a new art/runtime assertion).
This commit is contained in:
2026-08-23 03:52:21 +02:00
parent da3cb5e694
commit 37d11b602f
7 changed files with 98 additions and 35 deletions
+23 -1
View File
@@ -10,7 +10,7 @@ import app.state as state
from app.auth import current_user
from app.db import get_db
from app.main import app
from app.models import PlexItem, PlexLibrary, User
from app.models import PlexCollection, PlexItem, PlexLibrary, User
def _seed(db):
@@ -70,3 +70,25 @@ def test_multiple_collections_all_is_the_intersection(db, monkeypatch):
"/api/plex/library?scope=movie&collections=A,B&collection_mode=all",
)
assert _titles(resp) == {"A and B"}
def test_collection_cards_carry_montage_art_and_total_runtime(db, monkeypatch):
# The browse-tile montage (top films, best-rated first) + the collection's total runtime.
lib = PlexLibrary(plex_key="1", title="Movies", kind="movie", enabled=True)
db.add(lib)
db.flush()
db.add(PlexCollection(rating_key="A", library_id=lib.id, title="Coll A", child_count=2))
common = dict(library_id=lib.id, kind="movie", collection_keys=["A"])
db.add_all([
PlexItem(rating_key="m1", title="M1", duration_s=3600, rating=8.0,
added_at=datetime(2024, 1, 1, tzinfo=timezone.utc), **common),
PlexItem(rating_key="m2", title="M2", duration_s=1800, rating=9.0,
added_at=datetime(2024, 1, 2, tzinfo=timezone.utc), **common),
])
user = User(email="collart@example.com")
db.add(user)
db.commit()
card = next(c for c in _get(db, user, monkeypatch, "/api/plex/collections").json()["collections"]
if c["id"] == "A")
assert card["art"] == ["m2", "m1"] # best-rated first (m2 9.0 > m1 8.0)
assert card["total_runtime_s"] == 5400 # 3600 + 1800