Commit Graph
545 Commits
Author SHA1 Message Date
4gray ade043d427 fix(remote-control): use iptvnator favicon (#1023) 2026-06-02 10:39:36 +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 4847eb5241 refactor(electron): split epg event services
Split EPG IPC orchestration, worker lifecycle, and query logic into focused services. Harden clear-worker lifecycle after review with timeout/exit handling and regression coverage.
2026-05-27 22:05:40 +02:00
4gray edaa981061 refactor(electron): split external player sessions
Split the Electron external-player IPC monolith into focused launch-context, playback-request, runtime, MPV session, and VLC session modules. Includes Greptile follow-up fixes for Homebrew Cask VLC path resolution and VLC spawn-error promise handling, with regression coverage.
2026-05-26 23:15:28 +02:00
4gray 701cf23299 chore(frontend): clean logging and lint suppressions 2026-05-26 20:18:12 +02:00
4gray a1b0d9186c chore(test): clean lint and warning baseline 2026-05-26 19:30:21 +02:00
4gray a2bda8fd36 test(pwa): guard static stylesheet regression spec 2026-05-24 15:35:28 +03:00
4gray bb4e386c6c fix(pwa): avoid CSP-blocked stylesheet deferral 2026-05-24 14:59:08 +03:00
4gray 697eab6e73 refactor(epg): route renderer calls through runtime bridge
Add typed EPG runtime bridge, split EPG runtime capabilities, migrate renderer EPG callers away from direct window.electron access, and address Greptile review feedback.
2026-05-24 13:45:51 +03:00
4gray 003a8774a1 fix(electron): tighten navigation review feedback
Restrict packaged file navigation to the app renderer, guard redirects, and handle fire-and-forget header override IPC failures.
2026-05-23 19:05:38 +03:00
4gray c36d404a72 test(electron): cover broad playlist header override 2026-05-23 15:59:02 +03:00
4gray 3c5f7ab92e fix(electron): preserve playlist header defaults
Keep playlist-level request header overrides active when channel-scoped headers are cleared, and cover layered override behavior.
2026-05-23 15:57:29 +03:00
4gray fc449ca73e feat(electron): harden renderer security
Scope playback request header overrides to active stream origins and document the Electron runtime security contract.
2026-05-22 23:31:41 +03:00
4gray 189daea3fa test(electron): align xtream epg schedule expectation 2026-05-22 14:59:10 +03:00
4gray 0fb4c7d13e fix(settings): gate external player choices by launch support 2026-05-22 14:39:20 +03:00
4gray 959f6061f0 fix(runtime): address greptile capability feedback 2026-05-22 14:37:06 +03:00
4gray ed8680116c fix(runtime): address consolidated review feedback 2026-05-22 14:02:56 +03:00
4gray 582422c512 refactor(runtime): gate settings backup file save by capability 2026-05-22 13:41:24 +03:00
4gray c9a0d5210e refactor(runtime): gate settings sections by capability 2026-05-22 13:41:24 +03:00
4gray 09e2e08fc1 refactor(runtime): gate settings external players by capability 2026-05-22 13:40:14 +03:00
4gray 78a5a7af74 refactor(settings): use runtime capabilities for platform state 2026-05-22 13:33:31 +03:00
4gray b51cbcae15 refactor(app): use runtime capabilities for shell gates 2026-05-22 13:08:00 +03:00
4gray 5827ef05f0 refactor(playback): use runtime capability for external fallback 2026-05-22 12:45:09 +03:00
4gray 1806cb46aa refactor(web): harden runtime service payloads 2026-05-22 11:20:50 +03:00
4gray 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
2026-05-22 10:20:03 +03:00
4gray c96a805b47 Merge pull request #987 from 4gray/agent/electron-ipc-sqlite-contracts
test(electron): add sqlite ipc contract coverage
2026-05-22 09:39:25 +03:00
4gray dce5b557af test(electron): split sqlite ipc contract fixtures 2026-05-22 03:53:35 +03:00
4gray 6cf730ddf6 test(electron): add sqlite ipc contract coverage 2026-05-22 03:42:01 +03:00
4gray 5d355b6f10 fix(web): address strict mode review feedback 2026-05-22 03:11:14 +03:00
4gray 4ab8915483 chore(web): enable strict TypeScript mode 2026-05-22 02:58:12 +03:00
4gray 55efc24608 fix(pwa): harden self-hosted runtime boundaries 2026-05-22 02:09:57 +03:00
4gray 2a769ad44e Merge origin/master into PR 955 2026-05-22 01:34:56 +03:00
4gray 5cfe3aef06 fix(dashboard): localize progress labels 2026-05-22 01:02:04 +03:00
4gray 2ecb5c28f4 fix(dashboard): address PR review feedback 2026-05-22 00:59:17 +03:00
4gray 67d512d44c Merge pull request #961 from 4gray/agent/pwa-provider-target-flow
fix(docker): exclude agent worktrees from build context
2026-05-22 00:47:22 +03:00
4gray b2909a1186 feat(workspace): add keyboard shortcuts help (#957)
* feat(workspace): add keyboard shortcuts help

* style(workspace): polish keyboard shortcuts dialog

* fix(workspace): address shortcuts review feedback

* docs(workspace): clarify desktop shortcut availability

* refactor(workspace): split keyboard shortcut definitions
2026-05-21 19:15:46 +03:00
4gray 362595525d test(pwa): tighten self-hosted proxy assertions 2026-05-21 18:44:42 +03:00
4gray 6829c667bd fix(pwa): restore xtream rail and vod navigation 2026-05-21 14:18:11 +03:00
4gray 401d434bce test(pwa): cover self-hosted provider target flow 2026-05-21 11:19:04 +03:00
4gray 738397c9ed feat(website): add giscus blog comments 2026-05-20 18:02:19 +02:00
4grayandClaude Opus 4.7 7083bd2af4 fix(shell): keep Add Playlist reachable on Settings; fix dashboard e2e
Two Electron E2E regressions from this branch's redesigns:

1. Hiding the playlist switcher on /settings (a playlist-scoped control)
   also hid the global "Add playlist" button, which lives in the same
   header-actions block. A user configuring Settings before importing
   their first source had no way to add a playlist — and the settings /
   playlist-switcher E2E suites, which add a portal right after saving a
   setting, timed out waiting for the button. The Add Playlist button is
   a global action, so it now stays visible on Settings; only the
   per-playlist context shortcut and bulk actions are hidden there.

2. dashboard-activation.e2e.ts asserted on `dashboard-global-favorites-
   rail`, the mixed Favorites rail removed earlier in this branch. The
   test now targets the v0.22 rails: live favorites resolve through the
   favorites-first `dashboard-live-recent-rail`, and the played movie /
   series resolve through `dashboard-continue-watching-rail` (renamed
   from `dashboard-recently-watched-rail`). The movie-from-favorites
   assertion was dropped — that dashboard surface no longer exists; the
   favorites collection still has its own page-level coverage.

Verified locally: dashboard-activation, settings, and playlist-switcher
E2E specs all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:55:06 +02:00
4gray 3096115e64 docs: add external players playback article 2026-05-20 15:35:38 +02:00
4grayandClaude Opus 4.7 a8253c5801 test(e2e): adapt to v0.22 dialog + theme picker redesigns
The redesign rollup in this branch replaced the Add Playlist dialog's
2-level type × subtype tabs with a single flat 5-card radiogroup, and
replaced the Settings theme list with a compact segmented control whose
options are just "Light"/"Dark"/"System" (no "theme" suffix). E2E tests
that pinned to the old roles/labels failed in CI:

  - basic / xtream / stalker / self-hosted: getByRole('tab', ...) on
    "Add via file upload" / "Xtream" / "Stalker" — there are no tabs in
    the new dialog. Switched to getByRole('radio', { name: /M3U file/i })
    and friends, matched against the new radio labels.
  - settings: getByRole('radio', { name: 'System theme' }) — the new
    picker uses just "System". Scoped to the [data-test-id="select-theme"]
    radiogroup so it doesn't collide with the identically-labelled
    cover-size options below.

The Electron `clickDialogSegmentedOption` helper grew a radio-role lookup
as its primary path and keeps the old tab/button/legacy-selector
fallbacks so a future redesign won't break every fixture again. The
"M3U" parent category becomes a no-op (the new picker has no standalone
M3U tile — callers always specialise to M3U URL/file/text immediately
after, which is what we want anyway).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:07:26 +02: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
4grayandClaude Opus 4.7 8091dec074 feat(dashboard): show S·E badge on Continue Watching series cards
For series in the Continue Watching rail, surface which episode the user
left off on as a small "S2·E5" chip next to the card subtitle. Sources
the season/episode straight off the matched PlaybackPositionData so the
information is always in sync with the resume target.

Fixes a latent bug in the same patch: getPlaybackPositionForItem could
not resolve series whose recent_items row carried the series id (the
landing-page path), because playback_positions are keyed by the episode
id. The lookup now matches both shapes (contentXtreamId === xtreamId OR
seriesXtreamId === xtreamId) and prefers the most recently updated
episode, which also restores the resume progress bar that had been
silently dropping out for series.

Localised in all 17 non-English locales — most use language-specific
short forms (St·F in German, T·E in Spanish/Portuguese, С·Э in
Russian/Belarusian, 시즌N N화 in Korean) rather than the English S·E.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:32:13 +02:00
4grayandClaude Opus 4.7 6891a1ca30 i18n(epg): translate multi-EPG toolbar tooltips
The Zoom/Filter/Search Programs buttons in the Multi-EPG header
hard-coded English matTooltip strings, so they never localized — even
when the rest of the toolbar (Close, Previous/Next day) used keys. Adds
six new EPG.* keys (ZOOM_IN, ZOOM_OUT, FILTER_CHANNELS, CLOSE_FILTER,
SEARCH_PROGRAMS, CLOSE_PROGRAM_SEARCH), wires both [matTooltip] and
[attr.aria-label] through `| translate`, and fans the strings out to
all 17 non-English locales via the i18n-fill pipeline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:15:35 +02:00
4grayandClaude Opus 4.7 55215a5059 feat(radio): add tooltips + aria-labels on every player button
Radio audio-player buttons (skip-prev, play/pause FAB, skip-next, mute,
volume slider) had zero matTooltip or aria-label. The UX audit flagged
this: "Skip-prev / Skip-next — what do they skip? In radio, is the
next 'track' the next station? Next in favourites? Random? Without a
tooltip the buttons trade silently." Same critique applied to mute
and play/pause for screen-reader users.

Add hover tooltips and screen-reader labels on all 5 controls. Skip
prev/next say "Previous station" / "Next station" so the behaviour is
explicit (they walk the active filtered list). Play/Pause flips its
label with state. Mute/unmute flips when isMuted() || volume === 0.
Volume slider gets its own label so the slider thumb is announced
correctly.

i18n: 7 new AUDIO_PLAYER.* keys translated across all 17 non-English
locales by per-locale agents; all 18 locales pass coverage and
placeholder integrity checks.

52/52 ui-playback tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:03:14 +02:00
4grayandClaude Opus 4.7 b246f8e51b fix(settings): drop dead bottom padding below the action bar
`--settings-action-bar-space: 80px` added padding-bottom to the form
*after* its last child (the action bar), giving 80px of scrollable
empty space below the bar — visible when scrolling to the end as a
big dark band the page kept scrolling past the last setting.

The reservation was originally tuned for the OLD floating-chip
layout, where the bar hovered 24px above the bottom edge and content
needed to be pushed above it. In the new sticky-footer model the bar
IS the bottom edge, so no padding is needed below it.

Set to 0px. CDP verifies form.bottom === bar.bottom after the change
— the scroll now ends cleanly at the action bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:02:15 +02:00
4grayandClaude Opus 4.7 27becf5b2a fix(settings): remove transient box-shadow pulse on scroll
The settings-section-scroll directive ran a 260ms Element.animate() on
whichever section became "active" as the user scrolled — three
keyframes painting an `inset 0 0 0 1px var(--settings-group-active-ring)`
ring plus a soft glow on the section block. With the new flat layout
(no card chrome, no static active ring), that pulse drew a brief 1px
border around each block as you scrolled past — the user-reported
"short border kind of highlight around blocks" effect.

The rail's left active state already announces which section you're on,
so the inline pulse is redundant. Removed the entire active-section
animation effect (the pendingScrollTarget scroll-into-view effect is
untouched — that's the click-the-nav-and-jump behaviour).

CDP verifies: all .settings-group elements report `box-shadow: none`
after scrolling. 50/50 settings tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:00:56 +02:00
4grayandClaude Opus 4.7 66f70b62c7 fix(settings): pin save bar flush to viewport bottom
`--settings-action-bar-offset: 24px` was tuned for the previous
floating-chip layout (border-radius:18px, margin-left:auto) so the bar
visually hovered above the bottom edge like a card. After converting
it to a full-width sticky footer the 24px gap just left a strip of
empty content peeking out below the bar — the user-reported "not
perfectly aligned to the footer" gap.

Set the offset to 0px so the bar sits flush against the bottom of the
scroll container. CDP verifies: barBottom === viewport height after
the change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 01:56:25 +02:00