Skip to content

v1.5.0: custom EPG sources, channel mapping, category picker, backup domains, Xtream live format, desktop tray + titlebar + notifications, settings panel polish#61

Merged
infinitel8p merged 26 commits into
mainfrom
release/1.5.0
May 14, 2026
Merged

v1.5.0: custom EPG sources, channel mapping, category picker, backup domains, Xtream live format, desktop tray + titlebar + notifications, settings panel polish#61
infinitel8p merged 26 commits into
mainfrom
release/1.5.0

Conversation

@infinitel8p
Copy link
Copy Markdown
Owner

@infinitel8p infinitel8p commented May 12, 2026

v1.5.0 release. Headline work below; full changelog will land in the GitHub release notes once tagged.

Custom EPG sources + Jellyfin-style channel mapping

User-facing on /login and /epg:

  • Per-playlist primary EPG URL override and a list of additional waterfall sources on /login. A new "Test sources" button validates URLs without saving, and a "Don't use the provider's default EPG" toggle skips auto-detect entirely.
  • "Map channels" dialog on /epg lets users manually map a channel to any XMLTV entry when auto-match misses. The picker shows display-name + tvg-id + programme count, with a current-selection card and "Clear override". List is virtualised (~30 of 53k rows in DOM), search is debounced, and tvg-id resolution is memoised.
  • Auto-name fallback: channels without a tvg-id (or with one that doesn't match the loaded XMLTV) now match by normalised display-name with HD / FHD / UHD / 4K / SD suffix stripping and separator-insensitive compare. So "MDR Sachsen HD" auto-resolves to "MDR Sachsen", "Channel 21 HD" to "Channel21".
  • New Auto filter on the EPG mapping list to focus on auto-matched vs manually-overridden rows.
  • The M3U Live TV side panel now reads from the loaded XMLTV instead of the hardcoded "No EPG available for M3U source" stub.

Plumbing:

  • Multi-source fetch is parallel via Promise.allSettled; per-URL conditional GET (If-Modified-Since / ETag) + per-URL IDB cache so a single 304 still benefits the merged result.
  • Magic-byte gzip sniff (1F 8B) on every response, in addition to URL extension and Content-Type heuristics.
  • EPG fetches forced through tauri-plugin-http so desktop and Android bypass browser CORS on arbitrary XMLTV hosts.
  • O(1) name lookup via a per-playlist nameIndex built once per EPG load.
  • Parsing offloaded to a dedicated epg-worker.ts Web Worker so multi-source merges don't jank the main thread.

Closes #63, closes #64.

Shared category picker

  • New CategoryPickerDialog.astro component plus src/scripts/lib/category-picker.ts (single ~650 LOC controller) replaces the four duplicated inline picker implementations that lived in /livetv, /movies, /series, and /epg.
  • Picker has its own search input, "Sync with Live TV" toggle on the EPG variant, and consistent keyboard / D-pad behaviour across all four pages.
  • Search results re-ranked via scoreNormMatch (prefix > whole-word > substring), stable on ties via origIndex so unrelated rows don't reorder when typing.
  • Rows now carry data-row-kind="pseudo|all|hidden|regular" so styling and D-pad navigation can target row classes without string-matching the label.
  • Net change: ~1000 lines added, ~2000 lines removed across the page bundles.
  • New i18n keys for "Sync with Live TV" label + description in all 16 locales.

Closes #62.

Backup domains (Xtream)

  • New UI on /login for declaring backup domains per Xtream playlist alongside the primary host.
  • New src/scripts/lib/xtream-api.js wraps player_api.php calls with automatic mirror failover. Candidates are fired sequentially until one returns 2xx; the winning index is pinned in creds.js so subsequent calls (including stream-URL builds via loadCreds) target the same working host until the entry list changes.
  • Failover triggers on any non-2xx response or network / timeout error. 4xx is included on purpose: some providers hand out different credentials per backup domain.
  • Refactored existing API call sites (account-info.js, catalog.js, EPG auto-detect, etc.) to go through xtreamApiFetch for unified error handling.
  • Toast surfaces the first time a failover kicks in so the user knows the primary is down.

Refs #63.

Relaxed M3U URL validation + container detection

  • /login accepts any http(s):// URL that serves an M3U playlist; the .m3u / .m3u8 extension requirement is gone. Some providers (notably Dispatcharr) serve playlists from extensionless endpoints, so the hard regex blocked legitimate sources.
  • New streamKindHint + probeContainer in player-runtime.ts decide HLS vs raw TS from URL extension + MIME, with an async Range: bytes=0-0 Content-Type probe for ambiguous URLs (Dispatcharr's /proxy/ts/stream/<uuid>, Xtream's bare /live/<u>/<p>/<id>). Per-origin probe cache so a provider isn't re-probed on every channel switch.
  • ensureLive extracts the x-tvg-url / tvg-url value from the M3U body into xt_m3u_epg:<id> so the EPG-source resolver can pick it up.
  • Helper text + validation error messages updated across all 16 locales.

Closes #65.

Advanced playback: Xtream live stream format

  • New "Advanced playback (optional)" <details> block on /login for Xtream playlists with a Live stream format selector: HLS (.m3u8) - default - or MPEG-TS (.ts).
  • Stored as liveContainer on the Xtream entry; honoured when stream.ts builds the live stream URL so users can force .ts for providers that don't serve .m3u8 (Dispatcharr) without changing anything else.
  • The section auto-opens when editing an existing entry whose stored format is already ts.
  • Helper strings + select option labels added to all 16 locales.

Refs #65.

Native container support in the embedded player

  • player-runtime.ts now recognises a third stream kind, native, for mp4, m4v, mkv, webm, mov, avi, m4a, mp3, aac, flac, ogg, and any video/* / audio/* MIME. Both the Video.js backend (new loadNative path) and the ArtPlayer backend skip hls.js / mpegts.js entirely and hand the URL straight to the underlying <video> element. The async container probe also returns native when it sees a generic media MIME, so extensionless URLs that serve straight MP4 still play.

RTSP playback fix (external mpv)

  • buildMpvArgs now appends --demuxer-lavf-o=rtsp_transport=udp+tcp whenever the source is rtsp://, so mpv negotiates UDP first and falls back to TCP. Fixes "Open in MPV" being unable to play RTSP streams without manual user config. Unit-tested in tests/player-runtime.test.ts.

Window state + custom title bar (desktop)

  • tauri-plugin-window-state is now registered on desktop; window position, size, and maximized state persist across launches.
  • Main window ships with decorations: false on desktop; set_shadow(true) keeps the drop shadow. New src/components/TitleBar.astro renders only when html[data-tauri-desktop] is set, so Android, iOS, and web still use the host chrome.
  • Title bar carries the brand mark + a data-tauri-drag-region strip + minimize / maximize / close window-control buttons. Close routes through the same CloseRequested event as the native X, so the close-to-tray behaviour from the new tray still applies.
  • Sidebar, CatalogWarmingIndicator, and the toast container had their top offsets / safe-area padding updated so they sit below the custom title bar on desktop without clipping or overlapping it.

System tray (desktop)

  • New src-tauri/src/tray.rs installs a tray icon + menu: Show / Hide window, quick navigation (Live TV, Movies, Series, Search, Guide, Downloads, Settings), and Quit (with CmdOrCtrl+Q accelerator).
  • Left-click toggles main-window visibility. Right-click opens the menu. Menu nav items emit xt:tray:navigate <route> which src/scripts/lib/tray-handler.ts resolves with a full document navigation (Astro is multi-page, so SPA-style routing isn't an option).
  • Close-to-tray: clicking the window's X (or Cmd+W / WM_CLOSE) hides the window rather than exiting. Quit is reached only via the tray menu / accelerator. This is the prerequisite for the upcoming programme-start reminders and any background-while-minimized features.

Native clipboard + notifications

  • New src/scripts/lib/clipboard.ts writes via @tauri-apps/plugin-clipboard-manager on Tauri (Windows / macOS / Linux via arboard, Android via ClipboardManager, iOS via UIPasteboard) and falls back to navigator.clipboard.writeText on web. Wired into the Stream-diagnostic "Copy report" button and Live TV's right-click "Copy stream URL". Bypasses the WebView gesture + permission restrictions that previously broke copy on mobile.
  • New src/scripts/lib/notify.ts routes through @tauri-apps/plugin-notification on Tauri so notifications land in the OS notification surface (Toast / Notification Center / NotificationManager / UNUserNotificationCenter) and uses the browser Notification API on web. Permission is requested lazily on first notify(...) call and cached. Wired into download-complete on both the desktop and Android download paths, with i18n strings downloads.notify.complete.title / .body / .bodyGeneric.

Settings page: "living instrument panel"

  • Settings re-grouped under semantic section ids (#settings-playlists, #settings-preferences, #settings-data, #settings-help, #settings-about, #settings-support, #settings-danger) with a sticky side-nav rail on lg+ viewports that scrolls into a horizontal pill row on narrow screens.
  • New src/scripts/settings/settings_effects.ts adds three layered interactions on top of the existing controls:
    • Morphing accent pill that springs between [aria-pressed="true"] buttons inside every [role="radiogroup"] via WAAPI + FLIP (font scale, theme, EPG default, channel column width, retention, perf mode...).
    • Scroll-spy rail behind the side nav: the active link is tracked from scroll position + hash, and a rail element morphs height + position with calm easing.
    • Per-card commit pulse: whenever a control commits a value, the host card (.icon-mark-host / settings-group children) emits a brief accent ring so the change is felt.
  • Reduced-motion and data-perf-mode="on" collapse all of the above to instant snaps - placement still works, just without the morph.
  • New Continue watching retention card (30 / 90 / 180 / Never) surfaced as its own settings panel.
  • A11y: side-nav exposes aria-label="Settings sections" (i18n'd), every settings link is keyboard-focusable, danger-zone link carries data-danger="true" for styling without colour-only signal.

WelcomeCard component

  • New src/components/WelcomeCard.astro - a reusable "no playlists yet" hero card used on / and /livetv, replacing the ad-hoc empty-state markup that lived inline in each page. Same hit targets, same i18n keys, single source of truth.

Layout polish

  • Bumped page max-width and adjusted poster dimensions on /movies/detail, /series/detail, /login, and /search so 1080p TVs no longer pin content to the left and detail-page posters scale predictably across breakpoints.
  • Various global.css tweaks for hit-target consistency (>=44px), focus-visible coverage on the new settings nav, and a few responsive paddings.

Splash polish

  • Comet canvas resize now derives offsets from the splash wrap rect + padding instead of the inner SVG's bounding rect. The previous logic mis-aligned the comet trail on certain DPI / window sizes when the SVG layout shifted relative to the canvas.
  • userToPx scales off Math.min(contentW, contentH) so the orbit stays inside the visible square regardless of aspect.

Misc

  • App version bumped to 1.5.0 in package.json and src-tauri/tauri.conf.json.
  • toast.js migrated to TypeScript (toast.ts); call sites untouched.
  • Locale message cache key bumped to invalidate stale pre-paint caches now that ~50 new keys land.
  • todo.md: extended programme-reminders entry with system-tray plan.
  • New tests/epg-data.test.ts covering mapping / name-normalisation edge cases.

1.5.0 release polish

Late fixes caught while testing the release branch on Android:

  • EPG on Android: Xtream xmltv.php body streams through tauri-plugin-http very slowly on Android. The 20s default fetch timeout aborted the body read mid-stream and EPG never populated. Bumped the EPG-specific timeout to 90s, and switched gzip decompression to use an explicit ReadableStream so it doesn't rely on Blob.prototype.stream() (missing on some Android WebView builds).
  • Settings scroll-spy: Opening /settings nudged the route hero off-screen on every load because setActive() wrote #settings-playlists to the URL on initial render. Initial entry no longer touches the URL; the hash is only written on explicit nav-link click. Also fixed the active highlight sticking on Android by switching pickActive() from offsetTop to getBoundingClientRect().
  • Download folder display on Android: showed raw SAF JSON ({"uri":"content://..."}). Promoted the existing prettifier in settings.astro into a shared android-fs.js export and reused it on the downloads page.
  • Android dev startup: tauri android dev panicked at boot because android_logger and tauri_plugin_log both tried to claim the global log slot. Skipped the log plugin on Android (android_logger already routes to logcat).
  • Android navigation perf: Deferred SpatialNavigation.init/add/makeFocusable to requestIdleCallback with a first-arrow-keydown fallback, so touch-only Android navigations don't pay the 200-500ms per-page tabindex-walk cost. Switched CatalogWarmingIndicator from client:load to client:idle for the same reason.

refactor: improve splash comet setup logic and enhance canvas resizing
@infinitel8p infinitel8p linked an issue May 12, 2026 that may be closed by this pull request
1 task
@infinitel8p infinitel8p changed the title Update version and improve splash comet setup logic v1.5.0: Update version and improve splash comet setup logic May 12, 2026
- Removed deprecated category management functions and replaced them with a new category picker implementation.
- Updated event listeners to utilize the new category picker for handling category changes.
- Simplified category filtering logic by integrating the picker’s filtering capabilities.
- Cleaned up unused variables and functions related to category management.
- Enhanced the UI for category selection with updated CSS styles for better visibility and interaction.

closes #62
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

i18n key check

  • ar.json: up to date
  • de.json: up to date
  • es.json: up to date
  • fr.json: up to date
  • hi.json: up to date
  • id.json: up to date
  • it.json: up to date
  • ja.json: up to date
  • nl.json: up to date
  • pl.json: up to date
  • pt-BR.json: up to date
  • ru.json: up to date
  • tr.json: up to date
  • ur.json: up to date
  • zh.json: up to date

Missing keys fall back to English at runtime - this is informational, not a failure.

User-facing
- Per-playlist primary EPG URL override and additional waterfall sources
  on /login. New "Test sources" button validates without saving; new
  "Don't use the provider's default EPG" toggle skips auto-detect.
- "Map channels" dialog on /epg lets users manually map a channel to any
  XMLTV entry when auto-match misses. Picker shows display-name + tvg-id
  + programme count, with a current-selection card and "Clear override".
- Auto-name fallback: channels without a tvg-id (or with a tvg-id that
  doesn't match the XMLTV) now match by normalised display-name with
  HD/FHD/UHD/4K/SD suffix stripping and separator-insensitive compare,
  so "MDR Sachsen HD" auto-resolves to "MDR Sachsen", "Channel 21 HD"
  to "Channel21".
- M3U Live TV side panel now reads from the loaded XMLTV instead of the
  hardcoded "No EPG available for M3U source" stub.

Plumbing
- Multi-source fetch is parallel via Promise.allSettled; per-URL
  conditional GET (If-Modified-Since/ETag) + per-URL IDB cache so a
  single 304 still benefits the merge.
- Magic-byte gzip sniff (1F 8B) on every response, in addition to URL
  ext and Content-Type heuristics.
- Force tauri-plugin-http for EPG fetches so the desktop / Android
  builds bypass browser CORS on arbitrary XMLTV hosts.
- O(1) name lookup via a per-playlist nameIndex built once per EPG load.
- Virtualised mapping list (~30 of 53k rows in DOM), debounced search,
  memoised tvg-id resolution.

Other
- ~50 new i18n keys translated to all 16 locales.
- Locale message cache key bumped to v2 to invalidate stale pre-paint
  caches.
- todo.md: extended programme-reminders entry with system-tray plan.

Closes #63, closes #64
@gitguardian
Copy link
Copy Markdown

gitguardian Bot commented May 12, 2026

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@infinitel8p infinitel8p linked an issue May 12, 2026 that may be closed by this pull request
1 task
- Introduced UI elements for managing backup domains in the login page.
- Implemented functionality to read and validate backup domain credentials.
- Enhanced the Xtream API fetch mechanism to support automatic failover to backup domains.
- Updated translations for backup domain features in Urdu and Chinese.
- Refactored existing API calls to utilize the new xtreamApiFetch method for improved error handling and failover logic.

ref #63
@infinitel8p infinitel8p changed the title v1.5.0: Update version and improve splash comet setup logic v1.5.0: custom EPG sources, channel mapping, shared category picker, backup domains May 12, 2026
@infinitel8p infinitel8p mentioned this pull request May 12, 2026
1 task
- Updated the playlist URL validation to accept any http(s) URL that serves an M3U playlist, making the extension optional.
- Modified localization strings across multiple languages to reflect the new validation criteria for playlist URLs.
- Enhanced the login page to display the updated helper text for playlist URL input.
- Implemented changes in the catalog and player runtime scripts to support the new URL validation and improve container detection for streaming.

closes #65
@infinitel8p infinitel8p changed the title v1.5.0: custom EPG sources, channel mapping, shared category picker, backup domains v1.5.0: custom EPG sources, channel mapping, category picker, backup domains, desktop tray + titlebar + notifications May 13, 2026
…ry management strings

feat(login): enhance focus management for spatial navigation

fix(epg): improve rendering and interaction for category picker with accessibility enhancements

fix(epg): optimize category selection and filtering logic

fix(epg): update category selection state handling and aria attributes

fix(epg): improve handling of document-level event listeners for category picker

fix(epg): clear caches on active playlist change

fix(epg): implement candidate fetch with timeout for Xtream API calls

fix(movies): remove unused render token variable

fix(series): remove unused render token variable
- Updated language files for multiple languages to include translations for new settings.
- Enhanced accessibility by adding ARIA labels for settings navigation.
- Improved layout and styling for settings page to enhance user experience.
@infinitel8p infinitel8p changed the title v1.5.0: custom EPG sources, channel mapping, category picker, backup domains, desktop tray + titlebar + notifications v1.5.0: custom EPG sources, channel mapping, category picker, backup domains, Xtream live format, desktop tray + titlebar + notifications, settings panel polish May 14, 2026
@infinitel8p infinitel8p self-assigned this May 14, 2026
@infinitel8p infinitel8p added enhancement New feature or request wip currently in development labels May 14, 2026
…ay and update downloads page to use it

feat(settings): refactor hash writing logic for better readability and maintainability
feat(layout): change CatalogWarmingIndicator to load on idle for performance optimization
feat(epg): enhance EPG response handling with improved stream processing and timeout support
@infinitel8p infinitel8p merged commit cede6fe into main May 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request wip currently in development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] TS xstream format support [Feature] Add custom EPG lists [Feature] EPG should display only selected categories

1 participant