feat(live-tv): support catch-up on M3U playlists - #216
Open
bilipp wants to merge 1 commit into
Open
Conversation
Catch-up was Xtream-only by construction: the capability gate tested `directURL == nil` as a de-facto "is Xtream" check, and the Xtream timeshift path was the only URL builder. M3U playlists that declare an archive per channel never surfaced one, even though other players play the same archive from the same playlist. M3UParser now reads the catchup attributes it already collected and discarded (`catchup`/`catchup-type`, `catchup-days` falling back to `timeshift` then `tvg-rec`, and `catchup-source`), and also accepts unquoted values, which real playlists emit. The scan stops at the display-name comma so a channel called `Sky F1=HD` cannot yield a bogus attribute — which also made the parser measurably faster than before, since the old scan walked the whole line. M3UCatchupURL builds the archive URL for all five dialects: flussonic (filename rewrite, with an explicit catchup-source winning over the convention), append, default/shift template expansion, and xc, which recovers credentials from the channel URL path because an m3u Playlist carries none. Timestamps are raw UTC epochs and durations seconds; the Xtream builder's wall-clock-and-minutes convention exists to satisfy XC panels and is reached only through the xc dialect. URLs are built from the raw directURL, bypassing the stream-format rewrite that would swap .m3u8 for .ts and 404 a Flussonic archive. Buildability is decided once at import and persisted as `tvArchive`, so a channel only badges when a URL can actually be produced and the UI never re-derives it during scroll. The capability rule was previously spelled three different ways across nine sites; it is now one shared predicate, so the badge and the tap can no longer disagree. A stored duration of 0 means "declared, depth unknown" and renders the day-less badge using the existing localized string — no new strings are added in this change. Closes #203
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Catch-up / archive playback only worked for Xtream Codes playlists. An M3U playlist that declares catch-up per channel never marked any channel as having an archive, so the Guide never offered a past programme — while other players play the same archive from the same playlist. This makes M3U channels behave like Xtream
tv_archivechannels: the clock affordance appears, the archive depth comes fromcatchup-days, and selecting a past programme builds a playable URL.Implementation
Parsing.
M3UParsernow reads the catchup attributes it already collected and discarded —catchup/catchup-type,catchup-daysfalling back totimeshiftthentvg-rec, andcatchup-source— and accepts unquoted values, which real playlists emit and the quoted-only scanner silently skipped. The attribute scan now stops at the display-name comma, so a channel namedSky F1=HDcan no longer yield a bogus attribute. That fix also made the parser 15.9% faster than before this branch: the old scan walked the entire line.LumePerformanceTests/README.mdrecords the measurement, including the module-placement confound that makes the naive before/after comparison misleading.URL building. New
M3UCatchupURLcovers all five dialects —flussonic/flussonic-hls/fs(filename rewrite, with an explicitcatchup-sourcewinning over the convention),append,default/shifttemplate expansion, andxc, which recovers credentials from the channel URL path because an M3UPlaylistcarries none andstreamIdis an FNV hash. Timestamps are raw UTC epochs and{duration}is seconds; the Xtream builder's wall-clock-and-minutes convention exists to keep XC panels from answering 400 and is reached only throughxc. URLs are built from the rawdirectURL, deliberately bypassingPlaylistStreamFormat.applied(to:), which swaps.m3u8⇄.tsand would 404 a Flussonic archive for anyone who set the playlist to MPEG-TS. Every dialect's result is validated to an absolute http(s) URL with a host.Two decisions worth reviewing. First, buildability is decided once at import and persisted as
tvArchive, so a channel only badges when a URL can actually be produced, and no view re-derives it —EPGChannelRowis a value snapshot precisely because SwiftData reads fault to SQLite on the main thread during scroll. Second, the capability rule was previously spelled three different ways across nine sites; it is now one shared predicate, so a badge and its tap can no longer disagree. Getting that ordering wrong would have shipped dead buttons on all four platforms, so the gate was converged before the importer began settingtvArchive.A stored
tvArchiveDurationof 0 means "declared, depth unknown" and renders a day-less badge reusing the existingCatch-up availablestring. No new localized strings are added. Xtream behaviour is unchanged throughout — the unknown-depth window applies only to M3U channels.Testing
xcodebuild test -scheme Lume -destination 'platform=iOS Simulator,name=iPhone 17 Pro') — 1076/1076--strictsilent)M3UCatchupURLTests(per-dialect exact-URL assertions, relative/protocol-relative/garbage-source rejection,canBuild/buildagreement), plus cases inM3UParserTests,M3USyncTests,PlayableMediaTests,EPGGridBuilderTestsandModelTestsVerified on a simulator against a playlist covering every dialect. All six catch-up cases badged with the right depth (
flussonic+7d,tvg-recflag+6d,default+source+3d,append+query+2d, unquoted days+4d),catchup="fs"with no day count rendered the glyph alone rather than "Catchup: 0d", and both controls — an unknown dialect and a channel with no attributes — correctly showed no affordance.Platforms
Related
Closes #203
Field report: #213 (closed as duplicate) — its four real-world spellings are all covered.
Builds on #25 / #88 (original Xtream-only catch-up) and #114 / #115 (catch-up from the EPG).
Known limitation, not addressed here:
EPGGuideViewhard-codes a 12-hour reach-back, so a channel badged for several days is still only scrollable back 12 h in the Guide. That is a pre-existing window-size issue and is being tracked separately.