mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
fc7f23b2299a0b931555d6b45591216d8a340686
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fc7f23b229 |
feat(playback): forward portal Cookie/Authorization to built-in players (#1335)
* 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> * 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> * fix(playback): claim radio header ownership before awaiting the IPC Codex round-2 P2: leaving the radio route while the header IPC was still in flight left the portal cookie/token installed — ngOnDestroy saw a null scope URL (it was recorded only after the await) and could not clear the override. Ownership is now claimed synchronously before awaiting, destroy invalidates the pending playback continuation, and the apply's stillCurrent verdict is honored. Regression test covers destroy-during-pending-IPC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): carry portal headers into collection playback Codex round-3 P1: Stalker channels opened from Favorites/Recently Viewed resolved through StreamResolverService.resolveStalker(), which returned no portal headers — the video path handed the header owner an empty set and collection radio bypassed it entirely, so auth-gated streams still 403'd from collections. - resolveStalker() now builds the same profile as the live layout via the shared classifier: portal-owned streams get mac cookie/Bearer token/MAG UA/portal Origin+Referer, foreign hosts keep the credential-free KSPlayer profile (both create_link results and direct radio URLs). - UnifiedLiveTabComponent applies the scoped override for radio before the audio element gets its URL (ownership claimed before awaiting the IPC, round-2 lesson), and clears it on close and destroy. - Regression tests: resolver header profiles for portal-host and foreign streams; unified tab radio apply-then-clear. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): release the radio override when a new selection mounts no player Codex round-4 P2: after radio installed its credentials, selecting an item that never mounts a player surface (external video playback, failed resolution) left the old Cookie/Authorization installed — no WebPlayerViewComponent, close, or destroy cleanup runs on that path. Both radio hosts (unified collection tab and the Stalker live layout, which has the identical hole) now release the previously owned radio scope at the start of every new selection; the slot-ownership semantics keep this a no-op when another playback already owns the override. Regression test in the live-layout spec pins the failed-selection path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(playback): state the exact override release points Codex round-5 P2 flagged that the media 'ended' event does not clear the scoped override while the player stays mounted. That is deliberate, not a gap: a mounted player still owns the session — replay or a seek into an unbuffered range must keep working against a gated stream, and clearing on 'ended' would 403 exactly the streams this PR fixes. The credentials only ever travel to the exact origin that issued them, and every dismount path (channel/source change, player close/destroy, radio close, playerless selection) releases them. The security doc and the release note now say precisely that instead of the ambiguous "cleared when playback ends". Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(playback): fit the release note back under the 400-character cap Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
063662028a |
feat(portals): find the same movie in your other playlists (#1286)
* feat(portals): find the same movie in your other playlists A movie that exists in several imported Xtream playlists now shows a "Sources N" chip on its detail page and in the player. Switching playlist mid-film keeps the timecode, a preferred source can be pinned per movie, and a failed stream offers the alternatives instead of a dead end. The governing rule is that a guess is never presented as a fact. Every metadata value carries where it came from — `api` (the provider said so), `parsed` (inferred from the title) or `probe` (we contacted the stream). Facts render as plain tags, guesses are prefixed `~` in a warning colour, and an unknown value renders no tag at all plus a "check" affordance. Ranking and failover read through `factualOnly()`, so a filename claiming 4K is structurally unable to outrank a source that was actually reached. A probe that could not complete reports "unknown", never "unavailable". Scope is deliberately narrow: Xtream to Xtream, movies only, Electron only. Stalker never reaches the `content` table and M3U is a JSON blob whose search forces live content; both are additive later, since the candidate type already carries all three portal kinds. In the PWA every entry point is gated off and the chip renders nothing. Auto-failover is opt-in and off by default. Each source is tried at most once per session, so it terminates structurally, and the switch is never silent — the toast names the new playlist, offers an undo, and warns that the dub may differ only when both sides state an audio track as fact. Notable details: - Playlist names are routinely the pasted URL, credentials included. They are never rendered raw; a short host-only label is derived instead. - Quality is derived from pixel width, not height: a 2.39:1 1080p master is 1920x800, and bucketing that by height would publish "720p" as a fact. - Switching is a single `inlinePlayback.set()` so the player and engine survive and re-seek; the carried position is read before the 15s persistence throttle so it does not rewind. - Sources from one playlist collapse into a group, since the same film often appears there several times under different stream ids. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): stop stale source resolutions from committing Addresses three defects Greptile found in the multi-source review. **Concurrent switches committed out of order.** Selecting a second source before the first resolution returned let the slower request overwrite the newer selection and repoint Undo at itself. `switchTo` now takes a sequence number and drops its result if a newer switch already committed. **Stale switches crossed movie sessions.** Navigating to another film while a resolution was in flight let the continuation activate the old film's source inside the new controller — and restart it from that session's zero resume position. The controller is now snapshotted per operation and the movie session is revalidated after every await. `check()` had the same hazard across its two awaits and is guarded the same way. **Short titles skipped discovery entirely.** The trigram tokenizer cannot index tokens under three characters, so "Up", "It" or "Us" produced an empty MATCH expression and the query was discarded before SQLite was consulted — the chip could never appear for those films. Discovery now falls back to a bounded scan when FTS structurally cannot serve the title; the existing two-tier normalized confirmation still rejects loose hits like "Upgrade". Each fix carries a regression test; all three were mutation-checked by removing the guard and confirming exactly those tests fail. The previous test asserting that short titles return nothing encoded the bug and has been replaced. The host spec passed 400 lines, so its fixtures moved to a shared module and the race suite into its own file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(portals): make the pin decide playback and keep failover going Second round of Greptile review findings. **A pin had no behavioural effect.** Loading a stored pin only decorated the row: Play still started the route's playlist and failover ranking ignored `isPinned`, so "make this the main source" survived a restart as an icon and nothing else. The primary action now starts from the pinned source when one is set, and the pin outranks everything else in failover ranking. **Failover stopped at the first unresolvable candidate.** An expired account or a failing `get_vod_info` on the top-ranked source ended the attempt, and since production calls `failover()` only once — on the original playback failure — a healthy lower-ranked source was never reached. It now continues through untried candidates. `switchTo` reports why it stopped so the loop can tell "could not resolve, try the next one" from "something newer owns the screen"; without that distinction a superseded switch would have spun forever, because only the former marks the candidate tried. **Identity ignored enrichment.** The key was `playlistId:contentId:title`, so when `get_vod_info` added a TMDB id and release year to an unchanged title the host saw no change, never reloaded, and kept yearless discovery and title-only pin keys — a `tmdb:`-keyed pin could never be found. The key now covers every field that affects matching. **A server refusing HEAD read as unavailable.** Some stream hosts answer 405 or 501 to HEAD yet serve the media over GET. The probe now retries once with the ranged GET the main process already supported, instead of caching a working source as failed and penalising it during failover. Greptile also flagged a missing token check after the resolve await in `switchTo`; that guard landed in |
||
|
|
9ae53e4515 |
fix(playback): make the "Show subtitles" setting reach the web players (#1269)
The persisted subtitle preference only ever had an owner behind the default-off shared web-controls flag, so with the shipping controls it did nothing: Video.js never read it, ArtPlayer declared the input but never used it, and the HTML5 player only ran a one-shot pass after play() resolved — before hls.js had added its text tracks. No portal host bound the input at all, so it never reached Xtream or Stalker pages either. Extract the source-local track controllers into the adapter-free WebVideoSourceTracks and have WebVideoSourceControlsBridge wrap it, so both controls modes apply the preference through the same code. The preference-off players bind it directly (VjsLegacyTracks for Video.js), and WebPlayerViewComponent reads the preference from SettingsStore instead of an input so every host inherits it. The preference means different things depending on who renders the caption UI: shared controls stay authoritative for the session, while vendor chrome is source-default — the preference seeds each new source and is released once the media reports playing, so the engine own caption menu keeps working. Mode selection is an optional playbackStarted probe passed to the HLS, native and Shaka helpers; in that mode the HLS helper deselects the track rather than hiding it, since subtitleDisplay would silently override the vendor menu. Closes #1155 |
||
|
|
bd07e17857 |
feat(m3u): DASH + ClearKey playback via Shaka Player (#1225)
* feat(m3u): extract ClearKey DRM from #KODIPROP playlist lines Adds the typed ChannelDrm model (shared interfaces) and a KODIPROP post-processing step in createPlaylistObject() — the single funnel for all four playlist import paths. Parses inputstream.adaptive.license_type, license_key and drm_legacy; ClearKey keys accepted as kid:key hex pairs, W3C ClearKey license JSON, or a plain kid→key JSON map. Unsupported license types (Widevine/PlayReady/license URLs) are preserved with supported=false so playback can surface a DRM diagnostic instead of failing silently. Also adds isDashStreamUrl/isDashChannel helpers for DASH routing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(playback): add Shaka DASH source engine with ClearKey support Introduces ShakaVideoSession (libs/ui/playback/src/lib/shaka-engine/): a lazily imported shaka-player engine (separate lazy chunk, ~217 KB transfer) owning attach/configure/load with an operation queue and generation guard against channel-switch races. Channel ClearKey config maps to drm.clearKeys; channels with an unsupported license type emit a DrmOrEncryption diagnostic without starting an engine. Shaka errors are classified into the existing playback diagnostics (PlaybackDiagnosticSource.Shaka). Wires the engine into both built-in players like hls.js/mpegts.js: - HTML5: extension === 'mpd' branch in playChannel(); hls/mpegts/native glue extracted to helpers to keep the component within the size budget - ArtPlayer: customType 'mpd' in ArtPlayerSourceSession (+ getDrm seam) - Shared controls: WebVideoControlsSource kind 'shaka' + WebVideoShakaControls using the Shaka 5 text model (selectTextTrack(null) hides subtitles; Player.setTextTrackVisibility no longer exists) Adds a CJS shaka-player jest stub (video.js precedent) for web specs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(m3u): route DASH channels to the inline Shaka-capable player DASH (.mpd) channels always play in a built-in web engine (radio precedent): external MPV/VLC cannot receive KODIPROP ClearKey configuration (VLC upstream #29465) and Video.js has no DASH bridge yet. - shouldShowInlinePlayer() bypasses the external-player setting for DASH - new shouldAutoLaunchExternalPlayer() guard consolidates the MPV/VLC auto-launch conditions in the m3u-state effects (incl. catch-up path) - the M3U page overrides the player for DASH channels: ArtPlayer stays ArtPlayer, everything else falls back to the HTML5 player - ChannelDrm is passed through ResolvedPortalPlayback into the synthetic player-view channel Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(e2e): add offline DASH ClearKey fixtures and e2e coverage Fixtures (apps/web-e2e/src/fixtures/dash/): ~4s VP9+Opus DASH, clear and CENC-encrypted variants with fixed synthetic ClearKey credentials. Content synthesized by ffmpeg; encryption done by Shaka Packager because ffmpeg's mp4 muxer writes senc-only metadata (Chromium needs saiz/saio) and cannot produce the subsample encryption the VP9 CENC binding requires. Generation script + README document regeneration. web-e2e (Chromium): import an M3U with KODIPROP ClearKey via raw text, verify encrypted and clear DASH actually play (currentTime advances, no diagnostic banner) and that an unsupported license type (Widevine) surfaces the DRM diagnostic. Fixtures are served through Playwright route interception with HTTP Range support; the Angular service worker is blocked since SW-routed requests bypass interception. electron-backend-e2e: the same happy path + negative against a local Range-aware fixture server — the automated proof that ClearKey EME works in the real Electron runtime (file:// secure context). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: document DASH + ClearKey playback architecture Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(pwa): extract KODIPROP DRM on the web-backend /parse import path The web-backend keeps its own playlist builder for the PWA URL-import path, so the shared createPlaylistObject() DRM hook never ran there and encrypted DASH channels imported by URL reached Shaka without keys. Apply extractDrmFromRaw() in that builder too and cover the path with a regression test. Addresses Codex review on PR #1225. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): interrupt stalled Shaka loads and destroy failed engines Two review findings on the ShakaVideoSession lifecycle: - stop()/start() now tear the current player down immediately instead of queueing the destroy behind the in-flight operation. Shaka's destroy() interrupts a pending load() (LOAD_INTERRUPTED), so a stalled manifest fetch can no longer wedge the operation chain and block the next channel start (Codex P1). - A rejected attach()/load() now destroys the failed player after emitting the diagnostic, so a non-functional engine never stays attached to the media element or exposed to the shared-controls bridge (Greptile P1). Regression tests cover both paths. The Shaka fakes are consolidated into a shared jest-free test double that mirrors the destroy-interrupts-load semantic, and the ArtPlayer source-session spec is split (fixtures + DASH cases) to stay within the max-lines lint budget. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(m3u): unify DASH URL detection with playback extension normalization isDashStreamUrl() used the simpler getStreamExtensionFromUrl(), so URLs the player engines classify as DASH (stream.MPD, ?ext=mpd, ?format=mpd) were not routed to the Shaka-capable inline player and lost their ClearKey metadata with Video.js or external players configured (Codex P2). The normalized getPlaybackMediaExtensionFromUrl() now lives in @iptvnator/shared/m3u-utils (re-exported unchanged from the playback lib) and both routing and engine selection share it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(lint): satisfy CI lint and CodeQL in DASH support files - replace shell-built tar/npm commands with execFileSync arg arrays in the fixture generator (CodeQL: uncontrolled shell command) - give jest stub methods explicit bodies (no-empty-function) - compact the diagnostic label switches in WebPlayerViewComponent to stay under the max-lines budget Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): tear down the Shaka engine on critical error events too A non-recoverable Shaka error emitted after a successful load left the dead engine attached to the media element and exposed to the shared-controls bridge (Greptile P1, round 2). Critical error events now destroy the player right after the diagnostic is emitted, matching the load-failure path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(m3u): honor DASH catch-up URLs and drop unusable DRM fallbacks Two Codex round-2 findings: - The inline-playback DASH gate only examined the channel URL, while the external-player guard checks the resolved catch-up URL — a replay that resolves to an .mpd manifest with MPV/VLC configured ended up with no player at all. The gate now uses the effective playback URL (activePlaybackUrl ?? channel.url). - The unsupported-DRM diagnostic advertised MPV/VLC fallback actions, but external players cannot receive the KODIPROP license config either — the diagnostic no longer recommends them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): suppress unusable external fallback for ClearKey DRM failures Runtime DRM errors on channels that carry KODIPROP ClearKey config (wrong or rotated keys) advertised MPV/VLC fallback actions, but external players never receive the license config — the fallback could only fail differently. DRM-classified diagnostics from such channels no longer recommend external players; clear channels keep the hint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(m3u): symmetric DASH inline gate and lazy DRM for pre-upgrade playlists - The inline DASH gate is now true when either the channel or the resolved catch-up URL is DASH, mirroring the external-player guard — a .mpd channel whose catch-up resolves to .m3u8 no longer ends up with no player at all. - Playlists imported before the DRM feature carry no drm field, but the raw KODIPROP block survived in the stored items; the M3U page now falls back to extractDrmFromRaw(channel.raw) at playback time, so encrypted channels work without a re-import (Channel gains raw?). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: sync the DASH/Shaka contract across agent docs Mirrors the DASH/Shaka source-engine contract into AGENTS.md and adds Shaka to the shared web-video bridge descriptions in CLAUDE.md and the player-controls contract; documents the lazy raw-KODIPROP DRM fallback for pre-upgrade playlists in the M3U architecture doc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): reset the media element for rejected DRM and widen ClearKey fallback suppression - Switching from a playing stream to an unsupported-DRM DASH channel loads no new source, but play() still ran and the un-loaded element could resume the previous stream underneath the diagnostic banner. The HTML5 player now resets the element instead of playing. - Any inline failure on a KODIPROP ClearKey channel (manifest, codec, media, network — not just DRM-category errors) is unsolvable in MPV/VLC, which never receive the license config; the external fallback hint is now suppressed for all diagnostics of such channels. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): restore suppressed DASH captions when the preference re-enables The Shaka bridge dropped the auto-selected text track with selectTextTrack(null) when showCaptions was off, but did not remember it — re-enabling the preference mid-session left captions permanently off (HLS/native bridges already restore). The session now remembers the suppressed track id and reselects it via the bridge's caption-state pass; suppression is also skipped when no track is active. Covered by session and new WebVideoShakaControls specs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: retrigger CI GitHub Actions created no check suites for the last three pushes to this branch (third-party apps received the webhooks); an empty commit re-fires the push and pull_request events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(playback): split oversized Shaka session and HTML5 spec files CI lint enforces max-lines 400: extract ShakaTextTrackSuppression and the shaka-error helpers out of ShakaVideoSession, and move the DASH-specific HTML5 player test into its own spec. No behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: allow manual dispatch of the cross-platform E2E workflow GitHub stopped delivering push/pull_request events for this branch; workflow_dispatch provides a manual escape hatch (CI and build-and-make already have one). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6dc8a10d52 |
feat(playback): show media title overlay in fullscreen shared controls (#1228)
* feat(playback): show media title overlay in fullscreen shared controls In fullscreen with the shared player-controls layer, a pointer-transparent overlay at the top of the player now names the content while controls are revealed: one line for movies and live channels, two lines for series (series name + S01E03 label). The overlay follows the bar's auto-hide transition and stays hidden outside fullscreen, where page chrome already names the content. Data flows top-down: the Xtream/Stalker series detail views pass the series name via a new PortalInlinePlayerComponent.seriesTitle input (Xtream episode playback titles carry the episode name, not the series), the inline player builds the two-line form from the episode metadata label, and WebPlayerViewComponent falls back to playback.title for movies/channels while skipping raw stream-URL fallbacks. All four shared-controls hosts (HTML5, Video.js, ArtPlayer, Embedded MPV frame-copy) forward the value. To stay under the max-lines limit, scrub/timeline state is extracted into ControlsTimeline and the playback-diagnostics display helpers into web-player-view-diagnostics.utils.ts, with behavior unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: mirror fullscreen media-title contract into AGENTS.md Addresses Codex review: the Shared Player Controls section in AGENTS.md must stay in sync with the CLAUDE.md description of the new mediaTitle input, fullscreen overlay behavior, and series-title wiring. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
b1fc23abbb |
fix(playback): route MKV sources as Matroska (#1210)
* docs(playback): design native MKV source routing * fix(playback): route MKV sources as Matroska * chore(playback): address MKV review feedback |
||
|
|
beb62db314 |
feat(settings): add shared web player controls toggle (#1198)
* docs(playback): design shared controls setting * docs(playback): plan shared controls setting * feat(settings): persist shared web controls preference * test(settings): harden shared controls normalization coverage * feat(settings): expose shared web controls toggle * fix(settings): label shared controls toggle * feat(playback): resolve shared controls from settings * test(playback): cover shared controls setting * docs(playback): document shared controls preference * fix(playback): await settings before host creation * fix(settings): normalize shared controls updates |
||
|
|
c49ea2f6a8 |
feat(html-player): add feature-flagged shared controls (#1194)
* feat(html-player): bridge engine state to shared controls * fix(html-player): avoid HLS subtitle event reentry * fix(html-player): restore delayed HLS default subtitles * refactor(html-player): split controls bridge collaborators * feat(html-player): add feature-flagged shared controls * test(html-player): cover shared-controls source ownership * feat(html-player): pass shared-controls playback metadata * docs(player-controls): describe HTML5 shared-controls bridge * docs(player-controls): clarify HTML5 rollout effect * fix(html-player): reveal diagnostics from fullscreen * fix(html-player): defer HLS event resolution * refactor(html-player): isolate video element session |
||
|
|
8517c7a8f7 |
fix(playback): suppress diagnostics for embedded mpv (#1038)
* fix(playback): suppress diagnostics for embedded mpv * chore(playback): clarify selected player effect dependency |
||
|
|
77cf4065e0 |
feat(playback): add embedded mpv series navigation (#1030)
* fix(remote-control): use iptvnator favicon * feat(playback): add embedded mpv series navigation * refactor(playback): share series navigation state |
||
|
|
3d4d2ca9bf |
fix(angular): remove template diagnostics warnings (#1011)
* fix(angular): remove template diagnostics warnings * fix(angular): preserve template fallback behavior |
||
|
|
5827ef05f0 | refactor(playback): use runtime capability for external fallback | ||
|
|
36bce47764 |
merge: resolve master conflicts for pwa hardening
- merge origin/master into PR #964 and keep embedded MPV test on the isolated playback sub-entrypoint - centralize EPG capability through DataService.supportsEpg and update PWA web-e2e expectations - split BrowserAccessError copy between Electron and PWA diagnostics |
||
|
|
118d4fce32 |
fix(playback): improve inline stream diagnostics (#952)
* fix(playback): improve inline stream diagnostics * fix(playback): preserve headers in diagnostics wrapper * fix(portal): align unified live player imports |
||
|
|
d24c77a143 | chore(nx): enforce scoped workspace boundaries (#942) | ||
|
|
8f79b25f52 | fix(playback): treat portal mpeg-ts vod as non-live | ||
|
|
4a4334cb64 |
feat(playback): improve browser-player diagnostics (#939)
* feat(playback): diagnose browser stream access errors Entire-Checkpoint: f957cd9849e0 * feat(playback): expose browser-player diagnostic details Entire-Checkpoint: f957cd9849e0 * fix(playback): keep media path extensions authoritative Entire-Checkpoint: f957cd9849e0 * fix(playback): tighten browser access diagnostics Entire-Checkpoint: f957cd9849e0 * fix(playback): align diagnostic followups Entire-Checkpoint: f957cd9849e0 * fix(playback): narrow declared stream metadata Entire-Checkpoint: f957cd9849e0 * fix(playback): preserve explicit extension overrides Entire-Checkpoint: f957cd9849e0 * fix(playback): keep mpeg-ts out of unsupported containers Entire-Checkpoint: f957cd9849e0 * refactor(playback): split diagnostics utilities Entire-Checkpoint: f957cd9849e0 * fix(playback): treat mpegts early eof as media fallback Entire-Checkpoint: f957cd9849e0 |
||
|
|
db08ec2958 |
fix(playback): keep diagnostics within player viewport
Entire-Checkpoint: f957cd9849e0 |
||
|
|
2f9aef1578 |
fix(playback): handle IPTV proxy URLs without file extensions (#934)
* fix(playback): handle IPTV proxy URLs without file extensions IPTV proxy servers (Acexy, Xtream Codes, etc.) use query-string URLs like `https://proxy.example.com/ace/getstream?infohash=abc` that return raw MPEG-TS streams without a file extension in the path. `getExtensionFromUrl()` was returning garbage for these URLs (e.g. `com/ace/getstream` from splitting on dots across path segments). All four player backends then misrouted the stream — typically to HLS.js which failed because the response is raw MPEG-TS, not an HLS manifest. Root fix: rewrite `getExtensionFromUrl()` to extract the extension from the last path segment only, returning `undefined` when there is no dot-separated extension. Player adjustments: treat `undefined` extension as MPEG-TS (the most common format for live IPTV proxy streams) across all four backends (HTML5, ArtPlayer, VideoJs, web-player-view). * test(m3u-utils): cover URL extension parsing * fix(playback): preserve query-declared stream extensions --------- Co-authored-by: 4gray <serega05@gmail.com> |
||
|
|
6305dcf00e |
Add browser playback diagnostics and MPV/VLC fallback (#921)
* feat(playback): add codec diagnostics fallback Entire-Checkpoint: f957cd9849e0 * fix(playback): address diagnostics review feedback Entire-Checkpoint: f957cd9849e0 * fix(playback): ignore recoverable hls diagnostics Entire-Checkpoint: f957cd9849e0 * fix(playback): polish diagnostic fallback banner Entire-Checkpoint: f957cd9849e0 * fix(xtream): preserve live category search scope Entire-Checkpoint: f957cd9849e0 |
||
|
|
9f873e6bed |
feat(player): add embedded-mpv player for macOS as experimental feature
- Introduced tooling for building and staging the macOS `libmpv` runtime for IPTVnator's embedded MPV player. - Added `build-macos-runtime.mjs` for building an LGPL-compatible runtime from source. - Created `stage-macos-runtime.mjs` for staging the built runtime artifacts. - Implemented validation for the packaged embedded MPV runtime in `electron-after-pack.cjs` and `embedded-mpv-macos.cjs`. - Updated packaging scripts to ensure the embedded MPV runtime is correctly integrated and validated during the build process. - Added README files to document the expected layout and usage for the embedded MPV runtime artifacts. Entire-Checkpoint: c6e522b4276c |
||
|
|
7ac2fd66b8 | refactor: update nx monorepo structure |