diff --git a/.changes/README.md b/.changes/README.md index 318217075..ac69427f5 100644 --- a/.changes/README.md +++ b/.changes/README.md @@ -55,8 +55,10 @@ The body is capped at 400 characters — depth belongs in the blog post. gives it a short, poster-worthy name. The 60-character cap keeps it roughly to one line on the hero card; card text wraps by estimated width, so a headline of unusually wide glyphs may still wrap or ellipsize rather than overflow. Highlights lead the Telegram/Reddit -announcements (everything else collapses into a "+N more" counter) and become -ready-made section headings in the blog scaffold. Set it on the two or three +announcements (everything else collapses into a "+N more" counter) and open the +blog scaffold: a row in its "What changed" table and a `##` section ahead of +the themed sections, while fixes without a highlight collapse under a spoiler +there. Set it on the two or three changes worth announcing — a release where everything is a highlight has none. Not allowed on `type: internal`. diff --git a/.changes/dashboard-live-epg-timestamp-units.md b/.changes/dashboard-live-epg-timestamp-units.md new file mode 100644 index 000000000..a5b267810 --- /dev/null +++ b/.changes/dashboard-live-epg-timestamp-units.md @@ -0,0 +1,9 @@ +--- +type: internal +area: dashboard +--- + +The dashboard's live "now playing" time range and progress bar now read +pre-computed EPG timestamps as unix seconds, matching the rest of the EPG +code. Today's dashboard lookups never carry those fields, so nothing changes +on screen; this closes the gap before a future data path supplies them. diff --git a/.changes/database-catalog-write-batches.md b/.changes/database-catalog-write-batches.md new file mode 100644 index 000000000..ce695f522 --- /dev/null +++ b/.changes/database-catalog-write-batches.md @@ -0,0 +1,10 @@ +--- +type: perf +area: database +issues: [1292] +--- + +Refreshing or removing a large Xtream playlist is several times faster: the +"Removing cached content" stage and the re-import now commit around 5,000 +rows at a time instead of 100, and progress updates arrive at most ten times a +second instead of once per batch. diff --git a/.changes/deps-transitive-cve-overrides.md b/.changes/deps-transitive-cve-overrides.md new file mode 100644 index 000000000..4a1234242 --- /dev/null +++ b/.changes/deps-transitive-cve-overrides.md @@ -0,0 +1,10 @@ +--- +type: internal +area: deps +--- + +Closes the four open Dependabot alerts on transitive npm dependencies via +pinned pnpm overrides: browserslist (crash on untrusted stats), @xmldom/xmldom +(XML fragment injection — its existing override target had itself fallen into +the advisory range), @humanfs/node (recursive copy follows symlinks out of the +tree) and postcss-selector-parser (AST recursion DoS). diff --git a/.changes/embedded-mpv-extra-options-reconnect.md b/.changes/embedded-mpv-extra-options-reconnect.md new file mode 100644 index 000000000..8e5d60152 --- /dev/null +++ b/.changes/embedded-mpv-extra-options-reconnect.md @@ -0,0 +1,11 @@ +--- +type: feature +area: embedded-mpv +highlight: Embedded MPV reconnects dropped streams +--- + +The embedded MPV player now reloads a live stream that drops mid-playback on +its own, with increasing delays and an "attempt N of 6" line instead of a +dead error screen; a new Settings > Playback toggle turns this off. The same +section gains an advanced field for extra libmpv options (one key=value per +line) that applies on every engine, with a short network timeout on by default. diff --git a/.changes/embedded-mpv-no-ytdl-hook.md b/.changes/embedded-mpv-no-ytdl-hook.md new file mode 100644 index 000000000..273e28c7d --- /dev/null +++ b/.changes/embedded-mpv-no-ytdl-hook.md @@ -0,0 +1,10 @@ +--- +type: fix +area: embedded-mpv +--- + +When a stream refuses the connection, the embedded MPV player (frame-copy +engine) no longer hands the URL to yt-dlp before giving up: the failure +shows up right away and its error no longer reads "youtube-dl failed: +unexpected error occurred", matching the native-view engines and the +external MPV player. diff --git a/.changes/epg-display-offset.md b/.changes/epg-display-offset.md new file mode 100644 index 000000000..f90a8987a --- /dev/null +++ b/.changes/epg-display-offset.md @@ -0,0 +1,7 @@ +--- +type: feature +area: epg +issues: [50] +--- + +EPG settings now include a display-only time offset, making it easy to correct provider feeds with missing or incorrect timezone information without re-importing the guide. diff --git a/.changes/epg-removed-source-cache.md b/.changes/epg-removed-source-cache.md new file mode 100644 index 000000000..b2f7bd281 --- /dev/null +++ b/.changes/epg-removed-source-cache.md @@ -0,0 +1,6 @@ +--- +type: fix +area: epg +--- + +Removing and saving an EPG source now clears its cached programmes, including data left by previously removed sources on restart. Other configured sources and playlist guides are preserved, and Live TV stops showing programmes from the removed source. diff --git a/.changes/host-health-live-trial.md b/.changes/host-health-live-trial.md new file mode 100644 index 000000000..482435dff --- /dev/null +++ b/.changes/host-health-live-trial.md @@ -0,0 +1,7 @@ +--- +type: fix +area: host-health +issues: [1439] +--- + +Portal recovery now waits for an active probe to finish before sending another request, even when redirects or a slow response take longer than 45 seconds. Completed and cancelled requests release the probe slot reliably. diff --git a/.changes/m3u-url-user-agent.md b/.changes/m3u-url-user-agent.md new file mode 100644 index 000000000..bcc5e04b1 --- /dev/null +++ b/.changes/m3u-url-user-agent.md @@ -0,0 +1,7 @@ +--- +type: fix +area: m3u +issues: [465, 1120] +--- + +M3U URL imports now accept a custom User-Agent for providers that require it before downloading the playlist. The value is saved and reused for playlist refreshes in the desktop app and self-hosted web app. diff --git a/.changes/migration-legacy-electron-profiles.md b/.changes/migration-legacy-electron-profiles.md new file mode 100644 index 000000000..d8d3b00dc --- /dev/null +++ b/.changes/migration-legacy-electron-profiles.md @@ -0,0 +1,7 @@ +--- +type: fix +area: migration +issues: [1504] +--- + +Upgrades from older desktop versions retain all sources from the legacy profile. Already-upgraded users can choose to recover missing sources without replacing current sources or settings. Migration keeps the original data and retries safely after a failed write. diff --git a/.changes/playback-controls-hide-after-button-click.md b/.changes/playback-controls-hide-after-button-click.md new file mode 100644 index 000000000..39a6cc15b --- /dev/null +++ b/.changes/playback-controls-hide-after-button-click.md @@ -0,0 +1,10 @@ +--- +type: fix +area: playback +--- + +The built-in player controls now hide on their own after you click a button +in the bar with the mouse, such as fullscreen or mute. Previously the clicked +button kept the bar pinned on screen until you clicked the video, which also +paused it. Applies to the shared controls in the web players and the +Embedded MPV frame-copy engine. diff --git a/.changes/playback-embedded-mpv-seek-steps.md b/.changes/playback-embedded-mpv-seek-steps.md new file mode 100644 index 000000000..d8dd4bb7e --- /dev/null +++ b/.changes/playback-embedded-mpv-seek-steps.md @@ -0,0 +1,10 @@ +--- +type: fix +area: playback +--- + +Arrow keys and the ±10 s buttons in the Embedded MPV player now move by their +full step every time. Pressing an arrow repeatedly, or holding it, used to +advance only about a second per press because each step was computed from a +stale position; steps are now relative seeks executed by mpv itself, so rapid +presses add up. diff --git a/.changes/playback-fullscreen-channel-panel.md b/.changes/playback-fullscreen-channel-panel.md new file mode 100644 index 000000000..4ab8066fc --- /dev/null +++ b/.changes/playback-fullscreen-channel-panel.md @@ -0,0 +1,12 @@ +--- +type: feature +area: playback +issues: [48] +highlight: Channel list in fullscreen +--- + +Rest the mouse on the left edge (tap it on touch) or press C while a live +channel plays fullscreen: the channel list (M3U all/groups/favorites/recent, +Xtream, Stalker, global favorites) slides over the video with search, so you +can zap without leaving fullscreen. Nothing covers the video while it is +closed. M3U playlists also zap with PageUp/PageDown. diff --git a/.changes/playback-legacy-pip-teardown.md b/.changes/playback-legacy-pip-teardown.md new file mode 100644 index 000000000..a0ef28472 --- /dev/null +++ b/.changes/playback-legacy-pip-teardown.md @@ -0,0 +1,6 @@ +--- +type: fix +area: playback +--- + +Switching channels or leaving playback now closes the old picture-in-picture window in HTML5, Video.js, and ArtPlayer even when shared player controls are disabled, preventing frozen or outdated video from remaining on top. This includes Safari’s legacy picture-in-picture mode. diff --git a/.changes/playback-light-theme-panels.md b/.changes/playback-light-theme-panels.md new file mode 100644 index 000000000..908c75598 --- /dev/null +++ b/.changes/playback-light-theme-panels.md @@ -0,0 +1,7 @@ +--- +type: fix +area: playback +issues: [1530] +--- + +Embedded MPV controls and programme-guide panels now follow the visual theme and stay readable in light and dark mode. Video overlays retain their contrasting dark backgrounds. diff --git a/.changes/playback-native-container-routing.md b/.changes/playback-native-container-routing.md new file mode 100644 index 000000000..a70f3312a --- /dev/null +++ b/.changes/playback-native-container-routing.md @@ -0,0 +1,9 @@ +--- +type: fix +area: playback +--- + +The built-in HTML5 player now plays `.mkv`, `.webm`, `.avi`, `.mov` and other +non-HLS video files directly instead of handing them to the HLS engine, which +failed with a "network or provider loading error" on many Xtream episodes and +movies. ArtPlayer and the HTML5 player now choose their engine by the same rule. diff --git a/.changes/playback-shortcuts-after-button-click.md b/.changes/playback-shortcuts-after-button-click.md new file mode 100644 index 000000000..0067f1a7a --- /dev/null +++ b/.changes/playback-shortcuts-after-button-click.md @@ -0,0 +1,10 @@ +--- +type: fix +area: playback +--- + +Keyboard shortcuts work again right after you click a button in the built-in +player controls. Previously the clicked button kept the keyboard: after +clicking fullscreen, Space left fullscreen instead of pausing, and the seek, +volume and mute keys did nothing until you clicked the video. Applies to the +shared controls in the web players and the Embedded MPV frame-copy engine. diff --git a/.changes/playback-videojs-shortcuts-after-button-click.md b/.changes/playback-videojs-shortcuts-after-button-click.md new file mode 100644 index 000000000..6a9e38625 --- /dev/null +++ b/.changes/playback-videojs-shortcuts-after-button-click.md @@ -0,0 +1,10 @@ +--- +type: fix +area: playback +--- + +With IPTVnator's shared controls turned off, the Video.js player's keyboard +shortcuts now work again right after you click a button in its control bar. +Previously the clicked button kept the keyboard: after clicking fullscreen, +Space left fullscreen instead of pausing, and the seek, volume and mute keys +did nothing until you clicked the video. diff --git a/.changes/playback-vlc-windows-console.md b/.changes/playback-vlc-windows-console.md new file mode 100644 index 000000000..c29a9b86a --- /dev/null +++ b/.changes/playback-vlc-windows-console.md @@ -0,0 +1,7 @@ +--- +type: fix +area: playback +issues: [871] +--- + +VLC now opens without an extra console window on Windows when playback progress tracking or Reuse VLC instance is enabled. diff --git a/.changes/portals-concurrent-connectivity-failures.md b/.changes/portals-concurrent-connectivity-failures.md new file mode 100644 index 000000000..8b4324169 --- /dev/null +++ b/.changes/portals-concurrent-connectivity-failures.md @@ -0,0 +1,7 @@ +--- +type: fix +area: portals +issues: [1438] +--- + +Xtream and Stalker portals no longer enter a connection cooldown merely because several parallel requests fail in the same millisecond. This fixes false unavailability in both the desktop app and the self-hosted web version. diff --git a/.changes/portals-connectivity-setting.md b/.changes/portals-connectivity-setting.md new file mode 100644 index 000000000..65acde25c --- /dev/null +++ b/.changes/portals-connectivity-setting.md @@ -0,0 +1,6 @@ +--- +type: feature +area: portals +--- + +Desktop settings now let you disable the temporary pause after repeated Xtream or Stalker connection failures, without restarting. Account info explains when requests are paused and offers an immediate retry. diff --git a/.changes/shell-startup-window-mode.md b/.changes/shell-startup-window-mode.md new file mode 100644 index 000000000..fbe45baa0 --- /dev/null +++ b/.changes/shell-startup-window-mode.md @@ -0,0 +1,10 @@ +--- +type: feature +area: shell +issues: [1455] +--- + +Settings → General has a new "Window on startup" option: open the desktop app +at its last size, maximized, or fullscreen — handy on a TV or HTPC. Starting +with `--fullscreen` forces a single fullscreen launch without changing the +setting, and F11 now toggles fullscreen anywhere in the app. diff --git a/.changes/stalker-keep-live-playback-on-category-switch.md b/.changes/stalker-keep-live-playback-on-category-switch.md new file mode 100644 index 000000000..a6ae14ae9 --- /dev/null +++ b/.changes/stalker-keep-live-playback-on-category-switch.md @@ -0,0 +1,9 @@ +--- +type: fix +area: stalker +--- + +In Stalker portals, switching the Live TV or radio category in the sidebar no +longer stops the channel that is playing. The category only changes which +channels are listed, matching how Xtream portals and M3U playlists already +behave. diff --git a/.changes/stalker-season-marker-display.md b/.changes/stalker-season-marker-display.md new file mode 100644 index 000000000..0b0a1f4ce --- /dev/null +++ b/.changes/stalker-season-marker-display.md @@ -0,0 +1,6 @@ +--- +type: fix +area: stalker +--- + +Stalker series with season markers such as “s02” or “2 сезон” now show the correct season in tabs and episode labels while preserving watch progress. TMDB updates no longer reset loaded episodes, and delayed portal responses cannot mix episodes from different series. diff --git a/.changes/stalker-seek-byte-ranges.md b/.changes/stalker-seek-byte-ranges.md new file mode 100644 index 000000000..f95b140ee --- /dev/null +++ b/.changes/stalker-seek-byte-ranges.md @@ -0,0 +1,6 @@ +--- +type: fix +area: stalker +--- + +Seeking in Stalker movies and series now reaches the selected position instead of jumping forward or skipping to the next episode after resuming playback. diff --git a/.changes/ui-keyboard-scroll.md b/.changes/ui-keyboard-scroll.md new file mode 100644 index 000000000..2a5ca3bf1 --- /dev/null +++ b/.changes/ui-keyboard-scroll.md @@ -0,0 +1,7 @@ +--- +type: fix +area: ui +issues: [1506] +--- + +Live TV now supports keyboard movement between categories and channels, and channel lists keep scrolling after mouse selection. Channel scrollbars and resize handles are independently accessible. Movie and series details support immediate keyboard scrolling and no longer hide their scrollbars. diff --git a/.changes/ui-light-detail-surfaces.md b/.changes/ui-light-detail-surfaces.md new file mode 100644 index 000000000..4602cd79a --- /dev/null +++ b/.changes/ui-light-detail-surfaces.md @@ -0,0 +1,6 @@ +--- +type: fix +area: ui +--- + +Detail buttons, episode cards and episode rows now keep visible backgrounds and borders in the light theme. The grid/list switch also clearly highlights the selected view in both themes. diff --git a/.changes/xtream-filtered-category-selection.md b/.changes/xtream-filtered-category-selection.md new file mode 100644 index 000000000..0cfa027ac --- /dev/null +++ b/.changes/xtream-filtered-category-selection.md @@ -0,0 +1,7 @@ +--- +type: fix +area: xtream +issues: [812, 816] +--- + +Xtream category management now selects or deselects only matching categories while searching, preserving the visibility of all other categories. Button labels and availability reflect the search results, and the counter clearly shows the total selected. diff --git a/.changes/xtream-sync-overlay-contrast.md b/.changes/xtream-sync-overlay-contrast.md new file mode 100644 index 000000000..52d3a31ad --- /dev/null +++ b/.changes/xtream-sync-overlay-contrast.md @@ -0,0 +1,6 @@ +--- +type: fix +area: xtream +--- + +The Xtream sync panel now keeps its status text, labels and Stop sync button clearly readable in both light and dark themes. diff --git a/.codex/skills/iptvnator-theme-style/SKILL.md b/.codex/skills/iptvnator-theme-style/SKILL.md index d986bcc45..1f4e62418 100644 --- a/.codex/skills/iptvnator-theme-style/SKILL.md +++ b/.codex/skills/iptvnator-theme-style/SKILL.md @@ -40,9 +40,9 @@ consumers currently use relative `@use` paths to the needed partial. of copied SCSS. - In an Electron drag region, every interactive descendant—buttons, links, inputs, overlays, and resize handles—must explicitly use - `app-region: no-drag`. The shared directive-generated `.resize-handle` does - not set this centrally yet; consumers in drag regions must cover it - themselves and must not assume the generated handle opts out. + `app-region: no-drag`. The shared directive-generated `.resize-handle` sets + this centrally. Shared live sidebars reserve 8 px between their scroll + content and the edge so scrollbar and resize hit areas stay independent. - A shared change must be checked across M3U, Xtream, Stalker, workspace, portal catalog/shared UI, and unified collections where relevant. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3eeb77cf5..5b0434924 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,6 +153,9 @@ jobs: - name: Validate Vite dev-server transform filter patch run: pnpm run deps:vite:test + - name: Validate electron-builder keychain password patch + run: pnpm run deps:electron-builder:test + - name: Validate stylesheet Nx inputs run: pnpm run styles:inputs:validate diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index a730497af..f7e1b2c52 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -40,6 +40,9 @@ jobs: - name: Build website run: pnpm nx build website + env: + # Read by apps/website/src/lib/downloads.ts to resolve the latest published release. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Pages artifact uses: actions/upload-pages-artifact@v5 diff --git a/AGENTS.md b/AGENTS.md index 09fa0e868..4af88313b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,6 +24,17 @@ This file provides guidance to coding agents working in this repository. `patches/vite@7.3.6.patch`. Keep the patch until supported Angular tooling resolves a Vite version containing the fix, and run `pnpm run deps:vite:test` after related dependency updates. +- `app-builder-lib` `26.15.7` (electron-builder's macOS signing) is patched in + `patches/app-builder-lib@26.15.7.patch` with the upstream backport + electron-userland/electron-builder#10172: `security set-key-partition-list -k` + must receive the temporary keychain's own password, not the `.p12` import + password. macOS runner images since `macos-26-arm64` 20260831 verify that + password, and `Build on macos arm64` failed with `SecKeychainUnlock: The user +name or passphrase you entered is not correct`. Keep the patch until + electron-builder resolves an `app-builder-lib` containing the fix (26.16.1+), + and run `pnpm run deps:electron-builder:test` after related dependency + updates — the test fails when the patched version no longer matches the + installed one. - A directory holding files consumed by other projects must be an Nx project. Nx builds its graph from TypeScript imports only, so a relative SCSS `@use` across project roots creates no edge and the imported file lands in no task @@ -91,6 +102,17 @@ This file provides guidance to coding agents working in this repository. - Electron-specific changes affecting IPC, SQLite, packaged runtime, external players, native file access, or Electron-only routes require Electron E2E coverage where available, or CDP/manual verification with `agent-browser` and the tracing flags documented below. - Final task summaries must list tests added or updated, validation commands run with results, and any skipped validation with the reason. For docs-only changes, state that unit/E2E validation was not required and verify the changed Markdown instead. +## Legacy Desktop Profile Migration + +`electron-profile-bootstrap.ts` selects the known v0.19 `electron-backend` +profile before eager main-process imports only when current Chromium storage +is unused. Existing profiles retain their settings and offer explicit recovery +of missing sources from a disposable legacy snapshot. Playlist rows and a +completion receipt commit atomically in the DB worker; original IndexedDB is +retained, current payload rows are preserved, and completed imports never +replay deleted sources. Contract and recovery limits: +`docs/architecture/m3u-playlist-module.md` (Desktop upgrades from legacy profiles). + ## Electron Debugging (CDP) - Start the Electron development app with: `nx serve electron-backend` @@ -155,6 +177,68 @@ agent-browser connect ws://127.0.0.1:9222/devtools/page/ agent-browser screenshot /tmp/iptvnator-cdp.png ``` +## Xtream Category Management + +The Electron Live TV, Movies, and Series category dialog applies Select/Deselect +to search results while a filter is active and to the whole type otherwise. +Button states use the matching group; "Total selected" counts the whole catalog. +Save persists the complete draft, Close discards it, and refresh restores hidden +categories by provider ID and type. See `docs/architecture/category-management.md`. + +## XMLTV Source Removal + +Saving Settings → EPG reconciles cached XMLTV with committed global URLs and +all enabled M3U playlist sources. Startup runs the same reconciliation after +settings load and playlist migration. Ordinary saves skip unchanged normalized +source sets; an explicitly edited EPG field can retry a failed cleanup. +A cleanup failure after persistence still mirrors committed settings to Electron; +the form stays dirty for retry. Failed storage writes never mirror to main. +Failed settings reads and incomplete playlist migration never authorize pruning. +Removed sources retire queued/running imports and dismiss retained error rows +before worker-owned deletion. Retry waits for reconciliation and rechecks its +error row, including after trust-setting writes. Shared channel IDs survive while +another source has programmes or per-source channel metadata. The additive +`epg_channel_sources` table preserves each imported source's name, logo, URL and +timestamp plus transaction-ordered `write_order`, so removal restores the latest +surviving snapshot even when import timestamps tie; ambiguous legacy metadata +falls back to the XMLTV ID until reimport. Manual mappings remain user preferences, but no +longer resolve deleted data. Renderer lookup generations, Xtream previews and Stalker mapping-cache +invalidation prevent late results from restoring removed programmes. Provider +EPG is independent. See `docs/architecture/m3u-playlist-module.md` +("XMLTV source lifecycle"). + +## Portal Connectivity Preference + +- Half-open trial slots follow the complete request lifetime with no elapsed-time + expiry. All four Electron/web-backend portal handlers release in `finally`, + independently of outcome reporting; cleanup preserves trial/epoch ownership + and works while the environment override is disabled. Contract: + `docs/architecture/host-connectivity-guard.md` ("Trial ownership follows the + request lifetime"). +- Desktop Settings > General > Portal connections exposes default-on + `Settings.portalConnectivityGuard`. Only explicit false opts out. Save mirrors + the value to Electron `PORTAL_CONNECTIVITY_GUARD` and applies it without restart; + settings bootstrap restores it before the renderer loads. It controls Xtream + and Stalker together. Preference transitions clear cooldowns and invalidate old + request completions; unchanged saves preserve evidence. The environment switch + `IPTVNATOR_DISABLE_CONNECTIVITY_GUARD=1` remains authoritative. PWA clients do not + control the shared backend's guard. +- Both account-info dialogs explain guard refusals with localized paused-request + copy and Retry now; Stalker preserves cached account data on a failed refresh. + Contract: `docs/architecture/host-connectivity-guard.md`. + +## Channel and Detail Keyboard Scrolling + +Channel scroll owners use `ChannelScrollFocusDirective`; pointer selection +focuses the viewport, native scrolling survives virtual row recycling, and +row Enter/Space activation stays separate from focus movement. Portal Live TV +uses ArrowRight from the selected category and ArrowLeft from the channels +pane to move between columns. Shared live sidebars reserve scrollbar space +beside the resize handle. `PortalDetailShellComponent` owns a visible native +scrollbar and guarded initial page focus. Contracts: +`docs/architecture/iptvnator-ui-guidelines.md` and +`docs/architecture/portal-detail-navigation.md`. + ## Radio / Audio Player M3U playlists can contain radio channels identified by the `radio="true"` attribute on `#EXTINF` lines. When a radio channel is selected: @@ -174,8 +258,26 @@ Key files: - `libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html` — template conditionals for radio vs video - `libs/shared/interfaces/src/lib/channel.interface.ts` — `radio: string` field on Channel interface +## M3U URL User-Agent + +- The URL import form accepts an optional User-Agent and stores it as + `Playlist.userAgent`. Electron sends it on initial download, manual refresh, + and startup auto-update. The self-hosted PWA sends it through the registered + target `/parse` backend proxy for import and refresh; a matching backend is + required, and browser playback-header restrictions still apply. +- Reuse the existing source editor and channel-over-playlist playback header + precedence. Contract: `docs/architecture/m3u-playlist-module.md` + ("User-Agent for URL sources"). + ## Shared Player Controls +- The Embedded MPV native-view dock follows app theme tokens as a solid app + surface, including Material icon-button disabled states. Over-video loading, + stalled and feedback overlays keep a paired light-on-dark palette. Video + viewports remain black in windowed and fullscreen modes. Shared EPG panels + use the library-local app-token palette in `libs/ui/epg/src/lib/_epg-theme.scss`. + Theme/contrast contract: `docs/architecture/iptvnator-ui-guidelines.md`. + - `libs/ui/playback/src/lib/player-controls/` contains the additive, engine-neutral `PlayerController` contract, standalone `app-player-controls`, generic web-video adapter/helper, and component-scoped @@ -203,6 +305,29 @@ Key files: `PortalInlinePlayerComponent.seriesTitle` and `WebPlayerViewComponent.mediaTitle`; movie and live hosts fall back to `playback.title`, skipping raw stream-URL fallbacks. Outside fullscreen the overlay stays hidden. +- Auto-hide pauses while the pointer is over the controls bar or keyboard + focus is inside it, but only keyboard-originated focus pins the bar open. + Chromium also focuses a clicked ` - - )) - } - +
+ + {row.label} + +
+ + + Download for {row.label} + + + )) + } + - -
-

- Prefer the browser version?{' '} - - Self-host it with Docker - -

+ +
+ { + packageManagers.map((pm) => ( +
+ {pm.label} + {pm.command} + +
+ )) + } +

+ Prefer the browser? Self-host the web app and open it as a PWA on any device. +

+
diff --git a/apps/website/src/components/FeaturedBlogPost.astro b/apps/website/src/components/FeaturedBlogPost.astro index 56f5b1046..a1c1c7836 100644 --- a/apps/website/src/components/FeaturedBlogPost.astro +++ b/apps/website/src/components/FeaturedBlogPost.astro @@ -1,10 +1,13 @@ --- +import BlogTagChip from './BlogTagChip.astro'; +import type { BlogTag } from '../lib/blog-tags'; + interface Props { title: string; description: string; pubDate: Date; slug: string; - tags?: string[]; + tags?: BlogTag[]; heroImage?: string; } @@ -17,10 +20,8 @@ const formattedDate = pubDate.toLocaleDateString('en-US', { }); --- - +{/* Same stretched-link pattern as BlogPostCard: the tag chips are links of their own. */} + diff --git a/apps/website/src/components/Footer.astro b/apps/website/src/components/Footer.astro index 19a240698..e7edb422e 100644 --- a/apps/website/src/components/Footer.astro +++ b/apps/website/src/components/Footer.astro @@ -1,5 +1,6 @@ --- -import footerWatermark from '../assets/footer-watermark.png'; +import { Image } from 'astro:assets'; +import mascotImg from '../assets/illustrations/iptvnator-1-mascot.png'; const socialLinks = [ { @@ -23,92 +24,73 @@ const socialLinks = [ icon: 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z', }, ]; + +const footerLinks = [ + { label: 'Features', href: '/iptvnator/features/' }, + { label: 'Compare', href: '/iptvnator/compare/' }, + { label: 'Download', href: '/iptvnator/download/' }, + { label: 'Blog', href: '/iptvnator/blog/' }, + { label: 'Unofficial sites', href: '/iptvnator/blog/beware-unofficial-iptvnator-websites/' }, +]; --- -