feat(epg): add global EPG display time offset (#1489)

Adds a global EPG display-time offset (Settings → EPG, whole minutes, ±720) for guides whose provider labels programme times with the wrong timezone. Display-only: parsed XMLTV values, SQLite rows, catch-up URLs and recording snapshots keep the provider's own times, so changing it needs no guide refresh. Closes the global part of #50.

The contract lives in `libs/shared/interfaces/src/lib/epg-display-offset.util.ts` with two equivalent forms: `epgDisplayTimeMs` shifts a programme for display, `epgProviderClockMs` shifts "now" into the provider's clock for every "currently airing" decision — the batched `GET_CURRENT_PROGRAMS_BATCH` lookup takes an explicit `nowMs`, and the channel lists, the Xtream/Stalker previews, the M3U player's current-programme mirror, the unified collection resolver, the dashboard live cards and the recording overlap all pick the same programme the guide renders as "now". Portal short-EPG windows start at the provider's own "now", so under a non-zero offset the Xtream preview surfaces cut their window from the full guide at the provider clock, Stalker short-EPG requests are widened for negative offsets, and every per-stream memory of the previous offset is retired together when the setting changes.

Co-authored-by: Mark Jardine <markjardine27@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-09-04 18:46:10 +02:00
1 parent b2ca85172c
commit d8d36476e6
118 files changed
+3349 -352

No files matched your search

@@ -233,6 +233,14 @@ export interface Settings {
coverSize?: CoverSize;
/** Live EPG panel layout: horizontal timeline (default) or vertical list */
epgViewMode?: EpgViewMode;
/**
* Display-only correction, in whole minutes, for guides whose provider
* labels programme times with the wrong timezone. Clamped to ±720 by
* `normalizeEpgOffsetMinutes`; the two equivalent ways of applying it are
* documented in `epg-display-offset.util.ts`. Electron only in practice
* (the control is gated behind `supportsEpg`, like `epgViewMode`).
*/
epgOffsetMinutes?: number;
/** Per-rail dashboard visibility preferences. Missing keys default on. */
dashboardRails?: DashboardRailsSettings;
/**