[Skip to content](https://github.com/khalilbenaz/ultra-tv#start-of-content)

You signed in with another tab or window. [Reload](https://github.com/khalilbenaz/ultra-tv) to refresh your session.You signed out in another tab or window. [Reload](https://github.com/khalilbenaz/ultra-tv) to refresh your session.You switched accounts on another tab or window. [Reload](https://github.com/khalilbenaz/ultra-tv) to refresh your session.Dismiss alert

{{ message }}

[khalilbenaz](https://github.com/khalilbenaz)/ **[ultra-tv](https://github.com/khalilbenaz/ultra-tv)** Public

- [Notifications](https://github.com/login?return_to=%2Fkhalilbenaz%2Fultra-tv) You must be signed in to change notification settings
- [Fork\\
4](https://github.com/login?return_to=%2Fkhalilbenaz%2Fultra-tv)
- [Star\\
12](https://github.com/login?return_to=%2Fkhalilbenaz%2Fultra-tv)


main

[**1** Branch](https://github.com/khalilbenaz/ultra-tv/branches) [**37** Tags](https://github.com/khalilbenaz/ultra-tv/tags)

[Go to Branches page](https://github.com/khalilbenaz/ultra-tv/branches)[Go to Tags page](https://github.com/khalilbenaz/ultra-tv/tags)

Go to file

Code

Open more actions menu

## Folders and files

| Name | Name | Last commit message | Last commit date |
| --- | --- | --- | --- |
| ## Latest commit<br>![Khalil Benazzouz](https://github.githubassets.com/images/gravatars/gravatar-user-420.png?size=40)![claude](https://avatars.githubusercontent.com/u/81847?v=4&size=40)<br>Khalil Benazzouz<br>and<br>[claude](https://github.com/khalilbenaz/ultra-tv/commits?author=claude)<br>[resignRelease: opt out of configuration cache](https://github.com/khalilbenaz/ultra-tv/commit/1da9ab55ca59edea7c177773398d79138f1478b6)<br>Open commit detailssuccess<br>last monthJun 3, 2026<br>[1da9ab5](https://github.com/khalilbenaz/ultra-tv/commit/1da9ab55ca59edea7c177773398d79138f1478b6) · last monthJun 3, 2026<br>## History<br>[95 Commits](https://github.com/khalilbenaz/ultra-tv/commits/main/) <br>Open commit details<br>[View commit history for this file.](https://github.com/khalilbenaz/ultra-tv/commits/main/) 95 Commits |
| [.github/workflows](https://github.com/khalilbenaz/ultra-tv/tree/main/.github/workflows "This path skips through empty directories") | [.github/workflows](https://github.com/khalilbenaz/ultra-tv/tree/main/.github/workflows "This path skips through empty directories") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [android-app](https://github.com/khalilbenaz/ultra-tv/tree/main/android-app "android-app") | [android-app](https://github.com/khalilbenaz/ultra-tv/tree/main/android-app "android-app") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [android-native](https://github.com/khalilbenaz/ultra-tv/tree/main/android-native "android-native") | [android-native](https://github.com/khalilbenaz/ultra-tv/tree/main/android-native "android-native") | [resignRelease: opt out of configuration cache](https://github.com/khalilbenaz/ultra-tv/commit/1da9ab55ca59edea7c177773398d79138f1478b6 "resignRelease: opt out of configuration cache  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [cloudflare-config](https://github.com/khalilbenaz/ultra-tv/tree/main/cloudflare-config "cloudflare-config") | [cloudflare-config](https://github.com/khalilbenaz/ultra-tv/tree/main/cloudflare-config "cloudflare-config") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [docs](https://github.com/khalilbenaz/ultra-tv/tree/main/docs "docs") | [docs](https://github.com/khalilbenaz/ultra-tv/tree/main/docs "docs") | [v1.0.0 — UI redesign 2026](https://github.com/khalilbenaz/ultra-tv/commit/91025fd211a3a8b70742541cd1745fc972057536 "v1.0.0 — UI redesign 2026  Editorial AMOLED redesign of every screen + variant C launcher icon.  - Design tokens: accent #FF3A2F, Instrument Serif + Geist + Geist Mono - New shared components: ChannelLogo (hue-gradient), ContinueWatchingTile   (16:9 + progress), NowPlayingMini, UltraIcons (28 stroke icons),   HeroBanner (serif 84sp + accent eyebrow + CTA ring) - Chrome: SidebarNav collapsible 92↔220 with accent pill, TopBarNav with   search pill + clock/weather/bandwidth - Live TV: Tivimate 3-pane (categories | channels | preview window with   giant logo, LIVE chip, now/next cards, watch CTA) - Guide TV: 12h timeline with hour ruler, channel column with logo,   programme cards with EN COURS chip, NOW accent line - Search: 2-pane layout — on-screen keyboard 4×10 + recents | filter   chips + results grid (films/series/channels) - Home: editorial hero + NowPlaying mini column + Continue 16:9 + rails - Movies/Series: hero + rails with eyebrows, detail with serif 72sp +   meta accent - Settings: editorial header + section cards Surface1 + border - Onboarding: full-screen 3-step wizard with stepper + Cloud/Manual cards - Player: stats card mono with sain indicator - Launcher icon: variant C (red gradient + broadcast waves + play) - Removed Multi-View screen - README: new \"🎨 UI 2026 redesign\" section with home screenshot") | 2 months agoMay 21, 2026 |
| [electron](https://github.com/khalilbenaz/ultra-tv/tree/main/electron "electron") | [electron](https://github.com/khalilbenaz/ultra-tv/tree/main/electron "electron") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [web](https://github.com/khalilbenaz/ultra-tv/tree/main/web "web") | [web](https://github.com/khalilbenaz/ultra-tv/tree/main/web "web") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [.gitignore](https://github.com/khalilbenaz/ultra-tv/blob/main/.gitignore ".gitignore") | [.gitignore](https://github.com/khalilbenaz/ultra-tv/blob/main/.gitignore ".gitignore") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [CONTRIBUTING.md](https://github.com/khalilbenaz/ultra-tv/blob/main/CONTRIBUTING.md "CONTRIBUTING.md") | [CONTRIBUTING.md](https://github.com/khalilbenaz/ultra-tv/blob/main/CONTRIBUTING.md "CONTRIBUTING.md") | [Ultra TV — modern IPTV player for Web, Desktop and Android TV / Googl…](https://github.com/khalilbenaz/ultra-tv/commit/f9d2f05ea6a378e5296916a3788a9480180e0e82 "Ultra TV — modern IPTV player for Web, Desktop and Android TV / Google TV  Multi-platform IPTV client: - Web: React + TypeScript + Vite + IndexedDB (Dexie) + hls.js + shaka-player + mpegts.js - Desktop: Electron shell with native codecs (HEVC, AC3, MKV) — macOS, Windows, Linux - Android / Android TV / Google TV: Capacitor wrapper with custom ExternalPlayer plugin   that hands streams off to VLC / MX Player / Just Player / Next Player / ExoPlayer   for codecs the WebView can't decode  Supports Xtream Codes, M3U / M3U8 and Stalker Portal providers. Live TV with inline EPG (current/next program + progress), Movies and Series with rich detail pages (Xtream get_vod_info / get_series_info), category whitelist per content type (saves bandwidth on huge providers), drag-drop favorites, separate Watchlist, sort/filter (alpha/year/rating + genre + year range), Random pick, Home with Continue Watching / Top Rated / Recently Added / Live shelves, Cmd+K command palette, numeric TV-remote channel keys.  Player: HLS via hls.js with custom proxy loader (rewrites every segment URI), DASH via shaka-player, MPEG-TS via mpegts.js (lazy CDN), Picture-in-Picture, fullscreen, playback speed, audio/subtitle track selection, external .srt/.vtt upload, stats overlay (Shift+D), automatic codec/container fallback chain (.mp4 → .mkv → .ts → .mov…).  Multi-language UI (English / French / Arabic, RTL native), dark + light themes, collapsible sidebar, mobile-responsive layout, PWA installable with offline shell.  DVR via MediaRecorder + Origin Private File System. EPG reminders via the Notifications API. Catch-up playback for live channels that advertise it. Chromecast Web Sender + AirPlay handoff.  Includes four interchangeable CORS / User-Agent proxy implementations to choose from (Val Town paste-and-go, Vercel Edge Function, Cloudflare Worker, Deno Deploy) — pick whichever suits your hosting.") | 2 months agoMay 11, 2026 |
| [LICENSE](https://github.com/khalilbenaz/ultra-tv/blob/main/LICENSE "LICENSE") | [LICENSE](https://github.com/khalilbenaz/ultra-tv/blob/main/LICENSE "LICENSE") | [Ultra TV — modern IPTV player for Web, Desktop and Android TV / Googl…](https://github.com/khalilbenaz/ultra-tv/commit/f9d2f05ea6a378e5296916a3788a9480180e0e82 "Ultra TV — modern IPTV player for Web, Desktop and Android TV / Google TV  Multi-platform IPTV client: - Web: React + TypeScript + Vite + IndexedDB (Dexie) + hls.js + shaka-player + mpegts.js - Desktop: Electron shell with native codecs (HEVC, AC3, MKV) — macOS, Windows, Linux - Android / Android TV / Google TV: Capacitor wrapper with custom ExternalPlayer plugin   that hands streams off to VLC / MX Player / Just Player / Next Player / ExoPlayer   for codecs the WebView can't decode  Supports Xtream Codes, M3U / M3U8 and Stalker Portal providers. Live TV with inline EPG (current/next program + progress), Movies and Series with rich detail pages (Xtream get_vod_info / get_series_info), category whitelist per content type (saves bandwidth on huge providers), drag-drop favorites, separate Watchlist, sort/filter (alpha/year/rating + genre + year range), Random pick, Home with Continue Watching / Top Rated / Recently Added / Live shelves, Cmd+K command palette, numeric TV-remote channel keys.  Player: HLS via hls.js with custom proxy loader (rewrites every segment URI), DASH via shaka-player, MPEG-TS via mpegts.js (lazy CDN), Picture-in-Picture, fullscreen, playback speed, audio/subtitle track selection, external .srt/.vtt upload, stats overlay (Shift+D), automatic codec/container fallback chain (.mp4 → .mkv → .ts → .mov…).  Multi-language UI (English / French / Arabic, RTL native), dark + light themes, collapsible sidebar, mobile-responsive layout, PWA installable with offline shell.  DVR via MediaRecorder + Origin Private File System. EPG reminders via the Notifications API. Catch-up playback for live channels that advertise it. Chromecast Web Sender + AirPlay handoff.  Includes four interchangeable CORS / User-Agent proxy implementations to choose from (Val Town paste-and-go, Vercel Edge Function, Cloudflare Worker, Deno Deploy) — pick whichever suits your hosting.") | 2 months agoMay 11, 2026 |
| [README.md](https://github.com/khalilbenaz/ultra-tv/blob/main/README.md "README.md") | [README.md](https://github.com/khalilbenaz/ultra-tv/blob/main/README.md "README.md") | [Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt…](https://github.com/khalilbenaz/ultra-tv/commit/5985aa0709178a80aad4f1804efd93d8a89d7946 "Audit fixes: security hardening, Room migrations, CI/CD, codecs, debt cleanup (#1)  * Audit fixes: worker security hardening, Room migrations, player/recording bugs, web + electron robustness  cloudflare-config/worker.js: - opt-in protectReads per MAC: GET /api/config/:mac requires ?password= when enabled (dashboard toggle, session+CSRF) - fail-closed session secret (500 if SESSION_SECRET/ADMIN_PASSWORD missing) - constant-time compares for session/CSRF/password/token checks - exponential login lockout backoff, JSON.parse lockout guard - CSRF on raw-JSON POST /api/config/:mac - Referrer-Policy + nosniff headers; CRASH_TOKEN documented  android-native: - Room: exportSchema=true, schemaLocation, destructive fallback limited to v1-9 (no more DB wipe on future bumps) - fix VOD resume race (prepareResume now suspend, seekTo gets real value) - RecordingWorker: cancelled no longer overwritten by done; retryable vs terminal failures (\"error\" status, surfaced in UI) - replace runBlocking in PlayerScreen composition with produceState (ANR) - HlsRecorder: dedupe by media-sequence index (sliding playlist gaps + unbounded memory) - ReminderReceiver: goAsync + scoped job instead of GlobalScope - UpdateChecker: verify .sha256 sibling asset when published; drop dead versionCodeNormalised  web: - fix series whitelist bug (dead vodCats overwrite) — extracted filterCategories helpers + tests - top-level ErrorBoundary with TV-friendly fallback wired to diag buffer - proxy worker default-deny (ALLOWED_HOSTS mandatory) - EPG gzip magic-byte sniff + tests (vitest 9/9, tsc clean)  electron: - validate URL scheme before shell.openExternal (http/https only) - did-fail-load / render-process-gone / unresponsive fallback page - scope header rewriting to http(s), document webSecurity tradeoff - dev script sets SV_DEV via cross-env  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * Phase 2: PBKDF2, CI/CD, single-source versioning, castlabs Electron, web credential hygiene, debt cleanup  cloudflare-config/worker.js: - PBKDF2-SHA256 (100k iters, versioned pbkdf2$<iters>$<hex> format) replaces single-pass SHA-256 - backward compat: legacy hashes verified then transparently re-hashed on login - min password length 4 -> 8 (signup, change, UI hints, README)  CI/CD + versioning: - VERSION file as single source; build.gradle.kts derives versionName + versionCode (10029) - .github/workflows/ci.yml: web (tsc+vitest), android (compile+unit tests), workers (node --check) - .github/workflows/release.yml: tag==VERSION gate, ULTRA_* secrets with debug fallback, publishes UltraTV-debug.apk + .sha256  electron: - switch to castlabs fork pinned via GitHub tag v33.4.11+wvcus (HEVC/AC3/EAC3/Widevine); npm registry alias doesn't work (404) and semver ignores +wvcus metadata - electron/README.md (codecs, EVS signing notes, icon generation); build/ buildResources un-ignored  web: - stop persisting Xtream credentials in stored stream URLs; URLs rebuilt at play time from provider record (backup exports no longer leak credentials); 9 new URL-builder tests (vitest 18/18, tsc clean) - remove dead proxy code (proxiedStreamUrl, getProxy, LEGACY_DEFAULTS)  android-native: - StalkerClient: session token cached per provider (5 min TTL, invalidated on auth failure) — no more handshake on every zap - RemoteLog URL/token moved to BuildConfig fields (gradle prop/env overridable) - Search result opens the channel via the same player route as Live - remove unused Retrofit deps; drop redundant usesCleartextTraffic manifest flag  android-app: marked DEPRECATED (README), dead Firebase wiring + boilerplate tests removed  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  * CI: drop flaky setup-android action, use preinstalled SDK + chmod gradlew  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>  ---------  Co-authored-by: Khalil Benazzouz <k.benazzouz@b3gtech.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| [SECURITY.md](https://github.com/khalilbenaz/ultra-tv/blob/main/SECURITY.md "SECURITY.md") | [SECURITY.md](https://github.com/khalilbenaz/ultra-tv/blob/main/SECURITY.md "SECURITY.md") | [v1.0.20 — release-signing infra, CSRF, JUnit suite](https://github.com/khalilbenaz/ultra-tv/commit/6325f3290436250e98f47e3eff37b656aa3f9c0a "v1.0.20 — release-signing infra, CSRF, JUnit suite  Build / signing - New \"release\" signingConfig reads ULTRA_KEYSTORE / _PASSWORD /   ULTRA_KEY_ALIAS / _PASSWORD env vars and enables v1/v2/v3/v4   signature schemes. Falls back to the debug keystore when env vars   aren't set, so CI / dev checkouts still produce an installable APK. - SECURITY.md documents the full rotation procedure with apksigner   rotate + lineage so existing installs keep auto-updating across the   keystore swap (APK Signature Scheme v3, Android 9+).  Worker — CSRF - Stateless csrf token derived from the session cookie   (hmac(sessionSecret, sess + \".csrf\").slice(0,32)). All mutating   endpoints under /api/provider/, /api/password/, /api/config/ now   require it: the request's form body must carry the token in a   hidden \"csrf\" field. Mismatch → 403. SameSite=Lax already blocked   the obvious cross-site POST; this closes the lax-mode subdomain   and timing edge cases. - Dashboard HTML embeds the token in every <form> via a shared   csrfInput partial.  Tests - JUnit + Robolectric test setup wired into the Gradle config   (sourceSets[\"test\"].kotlin.srcDirs(\"src/test/kotlin\"),   testOptions.unitTests.isIncludeAndroidResources = true). - BackupCryptoTest covers round-trip, wrong-password rejection,   passthrough on plain JSON, ciphertext randomness (no IV reuse), and   the encrypted-but-no-password error path. Runs under   RobolectricTestRunner @Config(sdk=[34]) because android.util.Base64   needs the framework loader.  Backlog still open (documented in SECURITY.md / commit history) - Generating + lineage-rotating the real upload key — must happen   outside this repo so the keystore + passwords never land in git. - File splits for PlayerScreen.kt / LiveScreen.kt — mechanical, no   runtime change, picking the right moment for the diff churn. - Full i18n migration of the redesign's hardcoded strings.") | 2 months agoMay 23, 2026 |
| [VERSION](https://github.com/khalilbenaz/ultra-tv/blob/main/VERSION "VERSION") | [VERSION](https://github.com/khalilbenaz/ultra-tv/blob/main/VERSION "VERSION") | [v1.0.30](https://github.com/khalilbenaz/ultra-tv/commit/46994088a43344ccc3b2b76350cb04405fe7be37 "v1.0.30  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>") | last monthJun 3, 2026 |
| View all files |

## Repository files navigation

# Ultra TV

[Permalink: Ultra TV](https://github.com/khalilbenaz/ultra-tv#ultra-tv)

[![Ultra TV](https://github.com/khalilbenaz/ultra-tv/raw/main/docs/redesign/logo.svg)](https://github.com/khalilbenaz/ultra-tv/blob/main/docs/redesign/logo.svg)

**Native Android TV / Google TV IPTV player.**

Kotlin · Jetpack Compose · Compose-TV · Media3 · Room · Hilt

[![Download APK](https://camo.githubusercontent.com/cde48a31b73bb97d0b612230e33eb3f97979fa1dfb1a0edf3a10bd300dc10780/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f446f776e6c6f61642d416e64726f6964253230545625323041504b2d3344444338343f7374796c653d666f722d7468652d6261646765266c6f676f3d616e64726f6964266c6f676f436f6c6f723d7768697465)](https://github.com/khalilbenaz/ultra-tv/releases/latest/download/UltraTV-debug.apk)[![License](https://camo.githubusercontent.com/8b13a07ee3d1c0dae75b88de5973f63725b9d1b8397d99c20ce965df19158bc8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d3032383463373f7374796c653d666f722d7468652d6261646765)](https://github.com/khalilbenaz/ultra-tv/blob/main/LICENSE)![](https://camo.githubusercontent.com/9fe0574164f8f3dd1d87eb12329ab7df47a520734764ea2f1b795643513e67d6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4b6f746c696e2d322e302d3746353246463f7374796c653d666f722d7468652d6261646765266c6f676f3d6b6f746c696e)![](https://camo.githubusercontent.com/50d8021b1b1ef4762e872e2e77dc89881869ddbcb9427f5df68eac4cf5cd51be/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f6d706f73652d2d54562d312e302d3432383546343f7374796c653d666f722d7468652d6261646765)![](https://camo.githubusercontent.com/ae3b612b04a37917f8ebe357e5a1c2e2b1189cc734e8385aeb19a3924033372c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d65646961332d312e352d4646364630303f7374796c653d666f722d7468652d6261646765)

* * *

## 🎨 New UI (2026 redesign)

[Permalink: 🎨 New UI (2026 redesign)](https://github.com/khalilbenaz/ultra-tv#-new-ui-2026-redesign)

Full editorial redesign — AMOLED-first, accent rouge `#FF3A2F`, typo **Instrument Serif** pour les titres + **Geist** / **Geist Mono** pour le reste.

[![Home — hero éditorial + rails Netflix-style](https://github.com/khalilbenaz/ultra-tv/raw/main/docs/redesign/home.png)](https://github.com/khalilbenaz/ultra-tv/blob/main/docs/redesign/home.png)

| Écran | Description |
| --- | --- |
| **Home** | Hero éditorial 720 dp · serif 84 sp · eyebrow accent · colonne "En direct maintenant" · rails Continue Watching 16:9 + Films / Séries / Chaînes |
| **Live TV** | Layout Tivimate 3 panes : catégories (accent pill) · chaînes (numérotation mono, logo dégradé hue) · **fenêtre preview** avec logo géant, chip `EN DIRECT`, cards now/next, CTA Regarder |
| **Films / Séries** | Hero featured + rails Netflix par catégorie · focus Apple-TV (scale 1.08 + ring accent) |
| **Detail** | Affiche 320×480 · serif 72 sp · meta accent · `94% match` · épisodes mono `S01E02` |
| **Guide TV** | Timeline 12 h × N chaînes · ruler mono · ligne `NOW` accent · cards programme avec chip `EN COURS` |
| **Recherche** | 2 panes : clavier d'écran 4×10 + récentes à gauche · chips de filtres + grille de résultats à droite |
| **Player** | Overlays minimaux : top bar transport, bottom controls big play + ring accent, **stats card** mono, **EPG drawer** zapping |
| **Settings** | Tabs sidebar 300 dp · section cards Surface1 + border · MAC card gradient accent · toggle, pills, color swatches |
| **Onboarding** | Wizard 3 steps · stepper accent · option Cloud (recommandée) vs Manuel · QR code stub |

Multi-View screen was removed in v1.0.0 — too few users, awkward focus on a single remote.

### Logo

[Permalink: Logo](https://github.com/khalilbenaz/ultra-tv#logo)

[![Ultra TV — logo variant C](https://github.com/khalilbenaz/ultra-tv/raw/main/docs/redesign/logo.svg)](https://github.com/khalilbenaz/ultra-tv/blob/main/docs/redesign/logo.svg)

Logo "variant C" : dégradé rouge `#FF3A2F → #7A0E08`, trois ondes de diffusion qui émanent d'un point en bas à gauche, triangle play plein à droite. Installé en tant que `ic_launcher` adaptive + banner Android TV (320×180).

* * *

## What is Ultra TV?

[Permalink: What is Ultra TV?](https://github.com/khalilbenaz/ultra-tv#what-is-ultra-tv)

Ultra TV is a **fully native** Android TV IPTV client. D-pad navigation is handled by Compose-TV's focus tree (no WebView bridges), playback uses Media3 / ExoPlayer for native codec support, and the whole catalog (channels, movies, series, EPG, history, favorites) lives in a local Room database. It speaks **Xtream Codes**, **M3U / M3U8** (URL or local file), and **Stalker Portal** out of the box.

A companion **Cloudflare Worker** (in `cloudflare-config/`) provides a MAC-based remote-config dashboard so users can provision their providers from a web browser and have the TV pull them in one click.

## Features

[Permalink: Features](https://github.com/khalilbenaz/ultra-tv#features)

### Catalog & providers

[Permalink: Catalog & providers](https://github.com/khalilbenaz/ultra-tv#catalog--providers)

- 🎬 **Xtream Codes** · **M3U URL** · **M3U file from local storage** · **Stalker Portal** with Live + VOD + series catalogues (MAC handshake + lazy `create_link` at play time, including movies)
- 🔁 **Multi-provider** — add as many as you want, pick the default in Settings (★ Default badge)
- 🚦 **De-duplication** — re-adding the same `(kind, url, username)` reuses the existing row instead of duplicating
- 🛰️ **Cloud sync via Cloudflare Worker** — paste your device MAC into the dashboard (login + password), add providers, then the app pulls them with one tap. App reads are anonymous (the MAC, hashed from `ANDROID_ID`, is the bearer); only dashboard mutations require the per-MAC password.
- ⏱️ **Background sync via WorkManager** (every 6 / 12 / 24 h, or every launch)
- 📈 **Live sync progress banner** pinned to the top of every screen during sync

### Live TV

[Permalink: Live TV](https://github.com/khalilbenaz/ultra-tv#live-tv)

- 📺 **Tivimate-style two-pane layout**: categories on the left, channels of the selected category on the right
- 🔢 Channel position numbers, logos, focus highlight, **now-playing + next-up programme** under each name (from cached EPG)
- 🏷️ **Categories management** (search, bulk Hide / Show, "Hide adult" preset, 🔒 / 🔞 markers)
- 🧹 Cleans decorative wrappers (`### FRANCE ###` → `FRANCE`) for display while keeping DB intact

### Movies / Series

[Permalink: Movies / Series](https://github.com/khalilbenaz/ultra-tv#movies--series)

- 🎞️ **Netflix-style rails by category** (top 25 per rail), hero banner with the featured title
- 🟦 Focus scale animation (1.0 → 1.08, 160 ms tween)
- 🔍 Cross-content **search** (debounced 220 ms): channels + movies + series, with **last-10 recent queries** as one-tap chips
- ★ **Favorites** (per kind, browsable from a dedicated screen)
- 📚 **Series episodes** loaded on demand — Xtream via `get_series_info` (per-season map), Stalker via `get_ordered_list?category=…`; played through Media3 (Stalker episodes resolve their `stalker://` URL via `create_link` at play time, exactly like channels and movies)

### Player

[Permalink: Player](https://github.com/khalilbenaz/ultra-tv#player)

- ▶ **Media3 / ExoPlayer** — HLS, DASH, MPEG-TS, MP4 with hardware codec support
- 🎮 D-pad: BACK = exit, plus **Live**: ▲/▼ zap channels in the current category; **VOD**: ◀/▶ seek
- 🎚 **Subtitle + audio track selector** (VOD only) — reads Tracks from Media3, applies a TrackSelectionOverride
- 📋 **EPG drawer overlay** (Live only) — press OK / center to slide in a right-side channel list with now/next; D-pad picks a channel to zap to
- 📡 **Chromecast button** in the player toolbar — opens the system Cast picker when Google Play Services are available (silent no-op on Cast-less Android TV builds)
- ⏸️ **Continue watching** (position recorded every 10 s + on dispose)
- 🚀 **Auto-play last watched on launch** option
- 🥷 **Open in external player** (VLC / MX / Just Player / Next Player) for codecs Media3 can't handle
- ⏺ **Record VOD** — from a movie's detail page, queue an OkHttp-backed download via WorkManager; progress visible on a Recordings screen; played locally once done (no external storage permission — saved under app-private external storage)
- 📐 **Aspect & speed** controls in the player toolbar — Fit / Fill / Zoom / 16:9 / 4:3 for picture, 0.5× / 1× / 1.25× / 1.5× / 2× for VOD playback speed
- 💤 **Sleep timer** (15 min · 30 min · 1 h · 2 h · cancel) — pauses + exits player at the deadline
- 📊 **Stream stats overlay** — resolution / video & audio codec / frame rate / bitrate / buffer ahead, toggled from the player overlay

### Discovery / Home

[Permalink: Discovery / Home](https://github.com/khalilbenaz/ultra-tv#discovery--home)

- 🏠 Dynamic Home: **Continue watching** (tap an item → Resume / Dismiss sheet), **Recently watched**, **Movies**, **Series**, **Featured channels** rails
- 🆕 **First-time MAC card**: shows your device MAC + dashboard steps when no provider is configured
- 🗓 **TV Guide grid** (Tivimate-style): 12 h × N channels timeline with "now" indicator, refreshed from the provider's full `xmltv.php` feed (streaming pull-parser handles 50 MB+ feeds)
- ▦ **Multi-View**: up to 4 channels simultaneously in a 2×2 grid

### Personalization

[Permalink: Personalization](https://github.com/khalilbenaz/ultra-tv#personalization)

- 🎨 **3 themes**: Dark · AMOLED · Blue
- 📐 **Adaptive nav**: sidebar on tablets/TV (≥ 840 dp), top bar on medium widths (600–840 dp, also the user-selectable option in Settings), bottom bar on phones (< 600 dp). Phones / tablets ship from the same APK.
- 🌍 **Multi-language** UI: English / Français / Español / العربية + System (auto-detect). RTL layout direction flips automatically for Arabic. Translation table covers nav, home, settings and common buttons; the longer prose is still English for now.
- 🔄 **Boot autolaunch** — open Ultra TV automatically when the box finishes booting
- 🪟 **Picture-in-picture** — pressing Home while a stream plays shrinks the player into a corner (Android 8+)
- 🪜 **Onboarding wizard** on first launch — 3-step flow showing the device MAC and the two provider-adding paths
- 🔢 Show / hide channel numbers, hide adult categories beyond PIN, resume playback toggle, auto-play next episode

### Backup & state

[Permalink: Backup & state](https://github.com/khalilbenaz/ultra-tv#backup--state)

- 💾 **Export / restore** providers + favorites + watch history as a single JSON file (Storage Access Framework picker)

### Security

[Permalink: Security](https://github.com/khalilbenaz/ultra-tv#security)

- 🔐 **Parental PIN** (SHA-256, DataStore-backed) — auto-locks adult categories on each sync when a PIN is set
- 🔒 **Per-channel lock** — Settings → Manage locked channels lets you flag individual channels; play prompts for the PIN
- 🆔 **Stable per-device MAC** derived from `ANDROID_ID` (hashed) — never the real WiFi MAC

### Performance

[Permalink: Performance](https://github.com/khalilbenaz/ultra-tv#performance)

- 🖼️ **Coil ImageLoader** with 25 %-heap memory + 256 MB disk cache (no re-downloads on scroll)
- 📦 **Chunked DB inserts** (500 rows / batch) during sync — flat memory on huge catalogs
- ⚡ **DB indices** on `(providerId, categoryId)` for fast category filtering
- 🎯 SQL-level filtering for Live TV per category (only the visible subset materialises)
- 🧱 **R8 / ProGuard release build** with resource shrinking — **18 MB debug → 4.9 MB release** (incl. Google Cast SDK) (latest APK shipped is the release variant)
- 📑 **Paging Room** for Movies / Series flat-grid (pages of 60, only ~120 items in memory regardless of catalog size)

### Distribution & updates

[Permalink: Distribution & updates](https://github.com/khalilbenaz/ultra-tv#distribution--updates)

- 🇩 **Downloader code `5248504`** — initial sideload via the [Downloader app](https://www.aftvnews.com/downloader/) on any Android TV box.
- 🌐 GitHub Releases — latest APK at `releases/latest/download/UltraTV-debug.apk`.
- 🔄 **In-app self-update** (v1.0.5+) — the app pings GitHub Releases on launch, compares versionCode, and pops a dialog with a progress bar that downloads + installs the new APK via PackageInstaller. First update prompts once for "Install unknown apps"; subsequent updates are one-tap. No Play Store, no third-party updater required.

### Telemetry & crash reporting

[Permalink: Telemetry & crash reporting](https://github.com/khalilbenaz/ultra-tv#telemetry--crash-reporting)

- 🛰️ **Cloudflare Worker ingest** — every crash + ad-hoc `RemoteLog.info/warn/error/debug(...)` event is POSTed directly to the worker. No local crash.txt; no ADB pulls.
- 📒 **Crash dashboard** — `GET /crashes?token=…` returns an HTML page with expandable stack traces, device + version columns, 30-day rolling window.
- 🪵 **Event dashboard** — `GET /logs?token=…` table with level colouring (debug / info / warn / error), 7-day rolling window.
- 🔑 Token-gated via `env.CRASH_TOKEN` (fallback to `env.ADMIN_PASSWORD`). The app ships the URL + token baked in so every install reports automatically.

## Quick start

[Permalink: Quick start](https://github.com/khalilbenaz/ultra-tv#quick-start)

### Install on a TV box

[Permalink: Install on a TV box](https://github.com/khalilbenaz/ultra-tv#install-on-a-tv-box)

1. Install the **Downloader** app on your Android TV box from Google Play.
2. Open Downloader, enter code **`5248504`**, press **Go**.
3. Allow install from unknown sources when prompted; install the APK.
4. On first launch, you'll see a **First-time setup** card with your device MAC.
5. Either:
   - Open **Settings** → tap **\+ Xtream / + M3U URL / + M3U file / + Stalker portal** and fill in the form.
   - **Or** self-host the Cloudflare Worker, provision your MAC in its dashboard, then **Sync from cloud**.

From v1.0.5 onwards you only need Downloader for the _first_ install — the app auto-updates itself from GitHub Releases.

### Build from source

[Permalink: Build from source](https://github.com/khalilbenaz/ultra-tv#build-from-source)

```
git clone https://github.com/khalilbenaz/ultra-tv
cd ultra-tv/android-native

# JDK 17 is required (Android Gradle Plugin 8.7+)
export JAVA_HOME=$(/usr/libexec/java_home -v 17)   # macOS
./gradlew assembleDebug

# APK at app/build/outputs/apk/debug/app-debug.apk
```

For a smaller signed release build:

```
./gradlew assembleRelease
# ~8 MB APK at app/build/outputs/apk/release/app-release.apk
```

### Deploy the Cloudflare Worker (optional)

[Permalink: Deploy the Cloudflare Worker (optional)](https://github.com/khalilbenaz/ultra-tv#deploy-the-cloudflare-worker-optional)

```
cd cloudflare-config
npm i -g wrangler

wrangler kv:namespace create CONFIG             # paste id/preview_id into wrangler.toml
wrangler kv:namespace create CONFIG --preview
wrangler secret put ADMIN_PASSWORD              # strong password — dashboard login
wrangler secret put CRASH_TOKEN                 # optional: rotate the crash-report token away from the admin one
wrangler deploy
```

The Worker URL printed by wrangler is what you paste in the app's Settings → **Change** next to the Worker URL field, and is also the host of the crash + event dashboards (`/crashes?token=…`, `/logs?token=…`).

If you fork the project, swap the hard-coded `WORKER_URL` \+ `TOKEN` constants in `android-native/.../RemoteLog.kt` and `UpdateChecker.kt` so your installs report to _your_ worker, not the upstream one.

## Architecture

[Permalink: Architecture](https://github.com/khalilbenaz/ultra-tv#architecture)

```
android-native/
├── app/src/main/kotlin/com/ultratv/tv/nativeapp/
│   ├── MainActivity.kt         (entry point + nav host)
│   ├── UltraTvApp.kt           (Hilt + Coil + WorkManager Configuration.Provider)
│   ├── BootReceiver.kt         (BOOT_COMPLETED → MainActivity)
│   ├── data/
│   │   ├── db/                 (Room entities + DAOs)
│   │   ├── xtream/             (Xtream Codes player_api.php client)
│   │   ├── stalker/            (Stalker Portal handshake + create_link)
│   │   ├── m3u/                (M3U/M3U8 parser, URL or text input)
│   │   ├── repo/               (Provider / Catalog / History / PlaybackContext / SyncStatusBus)
│   │   ├── sync/               (WorkManager SyncWorker + SyncScheduler)
│   │   ├── parental/           (PIN store, SHA-256)
│   │   ├── prefs/              (UserPreferences, HiddenCategoriesStore)
│   │   └── config/             (DeviceMac, RemoteConfigImporter)
│   ├── di/                     (Hilt modules: DB / Network)
│   ├── nav/                    (Routes catalog)
│   ├── RemoteLog.kt            (direct-to-Worker crash + event transport)
│   ├── update/                 (UpdateChecker + UpdateDialog — GitHub Releases self-update)
│   └── ui/
│       ├── theme/              (DesignTokens, ultraCardColors, palettes: AMOLED / Dark / Blue)
│       ├── components/         (SidebarNav, TopBarNav, UltraIcons — 28 stroke icons)
│       ├── common/             (PosterCard, ContentRail, HeroBanner, ChannelLogo, ContinueWatchingTile, NowPlayingMini)
│       ├── home/               (rails + MAC onboarding card)
│       ├── live/               (Tivimate 3-pane: categories | channels | preview window)
│       ├── movies/             (Rails view + Detail)
│       ├── series/             (Rails view + Detail with episodes)
│       ├── guide/              (12 h × N timeline grid with NOW accent line)
│       ├── search/             (on-screen keyboard + filter chips + grid)
│       ├── favorites/
│       ├── categories/         (Hide / Show + bulk)
│       ├── player/             (Media3 PlayerView wrapper)
│       └── settings/           (editorial header + section cards + AddProviderDialogs)
└── cloudflare-config/          (Worker: KV-backed config per MAC + crash & event dashboards)
```

## Roadmap

[Permalink: Roadmap](https://github.com/khalilbenaz/ultra-tv#roadmap)

In active development / next iterations:

- 📊 **7-day xmltv** (current grid covers 12 h; longer window is a windowing change away)
- 🔍 **Full-text search index** (Room FTS4) — current LIKE is ok up to ~10k items
- 🧭 **Aggregated crash grouping** on the dashboard (currently one entry per occurrence; collapsing by stack fingerprint would scale better)

Recently landed:

- 🔓 **Anonymous worker sync (v1.0.8)** — the app no longer needs the per-MAC password to pull its config. The dashboard `/login` still gates mutations.
- 🎯 **Settings dialog focus (v1.0.8)** — text fields grab D-pad focus on dialog open and show an accent-tinted border so the cursor is visible.
- 🔁 **Update dialog loop fix (v1.0.8)** — local + remote versions are now compared on the same packed-semver scale; no more "update available" popping after every install.
- 🪟 **Auto-update via system installer (v1.0.6)** — switched from PackageInstaller sessions to `ACTION_VIEW` \+ FileProvider so the OS install activity handles the APK. Works on Fire TV, Mecool, vivo boxes that rejected the session path.
- 🩹 **Focus visibility + sidebar flicker (v1.0.7)** — `inverseOnSurface` flipped to near-black so TV Button/Card focus reads as a white pill with dark text instead of white-on-white. Sidebar labels gate on the animated width so returning via the left D-pad doesn't reflow text.
- 🎨 **2026 editorial redesign** of every screen — AMOLED-first, accent `#FF3A2F`, Instrument Serif + Geist typo, new variant-C launcher icon. See the _New UI_ section above.
- 🛰️ **Remote crash + event reporting** to a self-hosted Cloudflare Worker (`POST /api/crash`, `POST /api/event`); HTML dashboards at `/crashes` and `/logs`. No more `crash.txt` hunting.
- 🔄 **In-app GitHub-Releases auto-update** with a download progress bar + PackageInstaller commit. After v1.0.5 the Downloader code is only needed for the very first install.
- 🩹 **LiveViewModel init-order NPE fix** that was crashing any nav to Live TV on some devices (Main.immediate dispatch + property declared after init).
- 📥 **HLS-segment recording** for Live channels (m3u8 polling + .ts append).
- 🌐 **Deep i18n EN / FR / ES / AR** across every screen — Home, Live, Movies/Series, Settings (incl. private dialogs and SAF toasts), Preferences, Categories, Onboarding wizard, Guide list + grid, Add-provider dialogs, parental PIN flow, Recordings, Search, Player overlays plus the rail-title fallback. ~270 keys, RTL-aware.
- 👆 **Touch UX**: vertical-drag gesture overlays for system volume (right strip, `🔊 nn%`) and screen brightness (left strip, `☀ nn%`); **pull-to-refresh** on Home, Live TV, Movies, Series and the Guide grid. Both inert under D-pad, so TV remote behaviour is unchanged.

## Credits

[Permalink: Credits](https://github.com/khalilbenaz/ultra-tv#credits)

Ultra TV — original work by [khalilbenaz](https://github.com/khalilbenaz). MIT-licensed.

The native Android TV codebase supersedes the earlier Capacitor WebView build (kept under `android-app/` and `web/` for historical reference — legacy, no releases produced from it, see [`android-app/README.md`](https://github.com/khalilbenaz/ultra-tv/blob/main/android-app/README.md)) — Compose-TV's focus tree gave reliable D-pad navigation on every box we tested, including the Mecool KM7 Plus where the WebView bridge approach struggled.

If you fork / repackage, please keep the credit visible in the About screen.

## License

[Permalink: License](https://github.com/khalilbenaz/ultra-tv#license)

MIT. See [LICENSE](https://github.com/khalilbenaz/ultra-tv/blob/main/LICENSE).

## Disclaimer

[Permalink: Disclaimer](https://github.com/khalilbenaz/ultra-tv#disclaimer)

Ultra TV is an IPTV **client**, not a content provider. It does not include, host or distribute any stream. Use only playlists, EPG sources and credentials you are authorized to access in your jurisdiction.

## About

Modern IPTV player for Web, Desktop and Android TV / Google TV — Xtream, M3U, Stalker. HLS/DASH/MPEG-TS, EPG, DVR, Cast, multi-language.


### Topics

[electron](https://github.com/topics/electron "Topic: electron") [react](https://github.com/topics/react "Topic: react") [typescript](https://github.com/topics/typescript "Topic: typescript") [hls](https://github.com/topics/hls "Topic: hls") [chromecast](https://github.com/topics/chromecast "Topic: chromecast") [dash](https://github.com/topics/dash "Topic: dash") [epg](https://github.com/topics/epg "Topic: epg") [vlc](https://github.com/topics/vlc "Topic: vlc") [capacitor](https://github.com/topics/capacitor "Topic: capacitor") [android-tv](https://github.com/topics/android-tv "Topic: android-tv") [iptv](https://github.com/topics/iptv "Topic: iptv") [m3u](https://github.com/topics/m3u "Topic: m3u") [iptv-player](https://github.com/topics/iptv-player "Topic: iptv-player") [google-tv](https://github.com/topics/google-tv "Topic: google-tv") [xtream-codes](https://github.com/topics/xtream-codes "Topic: xtream-codes") [stalker-portal](https://github.com/topics/stalker-portal "Topic: stalker-portal")

### Resources

[Readme](https://github.com/khalilbenaz/ultra-tv#readme-ov-file)

### License

[MIT license](https://github.com/khalilbenaz/ultra-tv#MIT-1-ov-file)

### Contributing

[Contributing](https://github.com/khalilbenaz/ultra-tv#contributing-ov-file)

### Security policy

[Security policy](https://github.com/khalilbenaz/ultra-tv#security-ov-file)

### Uh oh!

There was an error while loading. [Please reload this page](https://github.com/khalilbenaz/ultra-tv).

[Activity](https://github.com/khalilbenaz/ultra-tv/activity)

### Stars

**12**
stars


### Watchers

**0**
watching


### Forks

[**4**\\
forks](https://github.com/khalilbenaz/ultra-tv/forks)

[Report repository](https://github.com/contact/report-content?content_url=https%3A%2F%2Fgithub.com%2Fkhalilbenaz%2Fultra-tv&report=khalilbenaz+%28user%29)

## [Releases\  37](https://github.com/khalilbenaz/ultra-tv/releases)

[v1.0.30\\
Latest\\
\\
on Jun 3Jun 3, 2026](https://github.com/khalilbenaz/ultra-tv/releases/tag/v1.0.30)

[\+ 36 releases](https://github.com/khalilbenaz/ultra-tv/releases)

## [Packages\  0](https://github.com/users/khalilbenaz/packages?repo_name=ultra-tv)

No packages published

## [Contributors\  2](https://github.com/khalilbenaz/ultra-tv/graphs/contributors)

- [![@khalilbenaz](https://avatars.githubusercontent.com/u/76207730?s=64&v=4)](https://github.com/khalilbenaz)[**khalilbenaz** Khalil](https://github.com/khalilbenaz)
- [![@claude](https://avatars.githubusercontent.com/u/81847?s=64&v=4)](https://github.com/claude)[**claude** Claude](https://github.com/claude)

## Languages

- [Kotlin60.5%](https://github.com/khalilbenaz/ultra-tv/search?l=kotlin)
- [TypeScript30.6%](https://github.com/khalilbenaz/ultra-tv/search?l=typescript)
- [JavaScript5.5%](https://github.com/khalilbenaz/ultra-tv/search?l=javascript)
- [CSS2.2%](https://github.com/khalilbenaz/ultra-tv/search?l=css)
- [Java1.2%](https://github.com/khalilbenaz/ultra-tv/search?l=java)

You can’t perform that action at this time.