mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 13:46:16 -08:00
perf(stalker): classify the static url before authenticating
Codex P2 on #1364. Both static call sites awaited the session warm-up and only then asked whether the stream needed portal credentials at all — so a movie or channel on a foreign CDN paid for a handshake whose result was immediately discarded. That is not free: non-`create_link` requests carry a 15 s timeout (`stalker.events.ts`), so a portal that is slow or offline stalled playback of a stream the CDN would have served instantly. Cold Favorites/Recently Viewed starts are exactly where this bites, since that is where the session is not warm already. Classification now runs first. Foreign host returns immediately, portal-owned still warms and still falls back to `create_link` without a usable session. Behaviour is otherwise unchanged; only the order and the wasted wait are gone. Two tests moved with it: the foreign-host case now asserts the portal is not contacted at all rather than merely not asked for a link, and the repaired-endpoint case had been written against a foreign-host command, which under the new ordering correctly never reaches the handshake it was meant to be testing — it uses a portal-owned command now. Mutation-checked: restoring warm-before-classify fails the foreign-host test alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bdf096887b
commit
53f9446b87
5 files changed
+86
-32
No files matched your search
@@ -390,6 +390,12 @@ 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 classification happens BEFORE the handshake, not after: a **foreign-host**
|
||||
static URL never needs the session at all, and warming it anyway would stall
|
||||
playback behind a request worth up to 15 s against a portal that may be slow
|
||||
or offline while the CDN is perfectly reachable — for a result that is then
|
||||
discarded.
|
||||
|
||||
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
|
||||
|
||||
@@ -900,6 +900,40 @@ describe('StreamResolverService', () => {
|
||||
expect(playback.headers?.['Authorization']).toBe('Bearer TOKEN-COLD');
|
||||
});
|
||||
|
||||
it('serves a foreign-host static stream without waiting on the portal', async () => {
|
||||
// The CDN needs no portal credentials, so the handshake would be a
|
||||
// discarded result — and a stall of up to 15 s when the portal is
|
||||
// slow or offline but the CDN is fine.
|
||||
playlistsService.getPlaylistById.mockReturnValue(
|
||||
of({
|
||||
_id: 'stalker-1',
|
||||
portalUrl:
|
||||
'https://stalker.example.com/stalker_portal/server/load.php',
|
||||
macAddress: '00:11:22:33:44:55',
|
||||
isFullStalkerPortal: true,
|
||||
} satisfies Partial<Playlist>)
|
||||
);
|
||||
|
||||
const playback = await service.resolvePlayback({
|
||||
uid: 'stalker::stalker-1::94',
|
||||
name: 'Cdn Channel',
|
||||
contentType: 'live',
|
||||
sourceType: 'stalker',
|
||||
playlistId: 'stalker-1',
|
||||
playlistName: 'Stalker',
|
||||
stalkerId: '94',
|
||||
stalkerCmd: 'ffrt3 https://cdn.example.com/live/94.m3u8',
|
||||
stalkerItem: {
|
||||
id: '94',
|
||||
cmd: 'ffrt3 https://cdn.example.com/live/94.m3u8',
|
||||
use_http_tmp_link: '0',
|
||||
},
|
||||
} as UnifiedCollectionItem);
|
||||
|
||||
expect(playback.streamUrl).toBe('https://cdn.example.com/live/94.m3u8');
|
||||
expect(stalkerSession.ensureToken).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('still plays a static stream when the session cannot be established', async () => {
|
||||
// The static URL may point at a CDN that needs no credentials, so a
|
||||
// failed handshake must not cost the user their playback.
|
||||
|
||||
@@ -385,23 +385,29 @@ export class StreamResolverService {
|
||||
// link; a simple portal returns null immediately. The raw row goes
|
||||
// in: the helper applies the repair override itself, exactly as
|
||||
// `executeStalkerRequest` does on the branch below.
|
||||
const sessionUsable = await this.warmStalkerSession(playlist);
|
||||
|
||||
// A foreign-host stream never needed the session. A portal-owned
|
||||
// one with no usable session would be served knowing it will 401,
|
||||
// so fall through to `create_link` instead — it mints a URL that
|
||||
// carries its own token and is the only path that can observe a
|
||||
// failure and trigger the lazy portal repair.
|
||||
if (
|
||||
sessionUsable ||
|
||||
!isStalkerStreamCredentialSafe(portalUrl, staticUrl)
|
||||
) {
|
||||
return this.buildStalkerPlayback(item, playlist, {
|
||||
//
|
||||
// Classified BEFORE authenticating: a foreign-host stream never
|
||||
// needs the session, and warming it anyway would stall playback
|
||||
// behind a handshake against a portal that may be slow or offline
|
||||
// while the CDN is perfectly reachable.
|
||||
const servePlayback = () =>
|
||||
this.buildStalkerPlayback(item, playlist, {
|
||||
macAddress,
|
||||
portalUrl,
|
||||
streamUrl: staticUrl,
|
||||
isLive: item.radio === 'true' ? undefined : true,
|
||||
});
|
||||
|
||||
if (!isStalkerStreamCredentialSafe(portalUrl, staticUrl)) {
|
||||
return servePlayback();
|
||||
}
|
||||
|
||||
// Portal-owned with no usable session would be served knowing it
|
||||
// will 401, so fall through to `create_link` instead — it mints a
|
||||
// URL that carries its own token and is the only path that can
|
||||
// observe a failure and trigger the lazy portal repair.
|
||||
if (await this.warmStalkerSession(playlist)) {
|
||||
return servePlayback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -254,13 +254,11 @@ describe('stalker-player-request.utils', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('serves a foreign-host static url even with no session', async () => {
|
||||
// A CDN stream never needed the portal session, so a failed or
|
||||
// skipped handshake must not push it onto the request path.
|
||||
(stalkerSession.ensureToken as jest.Mock).mockResolvedValue({
|
||||
token: null,
|
||||
});
|
||||
|
||||
it('serves a foreign-host static url without contacting the portal', async () => {
|
||||
// A CDN stream never needs the portal session, so it must not be
|
||||
// pushed onto the request path — and must not wait on a handshake
|
||||
// either: that is up to 15 s per request against a portal that may
|
||||
// be offline while the CDN is reachable.
|
||||
const streamUrl = await fetchStalkerPlaybackLink(deps(), {
|
||||
playlist: {
|
||||
...PLAYLIST,
|
||||
@@ -272,6 +270,7 @@ describe('stalker-player-request.utils', () => {
|
||||
});
|
||||
|
||||
expect(streamUrl).toBe('http://cdn.example/movies/1.mkv');
|
||||
expect(stalkerSession.ensureToken).not.toHaveBeenCalled();
|
||||
expect(dataService.sendIpcEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -289,6 +288,11 @@ describe('stalker-player-request.utils', () => {
|
||||
...stale,
|
||||
portalUrl: 'http://demo.example/stalker_portal/server/load.php',
|
||||
} as PlaylistMeta;
|
||||
// Portal-owned URL: only those reach the handshake now, since a
|
||||
// foreign host never needs the session.
|
||||
(stalkerSession.ensureToken as jest.Mock).mockResolvedValue({
|
||||
token: 'TOKEN-REPAIRED',
|
||||
});
|
||||
|
||||
await fetchStalkerPlaybackLink(
|
||||
{
|
||||
@@ -303,7 +307,7 @@ describe('stalker-player-request.utils', () => {
|
||||
{
|
||||
playlist: stale,
|
||||
selectedContentType: 'itv',
|
||||
cmd: 'ffrt3 http://cdn.example/live/42.m3u8',
|
||||
cmd: 'ffrt3 http://demo.example/live/42.m3u8',
|
||||
linkFlags: { use_http_tmp_link: '0' },
|
||||
}
|
||||
);
|
||||
|
||||
+16
-12
@@ -76,19 +76,13 @@ export async function fetchStalkerPlaybackLink(
|
||||
// Favorites on a cold start would play a same-host gated stream
|
||||
// without a Bearer token. Warming at this single choke point
|
||||
// covers ITV, VOD, radio and downloads alike.
|
||||
const sessionUsable = await ensureStalkerSession(
|
||||
deps,
|
||||
options.playlist
|
||||
);
|
||||
|
||||
// A stream on a foreign host never needed the session, so serve it
|
||||
// regardless. A portal-owned one with no usable session would be
|
||||
// served knowing it will 401 — fall back to the request path
|
||||
// instead, which both mints a URL that carries its own token and
|
||||
// is the only path that can observe a failure and trigger the
|
||||
// lazy portal repair.
|
||||
// Classify BEFORE authenticating. A stream on a foreign host never
|
||||
// needs the portal session, and warming it anyway would block
|
||||
// playback behind a handshake worth up to 15 s per request against
|
||||
// a slow or offline portal (`stalker.events.ts`) for a result that
|
||||
// is then discarded — the CDN is reachable even when the portal is
|
||||
// not.
|
||||
if (
|
||||
sessionUsable ||
|
||||
!isStalkerStreamCredentialSafe(
|
||||
options.playlist.portalUrl ?? '',
|
||||
staticUrl
|
||||
@@ -96,6 +90,16 @@ export async function fetchStalkerPlaybackLink(
|
||||
) {
|
||||
return staticUrl;
|
||||
}
|
||||
|
||||
// Portal-owned: it may be gated on the Bearer token, and minting
|
||||
// the link used to be what established the session. Without a
|
||||
// usable one, serving this would be serving a known 401 — fall
|
||||
// back to the request path instead, which both mints a URL that
|
||||
// carries its own token and is the only path that can observe a
|
||||
// failure and trigger the lazy portal repair.
|
||||
if (await ensureStalkerSession(deps, options.playlist)) {
|
||||
return staticUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user