feat(portal): season thumbnails in the season dropdown + PR #1628 follow-ups (#1633)

* feat(portal): season thumbnails in the season dropdown + PR #1628 follow-ups

Follow-ups to the season posters shipped in #1628:

- The >6-seasons dropdown (`SeasonTabsComponent`) now carries a 28×42
  season thumbnail at the start of each menu row that has a poster and in
  the closed trigger for the selected season, fed by a new `seasonPosters`
  input from the season container and the fullscreen episode panel. Rows
  without a poster get no placeholder, a failed image is dropped, and the
  pill row stays text-only as the design review decided.
- The fullscreen season strip's episode count uses its own
  `PORTALS.EPISODE_COUNT_ONE/OTHER` keys instead of borrowing the download
  manager's; all 18 locales filled through the i18n merger from their
  existing `DOWNLOADS.EPISODE_COUNT_*` translations.
- The Stalker mock's serve targets no longer pin `PORT` (an nx:run-commands
  `env` entry overrides the shell), and `main.ts` resolves `PORT`, then the
  Playwright-side `MOCK_PORT` alias, then 3210 — so `MOCK_PORT=3310` now
  relocates the whole E2E run. The Xtream mock honours `XTREAM_MOCK_PORT`
  the same way.
- `resolveAutoSelectedSeason` gets a direct spec covering every branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(xtream-mock): mint marketing asset URLs on the port the server bound

Greptile P1 on #1633: the listener honoured `XTREAM_MOCK_PORT`, but
`marketingAssetOrigin()` still read `PORT` alone, so a run relocated only
through the alias sent every poster/backdrop/logo/episode URL to 3211.

One resolver (`resolveXtreamMockPortString` in `mock-port.ts`: `PORT`,
then `XTREAM_MOCK_PORT`, then 3211) now feeds the environment parser, the
marketing asset origin and the demo-guide origin fallback. A spec pins the
precedence and that `marketingAssetUrl` follows the bound port.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

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 14:54:54 +02:00
1 parent 01c423ac43
commit 4cce4acaad
43 files changed
+699 -32

No files matched your search

+16 -1
View File
@@ -291,10 +291,25 @@ poster is the show poster a few hundred pixels below the hero), or when the
image request failed. The hero poster never follows the season: the show
keeps its identity element, the season gets its own picture next to its own
text. The fullscreen episode panel shows the same poster as a compact season
strip (poster, season name, episode count) above its season tabs, under the
strip (poster, season name, episode count — `PORTALS.EPISODE_COUNT_ONE` /
`PORTALS.EPISODE_COUNT_OTHER`) above its season tabs, under the
same gates, from `PortalInlinePlayerComponent.seasonPosters` through
`buildFullscreenEpisodePanelSeasons`.
Beyond six seasons the tabs become a `mat-menu` dropdown, and that dropdown
carries **season thumbnails** from the same `seasonPosters` map
(`SeasonTabsComponent.seasonPosters`, passed by the season container and by
the fullscreen episode panel): a 28×42 poster projected into the leading slot
of each menu row that has one, and the selected season's poster inside the
closed trigger (`season-tabs__dropdown--with-thumb` tightens the pill around
it). A season without a poster gets no placeholder tile — its row simply
starts with the text — and a poster whose image request fails is dropped from
both places rather than left as a broken-image frame (the component keeps its
own failed-URL set, independent of the container's cover column). The pill
row (six seasons or fewer) deliberately stays text-only: the design review
rejected per-pill thumbnails as a second poster rail, and the season cover
beside the tabs already shows the selected season's picture.
### Manual watched toggle for movies
Movies carry the same manual "watched" affordance as episodes, in the
+11 -9
View File
@@ -374,15 +374,17 @@ webServer: [
Playwright waits for every server to be healthy before starting tests. If one is already running (e.g. in local dev), it reuses the existing instance.
**`MOCK_PORT` moves the CLIENT side only** — Playwright's health-check URL and
the `MOCK_SERVER` constants in the specs. The server's own port comes from
`PORT` (`main.ts`), which the `serve` and `serve-with-watch` targets pin to
`3210` in `project.json`, and nothing maps one variable to the other. Setting
`MOCK_PORT` alone therefore points Playwright at a port nothing is listening
on and the run times out waiting for `/health`. It is only useful against a
mock you started yourself on that port (`reuseExistingServer` is on outside
CI); relocating the Nx-managed one would need `MOCK_PORT` passed through as
`PORT`.
**`MOCK_PORT` relocates the whole run.** Playwright's health-check URL and the
`MOCK_SERVER` constants in the specs read it, and `main.ts` resolves the
server's port as `PORT`, then `MOCK_PORT`, then `3210` — so
`MOCK_PORT=3310 pnpm exec playwright test …` starts the Nx-managed mock on
3310 and points every spec at it, which is how two worktrees run E2E side by
side when one already holds 3210. This only works because the `serve` and
`serve-with-watch` targets no longer pin `PORT` in `project.json`: an `env`
entry in `nx:run-commands` overrides the shell (`{...process.env, ...env}`),
so a pinned value silently discarded every override. `PORT=3310 pnpm nx run
stalker-mock-server:serve` relocates the server alone. The Xtream mock has the
same shape with `XTREAM_MOCK_PORT`.
### Test Isolation
+3 -1
View File
@@ -122,7 +122,9 @@ unauthenticated content, so it should not be reachable from other hosts by
accident. Set `HOST=0.0.0.0` explicitly to expose it, which is what you need
when driving the mock from a phone, an STB, a container, or another machine.
Control mode additionally *rejects* an explicitly configured non-loopback host. The Nx serve targets do not pin `PORT`, so an explicit shell
value reaches the parser; its no-value default remains `3211`.
value reaches the parser; without one it falls back to `XTREAM_MOCK_PORT` (the
client-side knob Playwright and the specs read, so one variable relocates the
mock and the tests together) and then to `3211`.
Use a dedicated port rather than the normal `3211` E2E server: