fix(stalker): fall back to create_link when a portal-owned static url has no session

Codex P1 on #1364. `create_link` was also the request that could FAIL, and a
failure is what triggers the lazy portal repair. A playlist still misclassified
as token-free, or pointing at an unrepaired endpoint, used to self-heal on that
failure and then play; the static path issues no request, so nothing fires and
the stream just 401s.

Its suggested remedy — routing a skipped warm-up through `repairPortal()` —
cannot be taken literally: a skipped warm-up is the NORMAL case for the many
legitimately token-free reseller panels, and probing each of them on every
playback would cost far more than the round trip this PR removes.

What is decidable without a request is whether we are about to serve a stream
we already know will fail. `ensureStalkerSession` now reports whether the
session can serve credentialed playback — true for a portal needing no token
and for one holding a usable token, false for a full portal left without one —
and both static call sites act on it:

- foreign-host URL: served regardless, it never needed the session;
- portal-owned URL with a usable session: served, as before;
- portal-owned URL with no usable session: falls back to `create_link`, which
  mints a URL carrying its own token AND re-enters the only path that can
  observe a failure and repair.

That covers the unrepaired-endpoint half exactly. The misclassified-as-simple
half stays open by construction — no request means no evidence, and "simple
portal" is indistinguishable from "misclassified" without one. It belongs with
the other reactive-repair work already handed to PR 6: refresh and repair on an
OBSERVED playback authorization failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5 committed 2026-08-03 10:21:17 +02:00
1 parent 277e0a0385
commit 01d9785c3e
5 files changed
+132 -19

No files matched your search

+15 -4
View File
@@ -389,9 +389,18 @@ validates the identity the cached token was negotiated for — which the raw
needed: a simple portal returns immediately and a warm cache with a matching
fingerprint resolves without a request.
The call is best-effort: a static URL may point at a CDN needing no credentials
at all, so a failed handshake degrades to the token-less header set rather than
costing the user their playback.
The call is best-effort in one direction only. A **foreign-host** static URL
never needed the session, so a failed or skipped handshake still serves it. A
**portal-owned** one with no usable session would be served knowing it will
401, so both call sites fall back to `create_link` instead — which mints a URL
carrying its own token and, crucially, is the only path that can observe a
failure and trigger the lazy portal repair. That keeps a playlist still
misclassified as token-free, or pointing at an unrepaired endpoint, on the
self-healing path it was on before this change.
`ensureStalkerSession` returns that verdict: `true` for a portal that needs no
token and for one holding a usable token, `false` for a full portal left
without one.
**Known trade-off: a cached token is not revalidated.** `ensureToken` returns a
same-identity cache entry without touching the network, so the static path no
@@ -475,7 +484,9 @@ Revisit both only with a portal that demonstrably fails without them.
`buildStalkerSelectedVodItem` / `normalizeStalkerVodDetailsItem` /
`normalizeStalkerFavoriteItem`, and an unflagged row gains no flags.
- `stalker-player-request.utils.spec.ts` — static short-circuit, both flags,
the `series` exception, relative VOD commands.
the `series` exception, relative VOD commands, the session warm-up (simple
portal skipped, repaired endpoint used, failure degraded) and the
portal-owned-without-session fallback to `create_link`.
- `with-stalker-player.feature.spec.ts` — ITV/radio store paths and proof that
Recently Viewed stores the `cmd`, never the stream URL.
- `stream-resolver.service.spec.ts` — the collection route, plus the cold