Skip to content

Pr/epg fixes#1104

Open
Alanon202 wants to merge 12 commits into
4gray:masterfrom
Alanon202:pr/epg-fixes
Open

Pr/epg fixes#1104
Alanon202 wants to merge 12 commits into
4gray:masterfrom
Alanon202:pr/epg-fixes

Conversation

@Alanon202

Copy link
Copy Markdown

I’m using IPTVNator with xstream providers and my experience left a lot to be desired so I tried to fix some of that.

I did some work on EPG and catch-up features which seemed broken in several ways, especially when browsing from the favourites tab. This PR tries to fix many problems I encountered.

While fixing stuff like not being able to load all the EPG data properly from the favourites list, not being able to activate catch-up, I also tried to fix some other things as well.

Some caveats:

  • I’m a crappy coder, so the code is probably janky in places, my hope was mainly that this is useful as a baseline for someone that knows better;
  • My abilities to iterate on this code are probably going to be limited, but I’ll try my best;
  • I was only able to test it out on xstream streams from two providers - further testing is needed for m3u to make sure I didn’t break something;
  • I was only able to test it out on Linux, further testing is needed on other platforms;
  • Currently all internal players seem to have buffering issues during catch-up and stop after 3 minutes (mpv is fine) - this could be due to my providers or the players themselves, TBD;

…back

- Remove .limit(500) from EPG database queries (returns oldest programs,
  silently dropping current/recent data)
- Fall back to get_short_epg endpoint when get_simple_data_table is empty
  (many Xtream providers don't implement the full EPG endpoint)
- Don't require explicit tv_archive=1 for Xtream archive detection
  (providers often omit the field, making catchup unavailable)
- Replace EpgViewComponent with EpgListComponent
- Add programActivated handler that constructs catchup URLs via
  resolveM3uCatchupUrl (M3U) or XtreamUrlService.resolveCatchupUrl
- Add EPG-to-program conversion (currentPortalEpgPrograms) with proper
  start/stop timestamps
- Add archive detection (currentPortalArchiveDays) reading tv_archive
  from the database (added to the favorites DB queries)
- Increase portal EPG timeout from 3s to 10s (for Xtream API calls)
- Try getFullEpg before getShortEpg in the favorites EPG loading path
  (matches the all-channels loadEpg strategy)
- Enable date navigation on the live-epg-panel for all content types
- Add epg_channel_id to the favorites DB query, XtreamFavoriteRow type,
  and UnifiedCollectionItem interface so it flows through to the UI
- Modify loadXtreamEpgItems (sidebar) and loadXtreamEpgBatch (card
  preview) to check the uploaded XMLTV EPG first via epg_channel_id,
  falling back to the Xtream API only when no match is found
… EPG paths

Add right-click context menu and channel-details-dialog button for mapping
EPG channels to content channels. The mapping is stored in a new
epg_channel_mappings table and resolved at three levels:

- epg-query.service.ts (backend query layer) — catches all getChannelPrograms
  calls including the Xtream XMLTV fallback
- loadXtreamEpgItems (sidebar) — checks uploaded XMLTV, then mapping table,
  then Xtream API
- loadXtreamEpgBatch (preview cards) — same three-tier fallback with
  concurrency limiting (max 3 simultaneous requests)

Also includes EPG reliability fixes:
- Reduced Xtream API failure cooldown 60s → 30s
- Increased preview fetch limit 2 → 5
- Dialog now fetches current mapping on open instead of relying on callers
@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown

Greptile Summary

This PR addresses the three previously-flagged issues: the 14-day/2-day time-window filter replaces the unbounded query, preferUploadedEpgOverXtream is now read in loadXtreamEpgItems, and the catchup logic is extracted into the new UnifiedLiveCatchupService (component drops from 724 → 571 lines). A new manual EPG channel-mapping feature is introduced — a full CRUD flow (IPC handlers, DB table, UI dialog) that lets users override the channel→EPG-ID resolution.

  • EPG query time window: selectChannelPrograms and selectLegacyChannelPrograms now filter by stop >= now−14d AND start <= now+2d, replacing the old .limit(500). The 2-day forward window is narrower than the commonly-expected 7-day advance guide and may truncate providers that supply a full week of schedule data.
  • Manual EPG mapping: New epg_channel_mappings table with IPC CRUD, a search dialog, and integration into all channel-list views. The mapping is applied both at the IPC boundary (resolveChannelId in epg.events.ts) and again inside EpgQueryService.getChannelPrograms, resulting in a redundant DB round-trip per lookup.
  • EpgMappingDialogComponent: Contains a duplicate bare this.search$ expression (dead code) and an unused toSignal import.

Confidence Score: 5/5

Safe to merge — all three previously-flagged regressions are corrected, the new EPG mapping feature is well-structured, and the remaining nits are minor.

All previously raised issues are addressed: the unbounded query is replaced by a time-window filter, the preferUploadedEpgOverXtream setting is now respected in the favourites path, and the catchup logic has been extracted into a dedicated service. Newly introduced issues are limited to cosmetic/redundancy concerns that do not affect correctness. The 2-day forward EPG window is the only functional trade-off worth revisiting.

The epg-mapping-dialog.component.ts has a duplicate this.search$ expression and an unused import to clean up. epg.events.ts and epg-query.service.ts both resolve the channel mapping for the getChannelPrograms path, producing a redundant round-trip.

Important Files Changed

Filename Overview
apps/electron-backend/src/app/events/epg-query.service.ts Added 14-day/2-day time-window filter replacing unbounded queries, plus internal getMapping for manual channel ID overrides. The 2-day forward window may truncate advance EPG guide visibility.
apps/electron-backend/src/app/events/epg.events.ts New IPC handlers for EPG mapping CRUD; handleGetChannelPrograms double-resolves the mapping (once at IPC boundary, once inside the query service), causing a redundant DB round-trip.
libs/portal/shared/data-access/src/lib/collection/stream-resolver.service.ts Properly reads preferUploadedEpgOverXtream from SettingsStore, adds 4-step EPG resolution waterfall in the favourites path, and introduces limited concurrency (3 workers) for batch EPG loading.
libs/portal/shared/ui/src/lib/components/unified-collection/unified-live-catchup.service.ts New service correctly extracts catchup logic from the component; activeItem as a root-provided singleton signal is appropriate since only one live tab is active at a time.
libs/ui/components/src/lib/channel-list-container/epg-mapping-dialog/epg-mapping-dialog.component.ts New EPG mapping dialog with debounced search; contains a stale duplicate this.search$ expression and an unused toSignal import.
libs/portal/xtream/data-access/src/lib/stores/features/with-epg.feature.ts Adds getShortEpg as a last-resort fallback when full EPG and XMLTV are both empty; guarded with Array.isArray to handle malformed responses.
libs/shared/database/src/lib/schema.ts Adds epg_channel_mappings table with a unique constraint on channel_key and a playlist index; schema matches the CREATE TABLE IF NOT EXISTS statement added to connection.ts.
apps/electron-backend/src/app/database/operations/epg-mapping.operations.ts New file providing typed CRUD helpers for the epg_channel_mappings table; uses Drizzle-ORM parameterized queries throughout so the LIKE pattern in searchEpgChannels is safe from injection.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[EPG Request for Channel ID] --> B{IPC boundary\nepg.events.ts}
    B --> C[resolveChannelId\nDB lookup in epg_channel_mappings]
    C --> D{Mapping found?}
    D -- Yes --> E[Use mapped EPG Channel ID]
    D -- No --> F[Use original Channel ID]
    E --> G[epgQueryService.getChannelPrograms]
    F --> G
    G --> H[getMapping internal DB lookup\nredundant round-trip]
    H --> I{Mapping found again?}
    I -- Yes --> J[Use remapped ID]
    I -- No --> K[Use ID as-is]
    J --> L[selectChannelPrograms\nnow-14d to stop AND start to now+2d]
    K --> L
    L --> M[Return EpgProgram list]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[EPG Request for Channel ID] --> B{IPC boundary\nepg.events.ts}
    B --> C[resolveChannelId\nDB lookup in epg_channel_mappings]
    C --> D{Mapping found?}
    D -- Yes --> E[Use mapped EPG Channel ID]
    D -- No --> F[Use original Channel ID]
    E --> G[epgQueryService.getChannelPrograms]
    F --> G
    G --> H[getMapping internal DB lookup\nredundant round-trip]
    H --> I{Mapping found again?}
    I -- Yes --> J[Use remapped ID]
    I -- No --> K[Use ID as-is]
    J --> L[selectChannelPrograms\nnow-14d to stop AND start to now+2d]
    K --> L
    L --> M[Return EpgProgram list]
Loading

Reviews (2): Last reviewed commit: "fix: add providedIn:root to catchup serv..." | Re-trigger Greptile

Comment thread apps/electron-backend/src/app/events/epg-query.service.ts
@Alanon202 Alanon202 force-pushed the pr/epg-fixes branch 3 times, most recently from 38d84bf to 005d2fa Compare June 30, 2026 22:32
@Alanon202

Alanon202 commented Jun 30, 2026

Copy link
Copy Markdown
Author

@greptileai Commit 005d2fa adresses your concerns, please conduct another review:

  • add 14-day time window filter to EPG queries (replaces unbounded .limit removal)
  • respect preferUploadedEpgOverXtream setting in loadXtreamEpgItems
  • extract catchup logic into UnifiedLiveCatchupService (component: 724→571 lines)
  • remove duplicate credential-fetching logic (now in shared service)

Alanon202 added 4 commits July 1, 2026 01:02
- remove dead this.search$ line in dialog constructor
- check save/remove result before closing mapping dialog
- escape LIKE wildcards (% and _) in EPG channel search
- remove double mapping lookup (epg.events IPC handler no longer
  resolves mappings since epg-query.service already does)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant