Skip to content

feat(epg): support multiple EPG sources#501

Open
stackia wants to merge 3 commits into
mainfrom
codex/multi-epg-sources
Open

feat(epg): support multiple EPG sources#501
stackia wants to merge 3 commits into
mainfrom
codex/multi-epg-sources

Conversation

@stackia
Copy link
Copy Markdown
Owner

@stackia stackia commented May 24, 2026

Summary

  • Support comma-separated x-tvg-url / url-tvg EPG sources with ordered per-source server caching.
  • Expose additional cached EPGs through /epg/N.xml routes and emit ordered local EPG URLs in exported playlists.
  • Load multiple EPGs in the web player and keep the first matching EPG per channel.

Closes #499.

Validation

  • cmake --build build -j$(getconf _NPROCESSORS_ONLN)
  • ./scripts/run-e2e.sh test_epg.py
  • pnpm run type-check:tsc
  • pnpm run lint:biome
  • pnpm run lint:ruff
  • pnpm run lint:clang
  • pnpm run docs:build
  • git diff --check

@stackia stackia marked this pull request as ready for review May 24, 2026 21:02
@stackia stackia requested a review from Copilot May 24, 2026 21:03
@github-actions
Copy link
Copy Markdown
Contributor

Azure Static Web Apps: Your stage site is ready! Visit it here: https://thankful-water-0a297bf00-501.eastasia.1.azurestaticapps.net

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds multi-EPG-source support across the daemon, exported playlists, and the web player by treating x-tvg-url / url-tvg as an ordered, comma-separated list of sources, caching each source independently, and exposing additional cached EPGs via /epg/N.xml(.gz) routes.

Changes:

  • Parse and split comma-separated EPG sources (only when the comma is followed by http(s):// or file://) and store them as ordered sources.
  • Cache/fetch EPGs per source with per-source retry state; expose extra cached sources through /epg/N.xml and include ordered local EPG URLs in generated playlists.
  • Update the web player to load multiple EPG URLs and merge them by per-channel priority (first matching source wins).

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
web-ui/src/types/player.ts Extends M3U metadata type to carry multiple EPG URLs.
web-ui/src/pages/player.tsx Loads multiple EPG sources and merges them for channel matching.
web-ui/src/lib/m3u-parser.ts Parses x-tvg-url/url-tvg and splits into ordered URL list with safe comma handling.
web-ui/src/lib/epg-parser.ts Adds helpers to load multiple EPGs and merge them by channel priority.
src/worker.c Updates periodic EPG retry/update logic to work per EPG source.
src/m3u.c Splits header EPG URL list, sets ordered sources, and emits ordered local EPG URLs in playlists.
src/epg.h Introduces per-source EPG cache structure and new multi-source APIs.
src/epg.c Implements multi-source cache, per-source fetch/retry, and generation-based stale fetch handling.
src/connection.c Adds /epg/N.xml(.gz) routing and serves cached EPG by source index.
rtp2httpd.conf Documents comma-separated multi-EPG configuration example.
e2e/test_epg.py Adds E2E coverage for splitting rules, playlist output, per-index serving, token propagation, and partial failures.
docs/reference/configuration.md Documents multi-EPG sources in config reference (ZH).
docs/guide/m3u-integration.md Documents multi-EPG behavior and exported URL format (ZH).
docs/en/reference/configuration.md Documents multi-EPG sources in config reference (EN).
docs/en/guide/m3u-integration.md Documents multi-EPG behavior and exported URL format (EN).
Comments suppressed due to low confidence (1)

src/epg.h:17

  • The is_gzipped field comment says it is determined "based on URL", but the implementation sets it by checking the gzip magic bytes in the cached fd. Update the comment to match actual behavior to avoid confusion for future changes.
  char *url;               /* EPG source URL */
  int data_fd;             /* tmpfs file descriptor for EPG data (zero-copy), or -1 if not
                              available */
  size_t data_size;        /* Size of EPG data */
  int is_gzipped;          /* 1 if data is gzip compressed (based on URL), 0 otherwise */
  int fetch_error_count;   /* Number of consecutive fetch errors */

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/epg.c Outdated
Comment thread src/m3u.c Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Azure Static Web Apps: Your stage site is ready! Visit it here: https://thankful-water-0a297bf00-501.eastasia.1.azurestaticapps.net

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Comment thread src/epg.c Outdated
Comment thread src/m3u.c Outdated
@github-actions
Copy link
Copy Markdown
Contributor

Azure Static Web Apps: Your stage site is ready! Visit it here: https://thankful-water-0a297bf00-501.eastasia.1.azurestaticapps.net

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Comment thread src/m3u.c
Comment on lines +1512 to +1519
if (i == 0) {
if (has_r2h_token && encoded_token) {
written = snprintf(dst_ptr, result_size - result_used, "%s%s?r2h-token=%s", base_url,
source->is_gzipped ? "epg.xml.gz" : "epg.xml", encoded_token);
} else {
written = snprintf(dst_ptr, result_size - result_used, "%s%s", base_url,
source->is_gzipped ? "epg.xml.gz" : "epg.xml");
}
Comment thread src/epg.c
Comment on lines +388 to +392
for (size_t i = 0; i < epg_cache.source_count; i++) {
if (epg_fetch_source_async(epfd, i) == 0) {
result = 0;
}
}
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.

当外部 m3u 内含有多个 epg 源时会导致获取失败

2 participants