mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 04:26:16 -08:00
e9cdacf6c30c8785d8e2b4f3d8439f2c331d2473
14
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c2d1bff89d |
fix(playback): apply header override to Stalker radio, redact mock cookie log
Address Codex review feedback on #1335: - The radio branch of the Stalker live layout renders the dedicated audio player, never WebPlayerViewComponent, so the resolved portal headers were built but never applied — an auth-gated radio stream still 403'd. The override sync is extracted into ElectronStreamHeadersService (single owner of the scoped override slot, with clear-only-while-owning semantics so a destroyed consumer cannot wipe a newer consumer's override), applied by WebPlayerViewComponent for video players and by the radio branch before the audio element gets its URL. The service feature-detects the bridge method so partial bridges behave like the PWA instead of throwing. - The gated-stream mock no longer logs the raw Cookie header on 403 — presence only, matching the Authorization logging. - The gated scenario now serves an audio fixture for radio create_link and the Electron e2e covers the radio path end-to-end (bare request 403s, built-in audio player advances past the gate). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
f196abd963 |
feat(playback): forward portal Cookie/Authorization to built-in players
The web players (HTML5/hls.js, Video.js, ArtPlayer, Shaka) could only ever receive User-Agent/Referer/Origin, so any Stalker stream gated on the portal session cookie or Bearer token played exclusively in external MPV/VLC — the long-running "only VLC works" cluster (#849, #910, #732). - request-header-overrides.service: the scoped override now carries Cookie and Authorization, attached only to requests on the exact stream origin, in-memory only, dropped on replace/clear. Unscoped (playlist-level) calls drop credentials fail-closed; control characters in header values are rejected. Chosen over session.cookies.set(): jar cookies attach only to credentialed requests, which would force withCredentials into every engine and break against the Access-Control-Allow-Origin:* IPTV panels send, and jar scoping is port-blind. - WebPlayerViewComponent is now the single owner of the scoped override for every built-in player: it extracts the full header set from the resolved playback, configures the override BEFORE handing the source over (players render only once the source exists), and clears the scoped layer on destroy. HtmlVideoPlayerComponent's own three-header call is removed — it would overwrite the credentialed override. - Stalker VOD, series episodes and radio now build the same portal header set ITV already had (they previously carried no portal headers at all); same-origin playback sends the real User-Agent alongside X-User-Agent. - Stream classification is host-based via one shared predicate (isStalkerStreamCredentialSafe): same-host port changes and scheme upgrades keep the portal profile (the #1158 class), a foreign host or https->http downgrade keeps the credential-free KSPlayer profile. The main-process fallback context uses the same predicate so isStalkerDirectStreamProfile can no longer discard renderer headers. - setUserAgent bridge gains an optional credentials parameter; preload, ipcMain handler and ElectronBridgeApi updated together. - stalker-mock-server: gated-stream scenario (MAC 00:1A:79:00:00:09) whose create_link returns a local /stream/gated/video.mp4 that 403s without the mac cookie + current Bearer token; new Electron e2e proves a built-in player actually plays it (and that the gate refuses bare requests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
297e9fbef8 |
fix(stalker): send cmd in the reference MAG wire format (#1334)
* fix(stalker): send cmd in the reference MAG wire format
A real MAG sends cmd unencoded and the portal decodes its query exactly
once, so a cmd that already contains percent sequences (%3A tokens,
pre-encoded path segments) must pass through untouched. The previous
encodeURIComponent transport (
|
||
|
|
3dbfefa3d8 |
test(stalker): enforce portal auth in the mock and cover the full-portal flow (#1324)
* test(stalker): enforce portal auth in the mock and cover the full-portal flow
The mock server implemented neither get_profile nor get_events and
validated no auth at all, and the e2e suite imported the portal through
/portal.php — which the app classifies as a *simple* portal. The entire
authenticated branch (handshake, token, watchdog, re-auth) therefore had
zero coverage, right before a series of PRs that reworks exactly that.
Mock server:
- serve the canonical /stalker_portal/server/load.php endpoint, which
enforces the Bearer token and the Infomir MAC format like the real
middleware; /portal.php stays tolerant so the existing suite keeps
covering the simple-portal branch
- auth-store.ts models the parts of Stalker 4.9.35 a client can get
wrong: plain-text auth failures with HTTP 200, a handshake that is not
yet a session, idempotent token re-presentation, and permanent
device_id pinning (including the blank-after-pinned lockout)
- add get_profile (status 0/1/2, device conflict, block_msg) and the
get_events watchdog; profile advertises watchdog_timeout/timeslot
- new login-required scenario MAC and POST /invalidate-session so tests
can force a mid-session token loss
- the /stalker proxy route now forwards the token as a Bearer header and
wraps auth failures in the { payload } envelope, matching web-backend
Also moves extractMac into request-mac.ts: importing it from the
categories handler dragged the whole data generator into any consumer,
which broke unit tests on the workspace alias.
E2E: new stalker-auth.e2e.ts asserts handshake precedes get_profile
precedes content, that content requests carry the token while the
handshake does not, that the plain-text failure body is never rendered,
and that the client re-authenticates after the portal drops the session.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(mock): address CodeQL findings in the new portal auth code
Two genuine defects in the code this PR added:
- readBearerToken's /Bearer\s+(.*)$/ backtracks polynomially on
"bearer" followed by a long run of spaces; require the token to start
with a non-space character instead
- the /stalker proxy route read query params as strings without
narrowing, so a repeated key (?url=a&url=b) arrives as an array and
String.prototype.includes silently changes meaning
The remaining three alerts (missing rate limiting x2, sensitive data in
a GET query) are web-service hygiene rules aimed at internet-facing
services. The mock servers bind to localhost, serve fabricated data,
ship in no artifact, and deliberately mirror the real backend proxy's
token-in-query contract; a rate limiter would break the E2E suite that
hammers them. Exclude only those two apps from analysis via a documented
CodeQL config; every shipped path keeps full coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(mock): tighten portal-auth fidelity per review
Review follow-up on #1324 (Greptile 2xP1, Codex 3xP2), all valid:
- adoptToken only accepts tokens the mock actually issued (or the
already-bound one). The stock server pins any presented Bearer —
handshake is stateless there — but a fixture that does the same
cannot catch a client with a broken token pipeline; documented as a
deliberate strictness divergence.
- /invalidate-session clears tokens but keeps pinned device identity:
losing a token never unpins device_id on a real portal, so changed
identity after re-auth must still hit the device-conflict branch.
- The login-required scenario gates on actual do_auth completion
instead of auth_second_step: the app sends auth_second_step=1 on its
very first get_profile, so the parameter check was trivially
bypassed and the status-2 flow never exercised. do_auth is now the
faithful boolean step (non-empty credentials -> {js:true}, recorded;
empty -> {js:false}).
- /server/load.php — the second URL shape isFullStalkerPortal
recognizes — is now served and enforced, directly and through the
/stalker proxy predicate, so full-portal tests cannot silently fall
into the tolerant branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(stalker): prove content actually reloads after re-authentication
Review follow-up on #1324 (Codex P2, valid — the "passes for the wrong
reason" class): the re-auth test only polled for a fresh handshake and
a negative body-text assertion, both of which pass even if the original
content request is never replayed or stays unauthorized. Capture the
content token from the initial import, then assert a post-invalidation
CONTENT request goes out under a DIFFERENT token and that the ITV
categories actually render — the mock only answers content for an
adopted token, so this proves the new token round-tripped through
get_profile. Verified against a live mock that the token genuinely
rotates (old token -> "Authorization failed.", new token -> content).
Also documents the second Codex P2: the mock is deliberately strict on
/server/load.php (a real portal enforces auth there); the import dialog
vs session predicate divergence is a separate app bug the strict
endpoint will let a later PR cover.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(stalker): serialize the portal specs and bind mocks to loopback
Review follow-up on #1324 (Codex, 4xP2):
- Parallel-reset race: under the workspace `fullyParallel` preset the new
auth file ran concurrently with stalker.e2e.ts against one shared mock
process, and each `beforeEach` wiped global state (sessions, favorites)
mid-assertion in the other. Reproduced locally: both suites green in
isolation, two failures when run together. Merged the auth tests into
stalker.e2e.ts and pinned the file to `mode: 'serial'`, which also
removes the pre-existing race between that file's own tests. 19/19
green across three consecutive runs.
- Watchdog was recorded but never asserted, so the suite would stay green
if the full-portal workflow stopped pinging or dropped its token —
`sendWatchdogPing` swallows failures. Now polls for an authenticated
`get_events`.
- Both mock servers listened on every interface (stalker: `listen(PORT)`
with no host; xtream: an explicit `0.0.0.0` default), which made the
CodeQL exclusion's "binds to localhost" rationale untrue. Both now
default to `127.0.0.1` with a `HOST` opt-in, and the config comment
states plainly what the directory-wide ignore trades away.
- Documented that the login-required scenario is HTTP-level only for now:
the client's `do_auth` path is dormant and sends empty credentials, so
the fixture is waiting on that client-side work rather than claiming
end-to-end coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(stalker): force a real auth failure before asserting it stays hidden
Review follow-up on #1324 (Codex 2xP2 + 1xP3), all valid:
- The "never surfaces the plain-text auth failure" test only performed a
successful import, so its negative body assertions were vacuous. It now
imports with a MAC outside the Infomir OUI: the strict endpoint answers
get_profile with a bare {status:1}, no token is ever adopted, and every
content request keeps returning "Authorization failed." Unlike an
invalidated session this cannot be repaired by the client retry, so the
failure is genuinely observed (asserted directly against the proxy) and
only then checked for not leaking into the UI.
- docs/architecture/xtream-mock-server.md still documented the wildcard
bind that
|
||
|
|
e55d55b47f |
feat(mock-data): add shared screenshot-safe poster catalog (#1271)
Moves the fictional movie catalog into `libs/shared/marketing-fixtures` so the
Xtream and Stalker mocks describe the same titles, and adds 20 rendered posters
plus the shared fixture types behind them.
Supporting changes made while getting it green:
- `shared-marketing-fixtures` is classified Tier B in the coverage policy. Not
Tier A: it is fictional fixture data, so a statement percentage over it means
nothing, and a Tier A entry would pull it into the merged coverage map and the
ratchet. Tier B still runs its spec in CI. `stalker-mock-server` needs no entry
of its own — it is already Tier C and the Tier B/C runner falls back to
`pnpm nx test <project>`, so its new `marketing-poster-url.spec.ts` runs.
- Two release-capture defects the catalog reorder introduced, both fixed in
`tools/release/capture-app-driver.ts`:
- VOD stream ids are `MARKETING_VOD_STREAM_ID_BASE + index` and the generator
now lists the showcase movies first, so 62000-62002 became Black Harbor, The
Paper Astronaut and Summer Static while the dashboard seeding still mapped
those ids to the previous titles' backdrops.
- the raw `tsx` spawn of the Xtream mock lacked `--tsconfig
tsconfig.base.json`, so the mock could not resolve
`@iptvnator/shared/marketing-fixtures` and the capture never started. Both
mock projects' own serve targets already passed the flag.
|
||
|
|
9885178f32 |
fix(stalker): refresh stale embedded-series snapshots from favorites and dashboard (#1253)
Favorites and recently-viewed rows store Stalker items as full JSON snapshots, so a vclub-style embedded series[] episode list froze at the moment the row was written: a series favorited when only episode 1 was out kept showing one episode forever when opened from favorites, recents, Continue Watching, or any dashboard rail. New withStalkerSnapshotRefresh() store feature renders the stored snapshot immediately and re-fetches the item from the portal in the background via a title search (get_ordered_list&type=vod&search=..., matched by id, paginated up to 5 pages, wildcard-category retry), patching fresh episodes and cmd into the active selection. The patch is guarded on both the item id and the active playlist id, since Stalker ids are only unique per portal. Only the in-memory selection is patched — the stored snapshot row is deliberately left alone, because every entry path into the detail view runs this refresh and writing it back would add an uncontrolled background writer to the whole-playlist read-modify-write that every favorite/recent mutation performs. Also fixes the stalker-mock-server embedded-series scenario, which generated series[] as objects the app's vclub adapters filter out instead of the episode-number arrays real portals send. Regular type=series and Ministra is_series items are unaffected; Xtream is unaffected (get_series_info is never cached). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
59c15493a7 |
docs: sync CLAUDE.md, AGENTS.md and architecture docs with actual code
Full audit of CLAUDE.md, AGENTS.md, README.md and docs/architecture/ against the codebase; every fix is backed by current code: - remove documented-but-unimplemented IPTVNATOR_DISABLE_HARDWARE_ACCELERATION flag (no reads anywhere in apps/, libs/, tools/) - CLAUDE.md: add epg_channel_mappings to the schema table list - m3u-playlist-module: *-tab dirs -> *-view (+recent-view), selectActivePlaylist, real PlaylistState shape, ChannelEpgMetadata instead of removed EnrichedChannel, actual /workspace/playlists routes, per-view outputs, live-epg-panel-state key - workspace-dashboard: per-rail Settings.dashboardRails toggles, three missing rails in the diagram, split live-favorites/recent-live rails, welcome-dashboard empty-state type, RECENTLY_WATCHED_LIVE_TV title key - stalker-portal: CategoryContentViewComponent for vod/series, collection-route components for favorites/recent, corrected series-view/favorites-button paths, actor/:personId route, epg panel selectors - category-management: reloadCategories lives in with-content.feature.ts, workspace-context-panel owns the dialog, XtreamPendingRestoreService flow - stalker-mock-server (+app README): scenario-seeded faker, resetAll() clears content cache too, ordinal season episode ids, handlers/ dir location - sqlite-db-worker: cancellation shipped (drop from out-of-scope), full operations module list - portal-detail-navigation: replace three removed component paths - tmdb-metadata-enrichment: details cache keys are id:<tmdbId>|v2 - electron-security: CSP frame-src youtube-nocookie exception, sandbox: !frameCopyExperiment nuance - download-manager: libs/portal/xtream instead of xtream-electron folder, data-driven downloads nav, drop removed app-search-result-item note - playlist-backup-restore: settings-backup facade owns the import handoff - workspace-shell: functional workspaceEntryRedirect, playlists route children - iptvnator-ui-guidelines: EPG card radius 11px, detail-view mixin is `base` - embedded-mpv-native, player-controls-contract: minor precision fixes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b3e130aa65 |
feat(stalker): full Live TV channel list for complete search, count badges, and all-channels grid (#1209)
Load the complete ITV channel list once per portal (Ministra get_all_channels with a paged crawl fallback) and use it for: full local search across every channel, per-genre category count badges, an all-channels paginated grid on Live TV entry, and eager bulk EPG so row previews appear without playing a channel. Censored (adult) genres absent from get_all_channels fall back to the legacy paged flow and show no badge. Includes mock-server support, unit + E2E coverage, and architecture docs. |
||
|
|
d24c77a143 | chore(nx): enforce scoped workspace boundaries (#942) | ||
|
|
ab905af285 |
feat(stalker): add radio support
Entire-Checkpoint: f957cd9849e0 |
||
|
|
7cc25380dd | docs: update EPG documentation to reflect new bulk handling and fallback logic | ||
|
|
545dfd8c67 |
feat(stalker): refactor epg handling to use get_epg_info
- Implemented unit tests for StalkerCollectionChannelsListComponent to verify EPG program loading and state management. - Created unit tests for StalkerLiveStreamLayoutComponent to ensure proper rendering of EPG lists and handling of channel playback. - Refactored EPG loading logic in StalkerLiveStreamLayoutComponent to improve performance and maintainability. - Updated HTML template in StalkerLiveStreamLayoutComponent to replace deprecated components with new EPG list component. - Enhanced EPG program handling by introducing methods for timestamp parsing and current program detection. |
||
|
|
ef344f4ee8 |
fix(e2e): resolve 4 remaining test failures in electron e2e suite
- Fix getVisibleSourceTitles: use .playlist-title selector instead of
.mdc-list-item__primary-text which no longer matches playlist-item component
- Fix category-management test: re-navigate to sources page after
refreshSource since refresh navigates away from sources
- Fix recent.e2e test: remove switchUnifiedCollectionContent calls after
clearRecentItems since the content toggle disappears when all items cleared
- Fix stalker mock server: generate numeric episode IDs ("1","2",...) in
season.series array so toSeriesNumbers() can parse them; previously used
non-numeric IDs like "30000-s1-e1" which parsed to NaN, producing empty
episode arrays and causing playFirstSeriesEpisode to time out
https://claude.ai/code/session_01VU6ZZTG7YrhKhKBQvBfmpp
|
||
|
|
95c32708ee |
test: add stalker-mock-server and integrate with e2e tests
- Introduced `stalker-mock-server` with TypeScript configuration for local development and testing. - Updated `playwright.config.ts` to run both Angular app and mock server in parallel during e2e tests. - Created comprehensive e2e tests for Stalker portal functionality, including health checks, portal addition, and content loading. - Added detailed architecture documentation for both `stalker-mock-server` and `xtream-mock-server`, outlining design decisions, data flow, and API protocols. - Implemented `xtream-mock-server` to simulate Xtream Codes API for local development and testing, with corresponding e2e tests. - Enhanced test isolation by resetting mock server state before each test run. |