feat(m3u): support #KODIPROP lines placed before #EXTINF (#1234)

* feat(m3u): support #KODIPROP lines placed before #EXTINF

Bumps the iptv-playlist-parser fork pin to v0.15.2-iptvnator.2: Kodi
property lines apply to the next list entry, so #KODIPROP lines placed
above the #EXTINF are now preserved in item.raw (previously the parser
dropped them and ClearKey config in that layout was lost). The DASH +
ClearKey feature (#1225) extracts license config from item.raw, so both
KODIPROP layouts now work on every import path.

Covered by a parser contract case and an extended web-backend /parse
regression (before-EXTINF + between-EXTINF-and-URL + plain channel).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: reflect before-#EXTINF KODIPROP support in the M3U architecture doc

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: list the KODIPROP delta in the parser-fork inventory

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 authored and GitHub committed 2026-07-25 11:43:58 +02:00
1 parent f852bc7459
commit 0ee73f2d0f
5 files changed
+70 -140

No files matched your search

+9 -6
View File
@@ -46,10 +46,11 @@ The M3U playlist module provides:
All four parse call sites (Electron `playlist-source.ts` import, `playlist-refresh.worker.ts`, `web-backend` `/parse`, PWA `playlists.service.ts`) use the
[4gray/iptv-playlist-parser](https://github.com/4gray/iptv-playlist-parser) fork, pinned by commit SHA in `package.json`. The fork tracks upstream
`freearhey/iptv-playlist-parser` (currently synced to v0.15.2) plus two deliberate deltas iptvnator depends on:
`freearhey/iptv-playlist-parser` (currently synced to v0.15.2) plus three deliberate deltas iptvnator depends on:
- **`radio` attribute** — `item.radio` (string, `'true'` triggers the radio player, EPG suppression, and external-player gating app-wide). Upstream does not have this field; it must survive every upstream sync.
- **Pipe stripping** — `item.url` is cut at the first `|`; `|User-Agent=` / `|Referer=` params still land in `item.http`. Upstream 0.15.0 stopped stripping, but iptvnator consumes `item.url` verbatim in hls.js/mpv/vlc, catch-up URL building, and url-keyed favorites.
- **`#KODIPROP` lines before `#EXTINF` are preserved** (since `v0.15.2-iptvnator.2`) — Kodi property lines apply to the *next* list entry, so ones placed above the `#EXTINF` are buffered and attached to that item's `raw` in file order (case-insensitive prefix); other stray `#` lines outside an open item are still dropped. The DASH + ClearKey feature extracts `inputstream.adaptive.license_*` config from `item.raw`, so this delta must survive every upstream sync.
There is intentionally **no URL validation** (upstream removed it in 0.15.0): any non-empty non-`#` line after `#EXTINF` becomes the item URL. This is what fixes issue #1189 (Pluto TV JWT URLs longer than validator's 2084-char IE-era limit used to be rejected, and the stalled item index collapsed the whole playlist into one channel). `#` comment lines and unknown directives are appended to `item.raw` and never treated as URLs.
@@ -745,11 +746,13 @@ player in settings.
**DRM data flow** (M3U module only; Xtream/Stalker have no DRM concept):
1. The playlist parser fork does not understand `#KODIPROP:` lines, but keeps
every unknown line between `#EXTINF` and the stream URL in `item.raw` (the
dominant Kodi/TiviMate layout; `#KODIPROP` lines _before_ `#EXTINF` are
dropped by the parser — fixing that requires a parser-fork patch and is
deferred).
1. The playlist parser fork does not interpret `#KODIPROP:` lines, but
preserves them in `item.raw` for **both** layouts: unknown lines between
`#EXTINF` and the stream URL are kept as before, and since parser pin
`v0.15.2-iptvnator.2` `#KODIPROP` lines placed *before* the `#EXTINF` are
buffered and attached to the **next** entry's `raw` in file order (Kodi
semantics, case-insensitive prefix). Other stray `#` lines outside an open
item are still dropped, matching upstream.
2. `extractDrmFromRaw()` (`libs/shared/m3u-utils/src/lib/kodiprop.utils.ts`)
post-processes `raw` inside `createPlaylistObject()` — the single funnel
for all four import paths (Electron URL/file import, refresh worker,