feat(portal): show each season's own poster beside the season tabs (#1628)

Series detail pages now render the selected season's poster as a season
cover next to the season tabs and description, and the fullscreen episode
panel shows the same poster as a season strip above its tabs.

Resolution is TMDB-first, like the show artwork merge: the lazy season
enrichment stores `/tv/{id}/season/{n}` `poster_path` as a w342 URL in
`tmdb_season_posters` (Xtream) or `StalkerSeriesTmdbSeasonsService.posters()`
(Stalker), under the same write-only-if-changed convergence guard as the
season overview. Xtream falls back to the provider's `seasons[].cover_big`/
`cover` when it is an http(s) URL other than the show poster, because panels
repeat the show poster on every season. Stalker is TMDB-only.

The cover column is not rendered for one-season items, seasons without a
poster, or a failed image, so every fallback is today's markup. It is sized
by a new `--season-cover-width` token (96/120/144px per Settings.coverSize).
The hero poster never follows the season.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 authored and GitHub committed 2026-09-19 11:45:54 +02:00
1 parent 43c1ceac16
commit 7790e68147
36 files changed
+1119 -102

No files matched your search

+7
View File
@@ -1,32 +1,39 @@
// Cover size tokens — consumed by all cover/poster grids and rails.
// Toggled via [data-cover-size] attribute on <html>, set from SettingsStore.
// --season-cover-width sizes the season cover beside the season tabs on the
// series detail page; medium matches the About block's 120px poster.
:root {
--cover-grid-min-width: 148px;
--cover-rail-width: 172px;
--cover-gap: 16px;
--season-cover-width: 120px;
}
:root[data-cover-size='small'] {
--cover-grid-min-width: 120px;
--cover-rail-width: 140px;
--cover-gap: 12px;
--season-cover-width: 96px;
}
:root[data-cover-size='medium'] {
--cover-grid-min-width: 148px;
--cover-rail-width: 172px;
--cover-gap: 16px;
--season-cover-width: 120px;
}
:root[data-cover-size='large'] {
--cover-grid-min-width: 200px;
--cover-rail-width: 220px;
--cover-gap: 20px;
--season-cover-width: 144px;
}
@media (max-width: 600px) {
:root[data-cover-size='large'] {
--cover-grid-min-width: 160px;
--cover-rail-width: 170px;
--season-cover-width: 120px;
}
}