Commit Graph
97 Commits
Author SHA1 Message Date
4gray b6c5367d63 fix(dashboard): speed up startup rails 2026-06-20 21:46:42 +02:00
4gray 254879f92b fix(xtream): improve portal compatibility 2026-06-14 13:21:56 +02:00
4gray adf37e7504 feat(dashboard): add configurable dashboard rails
* feat(dashboard): add configurable dashboard rails

* fix(dashboard): address rail review feedback
2026-06-14 11:52:44 +02:00
4grayand4gray e8aa7c3a34 [codex] Add scoped EPG security trust controls (#1054)
* Add scoped EPG security trust controls

* fix: address scoped trust review feedback

---------

Co-authored-by: 4gray <fourgray@proton.me>
2026-06-12 20:46:24 +02:00
4grayandClaude Fable 5 8e0abe6feb feat(ui): custom title bar with window controls for Windows and Linux (#1042)
* feat(ui): add custom title bar window controls for Windows and Linux

Hide the native title bar on win32/linux (titleBarStyle: 'hidden', frame
untouched so native resize borders and snapping keep working) and render
minimize / maximize-restore / close buttons in the renderer, mirroring the
existing macOS traffic-light setup.

- New WINDOW:* IPC contract (minimize, toggle-maximize, close, get-state)
  handled in window.events.ts, resolved from the sender WebContents;
  close goes through win.close() so window-bounds persistence still runs.
- WINDOW:STATE_CHANGED pushed on maximize/unmaximize/fullscreen so the
  maximize/restore glyph stays correct for OS-triggered changes; controls
  hide while fullscreen.
- WindowControlsComponent mounts once in app-root as a manual popover so
  it stays in the browser top layer above CDK overlays (dialogs,
  multi-EPG) - same behavior as macOS traffic lights.
- Theme-aware via CSS vars (--app-on-surface, --app-hover-overlay);
  Windows-red close hover. Drag regions get right padding through a
  body-level frameless-platform class.
- Gated by RuntimeCapabilitiesService.usesCustomWindowControls; PWA and
  macOS never mount the controls.

Includes unit specs for the component and IPC handlers, an Electron E2E
suite (window-controls.e2e.ts), and a window-chrome section in
docs/architecture/workspace-shell.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(linux): upgrade Electron to 41 for frameless window decorations on Wayland

With the native title bar hidden, Linux windows lost the WM-drawn shadow
and rounded corners. Electron draws client-side decorations only on
native Wayland, and frameless-window CSD (GTK drop shadow + extended
resize boundaries) landed in Electron 41 - before that, frameless
windows render as plain rectangles.

- electron ^39.8.5 -> ^41.7.2 (Wayland auto-detected since 38.2; X11
  sessions remain undecorated, matching other frameless Electron apps;
  Windows keeps its DWM shadow and rounded corners).
- better-sqlite3 pinned to exactly 12.9.0: the last release shipping
  prebuilt binaries for both Node 20 (ABI 115, Jest) and Electron 41
  (ABI 145, runtime). 12.10.0 dropped the Node 20 prebuilds, forcing a
  from-source build that fails without a C++ toolchain.
- pnpm override node-abi 3.85.0 -> 3.92.0 so electron-builder
  install-app-deps can map Electron 41 to ABI 145.

Reviewed Electron 40/41 breaking changes: only the renderer clipboard
deprecation, which this app does not use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(e2e): address review feedback and window-managerless Linux CI

- Skip the three window-manager-dependent E2E assertions (maximize
  toggle, main-process state sync, minimize) on Linux CI: GitHub's
  ubuntu runners drive Electron under xvfb without a window manager, so
  maximize/minimize state never materializes there. Windows CI and
  local Linux/macOS runs keep the coverage.
- WINDOW:TOGGLE_MAXIMIZE now returns the requested state instead of
  re-reading isMaximized() right after the call, which races on Linux
  window managers where maximize()/unmaximize() complete
  asynchronously; the WINDOW:STATE_CHANGED push stays authoritative.
- Skip attaching window-state push listeners on macOS, where the
  custom controls never mount and the IPC traffic had no subscriber.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): gate custom window controls on the full bridge surface

Include getWindowState and onWindowStateChange in the
usesCustomWindowControls capability check — the controls rely on both
for initial state and for keeping the maximize/restore glyph in sync
with OS-triggered changes, so a partial bridge should not mount them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:03:27 +02:00
4gray 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
2026-06-08 07:55:01 +02:00
4gray 1badb9a057 refactor(electron): share typed preload bridge contract (#1018)
* refactor(electron): share typed preload bridge contract

* refactor(electron): tighten bridge review fixes

* refactor(electron): tighten playlist bridge returns

* refactor(electron): use bridge epg progress type

* refactor(epg): alias progress bridge types
2026-06-01 09:52:22 +02:00
4gray a1b0d9186c chore(test): clean lint and warning baseline 2026-05-26 19:30:21 +02:00
4gray 2a769ad44e Merge origin/master into PR 955 2026-05-22 01:34:56 +03:00
4gray 2ecb5c28f4 fix(dashboard): address PR review feedback 2026-05-22 00:59:17 +03:00
4gray 5b091809ef fix(xtream): ignore future recently added dates
## Summary
- Normalize Xtream recently-added timestamps across UI, import, and dashboard query paths.
- Filter future/invalid provider dates before ranking rails and migrate legacy millisecond cache rows.
- Add regression coverage for future timestamps, series date priority, and DB migration behavior.

## Validation
- GitHub checks passed, including Unit Tests and Typechecks, Web E2E, Electron E2E on macOS/Ubuntu/Windows, CodeQL, builds, and Greptile Review.
2026-05-19 14:39:38 +02:00
4gray 272e0e772e fix(stalker): preserve explicit portal identity (#941) 2026-05-15 18:34:49 +02:00
4gray 2d5c4fa4f9 chore: tighten validation and runtime logging
* chore: tighten validation and runtime logging

* fix(i18n): localize new settings labels
2026-05-15 17:43:42 +02:00
4gray d24c77a143 chore(nx): enforce scoped workspace boundaries (#942) 2026-05-15 09:59:06 +02:00
4gray a40d5447e7 feat(settings): add external player arguments (#932)
* feat(settings): add external player arguments

* fix(settings): address external player argument review

* fix(settings): require external player argument settings

* feat(settings): add external player argument placeholders

Closes https://github.com/4gray/iptvnator/issues/835
2026-05-14 11:04:01 +02:00
Toni Orioland4gray 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>
2026-05-14 00:13:08 +02:00
4gray a33d506266 fix(stalker): play radio collections with audio layout (#930)
* fix(stalker): play radio collections with audio layout

* fix(stalker): address radio review feedback
2026-05-12 19:48:38 +02:00
4gray 1ca291d35c chore(nx): update Nx to 22.7.1 (#923)
* chore(nx): update Nx to 22.7.1
Entire-Checkpoint: f957cd9849e0

* fix(ci): patch nx-electron package metadata copy
Entire-Checkpoint: f957cd9849e0

* fix(packaging): preserve electron package metadata
Entire-Checkpoint: f957cd9849e0

* fix(packaging): address package verifier review
Entire-Checkpoint: f957cd9849e0
2026-05-10 22:01:25 +02:00
4gray 724e4b1ab3 feat(playback): add embedded mpv (macos) stream recording (#916)
* feat(playback): add embedded mpv stream recording
Entire-Checkpoint: f957cd9849e0

* fix(playback): address embedded mpv recording review
Entire-Checkpoint: f957cd9849e0

* fix(playback): track mpv recording auto-stop replies
Entire-Checkpoint: f957cd9849e0
2026-05-10 12:27:09 +02:00
4gray 9261794b4a Merge pull request #902 from weeco/feat/xtream-xmltv-epg-fallback
feat(xtream): fall back to uploaded XMLTV when provider has no EPG
2026-05-09 13:06:51 +02:00
Tedd Johnson 626948f2f5 Add double-click stream open setting 2026-05-07 20:03:33 -07:00
weeco e751e82c07 feat(xtream): fall back to uploaded XMLTV when provider has no EPG
Live TV channels in Xtream playlists go blank whenever the provider's
get_short_epg returns nothing, even when the user has working XMLTV
URLs in Settings. The XMLTV pipeline already populates epg_programs
but only the M3U module was reading it.

Wire those uploads up as a fallback for Xtream too: when the provider
returns nothing for a channel with an epg_channel_id, look it up in
the local table. A settings toggle flips the priority for users whose
curated XMLTV is better than the provider's auto guide.

EpgQueueService.enqueue() became async to batch the XMLTV lookup once
per viewport change. To keep the queue consistent under fast scroll,
all shared-state mutations now happen behind a generation counter so
only the latest call commits. Per-method bridge gating in the new
XtreamXmltvFallbackService keeps each path working when the preload
exposes only one of the two endpoints.
2026-05-07 08:52:59 -07:00
4grayandClaude Opus 4.7 8e3f559033 feat(embedded-mpv): subtitles, playback speed, aspect override, retry on error
Native addon (apps/electron-backend/native/src/embedded_mpv.mm)
- Extend SessionSnapshot with subtitleTracks, selectedSubtitleTrackId,
  playbackSpeed, aspectOverride.
- Refactor track parsing into a shared updateTracksFromNode helper that
  filters by mpv "type" so audio and subtitle tracks share the code path.
- Observe sid, speed, video-aspect-override; clear sub state on
  MPV_EVENT_START_FILE.
- Export setSubtitleTrack (handles trackId === -1 as "no" to disable),
  setSpeed (clamped to 0.25–4.0), setAspect (passthrough string for
  video-aspect-override).
- Snapshot output now includes the new fields.

Service (embedded-mpv-native.service.ts) + IPC + preload
- Mirror methods on EmbeddedMpvNativeService with capability detection: each
  method throws a descriptive error if the loaded addon doesn't expose the
  underlying native function (i.e. user is running an older build).
- New IPC channels EMBEDDED_MPV_SET_SUBTITLE_TRACK, _SET_SPEED, _SET_ASPECT
  registered in events file and exposed via preload.
- Extend EmbeddedMpvSupport with a capabilities probe so the renderer can
  hide controls for features the current addon build doesn't ship.

Renderer (embedded-mpv-player.component.{ts,html})
- Three new popovers anchored above their buttons (subtitle / speed /
  aspect), gated by capabilities() and (for subtitles) by track count.
- Subtitle popover includes an Off entry; speed/aspect use fixed presets.
- Error state now surfaces the same overlay as the stalled state, with a
  Retry button that bumps the existing retryNonce signal — covers #8 from
  the audit.
- All session-payload defaults (loading stub, error stub, refresh fallback,
  dispose payload, native createSession default) updated for the new
  required fields.

NOTE: Existing addon binaries do not expose the new methods. Until the
addon is rebuilt (pnpm run serve:backend:embedded-mpv or the release
build script), capabilities will report subtitles/playbackSpeed/
aspectOverride as false and the new buttons will simply not appear.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
2026-05-05 09:53:32 +02:00
4grayandClaude Opus 4.7 e192cba776 feat(player): add VLC reuse-instance setting (#893)
VLC was unconditionally spawned per click — VLC's own single-instance
preference fails because the per-launch RC args defeat its D-Bus
forwarder. Mirror the existing MPV reuse pattern so users can opt in to
driving one tracked VLC via its RC interface (clear + add) instead of
opening a new window every stream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
2026-05-05 00:55:04 +02:00
4grayandClaude Opus 4.7 29fe8b905c perf(database): partial covering index for visible categories
Three queries filter on categories.hidden = false:

- getGlobalRecentlyAdded — every dashboard load (now sorts by added DESC
  on the indexed content side after the prior CAST removal)
- searchContent — Xtream search with excludeHidden
- globalSearch — global Xtream search with excludeHidden

All three start from content and join into categories on the
category_id PK. The hidden filter is then applied per-row, requiring a
row lookup for every result candidate — even before LIMIT.

Add a partial covering index on categories: idx_categories_visible(id,
playlist_id, type) WHERE hidden = 0. Two wins:

- Covering: SQLite serves the join (id) plus filters (playlist_id,
  type) and the hidden = 0 predicate directly from the index without
  ever touching the categories row.
- Partial: hidden categories are absent from the index, so they're
  pruned before any work happens — no row lookup, no comparison.

Schema mirrored in Drizzle so the ORM stays the single source of
truth. CREATE INDEX IF NOT EXISTS so existing DBs pick it up on next
startup with no migration needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
2026-05-02 10:27:38 +02:00
4gray ec2f6a702a chore: resolve master conflicts for embedded mpv branch
Entire-Checkpoint: 5b514fe72836
2026-05-02 02:19:54 +02:00
4grayandClaude Opus 4.7 9ecf4d44d3 perf(database): add composite indexes for hot ORDER BY paths
The existing single-column indexes force a sort step for the most common
list queries. These three composite indexes let SQLite scan in index
order and skip the sort entirely:

- recently_viewed(playlist_id, viewed_at DESC):
  getRecentItems(): WHERE playlist_id = ? ORDER BY viewed_at DESC LIMIT 100
- favorites(playlist_id, position, added_at DESC):
  getFavorites/getGlobalFavorites/getAllGlobalFavorites():
  WHERE playlist_id = ? ORDER BY position ASC, added_at DESC
- playback_positions(playlist_id, updated_at DESC):
  getRecentPlaybackPositions(): WHERE playlist_id = ? ORDER BY updated_at DESC LIMIT 20

CREATE INDEX IF NOT EXISTS so existing DBs pick up the new indexes on
next startup with no migration required. Mirrored in the Drizzle schema
using sql\`col DESC\` so the ORM stays the source of truth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3d6901072049
2026-05-01 21:11:44 +02:00
4grayandClaude Opus 4.7 eedfc498e7 perf(database): run PRAGMA optimize before closing connections
SQLite's recommended approach for keeping query plans current: cheap when
nothing needs analyzing, runs incremental ANALYZE on tables/indexes that
have grown significantly since the last run. Wrapped in try/catch since
optimize is advisory and must never block connection close.

Applied to both the main connection and the EPG worker connection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3d6901072049
2026-05-01 21:09:24 +02:00
4grayandClaude Opus 4.7 e33cb70dec perf(database): add SQLite performance pragmas (synchronous, cache, mmap)
Pair the existing journal_mode=WAL with the companion pragmas it needs to
actually pay off, and reduce read latency on hot query paths.

- synchronous=NORMAL: ~2-10x faster writes when paired with WAL. Safe — only
  risks losing the last committed txn on power loss; DB stays consistent.
- cache_size=-64000: 64MB page cache (default is 2MB).
- temp_store=MEMORY: keep sort/group temp tables in RAM.
- mmap_size=268435456: 256MB memory-mapped I/O for reads.

Applied to both connection sites: the main read-write/read-only connection
in libs/shared/database and the EPG worker connection in electron-backend.
The per-connection pragmas (cache_size, temp_store, mmap_size) apply to the
read-only agent-backend connection too.

Inspired by matracey/iptvnator@4ad8f88; ported manually since the file has
diverged significantly and the worker connection didn't exist in that fork.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3d6901072049
2026-05-01 21:02:52 +02:00
4gray 1af287205f perf: optimize playlist favorite lookups
Use Map and Set lookups for M3U favorite aggregation, dashboard global favorite mapping, and channel-list favorite/group resolution.

Remove the duplicate NgxIndexedDB provider registration and add regression coverage for duplicate URL and earliest-match behavior.

Entire-Checkpoint: c2cea9c530e6
2026-05-01 20:44:30 +02:00
4grayandClaude Opus 4.7 2f47ed16cb feat(command-palette): add recently used section and switch-player commands
Adds a "Recently used" section at the top of the command palette (capped at 5,
persisted via StorageMap) and five "Switch player to ..." commands for one-step
player switching from anywhere. MPV/VLC are gated to Electron; the active player
entry is shown disabled. Switching applies to the next playback session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-04-30 23:54:02 +02:00
4gray 4b2076fa53 feat(settings): add cover size settings and implement responsive cover sizing
Entire-Checkpoint: c2cea9c530e6
2026-04-30 18:56:06 +02:00
4gray 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
2026-04-27 00:32:14 +02:00
4gray 74c8adc12e fix(stalker): series favorites handling
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:16:33 +02:00
4gray efa0385fc8 feat(dashboard): implement context menu with actions for recent sources 2026-04-24 14:27:04 +02:00
4gray 7caa18308d feat: enhance unified collection with workspace commands and backdrop handling
- Added WorkspaceViewCommandService to UnifiedCollectionPageComponent for workspace layout commands.
- Implemented clear current view command for favorites and recent items.
- Updated unified favorites data service to use buildXtreamCollectionUid for UID generation.
- Enhanced unified recent data service to normalize timestamps and handle backdrop URLs.
- Introduced withRecentItems feature to manage recent item additions with backdrop support.
- Updated VodDetailsRouteComponent and SerialDetailsComponent to backfill content backdrops.
- Modified IXtreamDataSource interface to include backdrop URL handling for favorites and recent items.
- Added tests for recent items feature to ensure correct functionality and backdrop handling.
2026-04-22 22:23:50 +02:00
4gray a5b26065c9 feat: implement SQLite UTC timestamp normalization in stalker-item normalizer 2026-04-22 22:21:29 +02:00
4gray ad528238b2 feat: add backdrop_url to content schema and related database operations 2026-04-22 22:20:48 +02:00
4gray c8c93798f9 feat: enhance playlist backup export functionality and UI feedback
Entire-Checkpoint: c6e522b4276c
2026-04-21 08:54:03 +02:00
4gray e19257da22 feat: add export for epg-channel-metadata.model in index file
Entire-Checkpoint: c6e522b4276c
2026-04-20 00:23:52 +02:00
4gray c13dcc59dd feat: add channel metadata resolution and logo handling for enriched channel views
Entire-Checkpoint: c6e522b4276c
2026-04-20 00:23:32 +02:00
4gray 0e98af2e1a feat(m3u): implement group management dialog and hidden group titles functionality for m3u module 2026-04-11 22:59:58 +02:00
4gray 4175fc57b1 feat(xtream): enhance VOD details handling with fallback mechanisms and metadata validation
- Updated XtreamUrlService to handle missing stream IDs and extensions gracefully.
- Modified XtreamSelectionItem interface to allow for empty info arrays and null values.
- Introduced findVodCatalogItem function in XtreamStore for better catalog item retrieval.
- Added unit tests for fallback view model generation in vod-details-fallback.util.spec.ts.
- Created vod-details-fallback.util.ts to manage fallback view model construction and metadata checks.
- Updated vod-details-route.component.html to display fallback information when metadata is unavailable.
- Enhanced styling for fallback notes and status indicators in vod-details-route.component.scss.
- Implemented comprehensive tests for VodDetailsRouteComponent to ensure proper rendering of fallback and detailed views.
- Refactored xtream-vod-details.interface.ts to include new utility functions for metadata extraction.
2026-04-11 22:32:50 +02:00
4gray 4005687f18 refactor: improve archive playback support and enhance EPG item display 2026-04-10 07:53:48 +02:00
4gray 04f9686483 feat: implement detailed tracing for database operations, window events, and renderer API interactions 2026-04-05 23:26:09 +02:00
4gray 12eca90e4a feat: enhance Xtream data models and services with EPG support, including new fields for content and improved API handling 2026-04-05 19:04:40 +02:00
4gray 34098fd569 feat: add EPG metadata fields to content schema and migration statements 2026-04-05 19:04:26 +02:00
4gray 91cd650c1a feat: enhance EPG and portal features with error suppression and status resolution
- Added suppressErrorLog option to EPG API calls in withEpg feature.
- Introduced resolvePortalStatus function to streamline portal status checks in withPortal feature.
- Updated checkPortalStatus to return PortalStatusType instead of void.
- Enhanced XtreamStore to preserve cancelled content initialization block.
- Expanded XtreamState to include contentInitBlockReason for better state management.
- Added XtreamContentGateComponent to manage content initialization states.
- Implemented tests for XtreamContentGateComponent to ensure correct rendering based on contentInitBlockReason.
- Created PlaylistRefreshService to handle playlist refresh operations and events.
- Introduced new IPC commands for playlist refresh functionality.
- Added interfaces for playlist refresh events and payloads to improve type safety.
2026-04-04 13:40:34 +02:00
4gray 4d047f89ca feat: add dashboard settings and startup behavior options to settings component 2026-04-04 13:38:53 +02:00
4gray 654e63de27 fix(database): implement deduplication for Xtream categories and content 2026-03-29 13:37:29 +02:00