Imports GitHub main at v0.8.0, including IPTV live-channel browsing, while preserving Gitea Actions, Python Semantic Release, Gitea-only container publishing, installer URLs, and runner networking.
User impact: Adds the v0.8.0 IPTV feature set.
Operator impact: Adds IPTV settings and local /data/iptv.sqlite3 storage; IPTV remains opt-in.
Breaking changes: None.
Tests: ruff check app tests; PYTHONPATH=app pytest -q (470 passed); git diff --check.
Imports GitHub main at v0.8.0, including IPTV live-channel browsing, while preserving Gitea Actions, Python Semantic Release, Gitea-only container publishing, installer URLs, and runner networking.
User impact: Adds the v0.8.0 IPTV feature set.
Operator impact: Adds IPTV settings and local /data/iptv.sqlite3 storage; IPTV remains opt-in.
Breaking changes: None.
Tests: ruff check app tests; PYTHONPATH=app pytest -q (470 passed); git diff --check.
* docs: add IPTV research and milestone roadmap
* feat: add IPTV source catalog
* feat: add IPTV channel browsing and playback
* docs: add IPTV operator guidance
* feat(iptv): modernize channel UI with the glass design system
Rebuild the IPTV shell to match the v0.7.0 glass system used by the
remote and Jellyfin surfaces, and tighten the browse UX.
- Reskin onto shared theme tokens: brand sky/blue accent (replacing the
bolt-on green), backdrop-blur glass panels/tabs/menus, sentence-case
tabs, launcher aligned with the Jellyfin launcher.
- Collapse the five-tab nav (My Channels/Favorites/Hidden/Sources/
Discover) to three (Channels/Sources/Discover); Favorites and Hidden
become a segmented view control inside Channels.
- Redesign channel tiles: whole logo+title area is the Play target, a
star toggles favorite, and secondary actions (Play next, Add to
queue, Hide/Show, Check, reorder) move into a per-tile overflow menu.
Controls sit in an in-flow column so no interactive elements nest.
- Add a live "On now" indicator on the last-played channel.
- Open Discover automatically on first run (no sources yet).
- Move the destructive "Remove unavailable" action out of the browse
toolbar into the Sources tab.
- Include iptv.css/iptv.js in the UI asset version stamp so IPTV asset
changes bust the browser cache.
Playwright-verified across dark/light/phone and all three tabs; smoke
script and pinned UI-markup tests updated to the new selectors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: keep live IPTV playback active during buffering
* fix: show stable live status for IPTV playback
* feat(iptv): restructure the channel shell into three focused pages
Rework the IPTV shell around three purpose-built surfaces so browsing,
finding, and managing channels each have a clear home:
- Channels: your catalog with a Channels|Favorite segmented toggle, the
now-playing tile highlighted, a Discover shortcut in the head and at the
foot of the list, and roomier tiles on desktop.
- Discover: browses the full catalog pulled from your sources with search,
a group filter, a Sources shortcut, and a Refresh control. Favoriting a
channel updates it in place and keeps your scroll position.
- Sources: an expandable "Add playlist" card, your sources (with Remove and
Remove-unavailable) on top, and the free provider directory folded in
below, with already-added providers marked.
Drops the separate Hidden view in favour of favorites-first curation, fixes
the overflow menu so it paints above neighbouring tiles (z-index) and lays
its items out icon-then-label (opting out of the global button span growth),
and shares now-playing/favorite state across the two channel grids.
Updates the Playwright smoke walk for the new IA (menu actions, Discover
channel browse, directory on Sources). 247 pytest pass; verified via remote
Playwright across desktop dark/light and phone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(iptv): link-driven channel shell with favorites-first My channels
Replace the tab bar with a link-and-back navigation model and simplify the
home surface:
- Remove the Channels/Discover/Sources tab nav. My channels is the home;
Discover and Sources are reached only via links (the Discover button/foot
link, and Discover's Sources shortcut) and left via a Back control on each
sub-page. Escape now steps back a level before closing the shell.
- Drop the Channels|Favorite toggle. My channels shows favorites in a
Favorites section floated to the top (fetched separately so they always
show), then All channels below; favoriting restructures the list.
- Modernize the search fields into taller glass pills with an accent focus
glow and a focus-lit search icon.
Updates the Playwright smoke walk (favorites-to-top, link + Back navigation)
and repins test_smoke.py to the favorites section selector. 250 pytest pass;
verified via remote Playwright across desktop dark/light and phone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(iptv): My Channels membership distinct from favorite pinning
Introduce an explicit "added to My Channels" membership so the front page is
a curated list, separate from favorite (pin-to-top):
- Add an `added` column to iptv_channels (backward-compatible ALTER for
existing DBs; defaults off so new catalog channels are not auto-added), an
`added_only` query filter, and `added` on the channel PATCH.
- My Channels (front page, now titled) shows only added channels — favorites
floated to a section on top, then the rest — and its star pins/unpins.
Favoriting is available only here.
- Discover shows the full catalog; each tile has a + (add to My Channels)
toggle, a whole-tile play target, and the overflow menu. Adding updates the
tile in place; the menu also offers Remove from My Channels on the front
page. No favorite control on Discover.
Updates the Playwright smoke walk (seed as added, exercise the + toggle,
assert Discover has no favorite). 250 pytest pass; verified via remote
Playwright across desktop dark/light and phone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* style(iptv): glassy channel cards in place of flat grey
Give channel cards a layered frosted-glass surface: a top-to-bottom
translucent gradient, an inset top-edge highlight, refined borders, and a
soft lift shadow — driven by theme-aware shell-scoped tokens so the manual
light/dark toggle keeps working. The logo tile and corner chips get the same
sheen; the now-playing card carries a brand-tinted glass wash. Also flattens
the play button so its inherited global-button shadow no longer casts a seam
across the card.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(readme): add IPTV live channels + My Channels phone mockup
Introduce a "Watch it live" capability blurb and a "Live TV, curated the way
you watch" screenshot row in "See it in action", with a My Channels phone
mockup matching the existing remote/library device shots.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(readme): reshoot IPTV mockup with the live iptv-org playlist
Regenerate the My Channels phone mockup against a real IPTV playlist
(iptv-org · English) so the tiles show real channel logos — Al Jazeera, Sky
News, Bloomberg, CBS News, Euronews, France 24 — instead of placeholders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): address Codex review findings on the channel UI
- Sync the "On now" highlight from the authoritative status on every poll, so
it is correct after reload, cross-client playback, or when playback stops —
not only after this page's own Play action.
- Stop favorites past the first page from vanishing in My Channels: fetch more
favorites and only drop the ones actually shown in the Favorites section from
the main list, so any beyond the cap fall through instead of disappearing.
- Make Discover's Refresh pull fresh channels from the enabled sources before
reloading the grid, matching the control's promise instead of only
re-reading the local catalog.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): address second Codex review pass on playback + My Channels
- Stop the interrupt-preserve path from writing credential-bearing IPTV stream
URLs to queue.json: persist only the opaque catalog reference and the source/
channel IDs, skip caching the resolved stream for IPTV, and let replay
re-resolve the stream and headers from the catalog. Adds a redaction test.
- Do not mark an IPTV channel "available" just because Play launched mpv;
launching is not proof the stream plays, so a dead or geo-blocked channel now
keeps its warning until real probes run.
- Show all added channels in My Channels (visibility=all) so favorites and
other members that go unavailable or lose their source still appear for the
user to retry or remove, instead of silently vanishing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): stop history from persisting credential-bearing stream URLs
- Carry the opaque IPTV catalog references into history entries so persistence
redacts the stream URL to iptv.invalid/<source>/<channel> and history replay
re-resolves the stream and headers from the catalog; skip caching the
resolved IPTV stream so no credentials reach history.json. Adds a test.
- Correct docs/IPTV_OPERATIONS.md: playing a channel no longer restores its
availability (only an explicit or scheduled check does), matching the code.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(player): stop first video's audio from persisting across queue advances
The Pi's subprocess mpv path spawned with a process-global --audio-file,
so every seamless-replace loadfile on the reused --idle=yes player
inherited the first video's external audio track and mpv kept selecting
it over each new video's own audio. Scope --audio-file and --start to
the first file with mpv's per-file --{ ... --} grouping in both spawn
arg builders (Qt shell subprocess + external mpv), so later loads define
audio purely via their per-file options. Also prevents a global resume
--start from seeking every subsequent load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(iptv): route IPTV and header-bearing items through play_item
mpv's direct up-next handoff appends a queued item's URL to mpv without
re-resolving it or installing per-channel headers. For IPTV that both leaked
the credential-bearing stream URL to history.json/session (the rebuilt
now_item dropped iptv_source_id/iptv_channel_id, so redaction could not fire)
and, for header-protected channels, dropped the http-user-agent/http-referrer
headers and failed with 401/403.
Make IPTV and any header-bearing queue item ineligible for the direct handoff
so they go through play_item, which re-resolves the opaque catalog reference
(restoring redaction) and applies the per-channel headers. Adds a test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): source-management correctness (Codex review pass)
- Recount iptv_sources.channel_count after Remove unavailable so status and the
Sources UI stop reporting a stale channel total.
- Exclude disabled sources from scheduled favorite checks: join iptv_sources
and filter enabled, so disabling a source actually stops its background
probing.
- Switch a pasted source to URL mode when a location is PATCHed in (clear
content and set kind=url) so refresh fetches the URL instead of failing on
now-empty pasted content.
- Keep channel identity stable when a duplicate tvg-id appears in a later
refresh: preserve the incumbent tvg identity so favorites, My Channels
membership, hidden state, and manual order are not detached.
Adds regression tests for all four.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(iptv): sync operator + API docs with the current IPTV UI
Update IPTV_OPERATIONS.md for the link-driven shell: My Channels is the added-
channel home with favorites pinned to the top (no separate Favorites/Hidden
views, no Include-unavailable toggle), Discover browses the full catalog with
+/Play/menu, and Sources hosts add-playlist plus the folded-in provider
directory. Note that My Channels keeps showing unavailable added channels, that
disabling a source stops its background checks, and that identity survives a
later duplicate tvg-id. Document the `added` PATCH field and `added_only`
channel filter in API.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: use install-relative paths instead of assuming /opt/relaytv
Reference the .env and data/iptv.sqlite3 files relative to the reader's RelayTV
directory rather than hardcoding /opt/relaytv, which is not the default install
location (the quick start uses ~/relaytv). Container paths (/data/iptv.sqlite3)
and the --dir install example are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(iptv): remove the planning/research doc now that IPTV has shipped
The IPTV catalog research and milestone-roadmap doc was planning-era material.
Its durable operator guidance lives in IPTV_OPERATIONS.md, the API surface in
API.md, and the design rationale in git history; its "Proposed UX" section no
longer matches the shipped link-driven UI. Nothing references it and it is not
in the docs index.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* ci(release): prepend an optional per-release highlight to release notes
The release workflow now prepends docs/release-highlights/<version>.md (when
present) above the generated GitHub Release notes, below the hero image and
under the existing overview marker. Seeds 0.8.0 with a heading introducing the
IPTV live-channels feature. Future releases can add their own highlight file;
releases without one are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs(agents): document the release-highlight mechanism and PR review step
Describe docs/release-highlights/<version>.md (prepended to the GitHub Release
notes by the release workflow) under Release and Changelog Discipline, and add
a pull-request preparation step to review whether a release warrants a
highlight heading.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(player): preserve grouped media args in the external-mpv builder
_build_mpv_args now emits the media as a per-file group
(--{ ...file-opts... url --}). _build_qt_external_mpv_args assumed the last
token was the bare stream URL, so with an external audio track or a resume
position it sliced off the closing --} and re-appended the URL, leaving an
unclosed --{ group and a duplicated media URL that mpv could misparse or queue
twice. Split off and re-append the whole file spec intact.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): resilient source add and URL repoint
- Keep a newly created source when its initial refresh fails: return success
with the retained source and a refresh_error instead of a 502, so the client
shows the added source (with its recorded error) rather than retrying and
creating duplicates. The add form surfaces the partial failure.
- Clear the stored ETag/Last-Modified conditional validators when a source's
location changes, so the new URL is fully fetched instead of being 304-skipped
against the old catalog.
Adds regression tests for both.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): SSRF guard, stale-queue skip, and duplicate-entry dedupe
- SSRF: on a token-less (open-LAN) deployment, only fetch playlist hosts that
resolve to public addresses, re-validating every redirect hop. A configured
RELAYTV_API_TOKEN gates source management to the operator, who may then
target private hosts.
- Queue: a queued IPTV channel whose source was deleted or refreshed to
inactive resolves to a 404; classify it as unplayable and skip it in every
mode instead of reinserting it at the queue front and blocking autoplay.
- Catalog: deduplicate playlist entries that resolve to the same channel_id so
channel_count matches the browsable catalog and no entry silently disappears.
Adds regression tests for all three.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): probe SSRF guard, identity/redirect/status hardening
- Apply the SSRF public-address guard (and guarded redirect opener) to the
channel availability Check as well, not just playlist downloads.
- Keep a channel's identity stable when a duplicate tvg-id disappears: a
now-unique entry whose name already holds a name: identity keeps it instead
of upgrading to tvg: and detaching membership/favorite/rank.
- Resolve relative channel URLs against the final (post-redirect) playlist URL
returned by _fetch_source, not the originally configured location.
- Stop reading the potentially 20 MB pasted content column during source
listing / status polling.
- Preserve runtime telemetry gaps only for live/IPTV sessions so a crashed VOD
player still demotes to idle instead of sticking in playing/buffering.
Adds regression tests for the probe guard, identity survival, and redirect-base
resolution.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(iptv): exclude disabled sources from Discover results
query_channels now filters discovery/browse queries (and the group list) to
enabled sources, so disabling a source removes its channels and groups from
Discover. My Channels (added_only) is unchanged: added entries from disabled
or inactive sources still appear so they can be retried or removed. Adds a
regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Imports GitHub main at v0.8.0, including IPTV live-channel browsing, while preserving Gitea Actions, Python Semantic Release, Gitea-only container publishing, installer URLs, and runner networking.
User impact: Adds the v0.8.0 IPTV feature set.
Operator impact: Adds IPTV settings and local /data/iptv.sqlite3 storage; IPTV remains opt-in.
Breaking changes: None.
Tests: ruff check app tests; PYTHONPATH=app pytest -q (470 passed); git diff --check.