Disclaimer: This documentation is provided for convenience and may contain errors. Always verify information against the official Kodi and provider documentation. Report issues.
Fastmail
Home / Files / stv.py

resources/lib/stv.py — STV live resolver

Path /resources/lib/stv.py Lines 31 Type playback resolver (DASH + (optional) Widevine) ← called by default.py:_resolve_channel → calls STV player API, setup_dash_item

Role

Resolves the two STV live channels — STV (the Scottish ITV affiliate) and STV +1 — to a playable manifest. The STV player API returns multiple streams targeted at different devices; this module picks the best one and configures the listitem appropriately (DASH vs HLS).

Constants

LIVE_API = "https://player.api.stv.tv/v1/channels//" · line 5

The single endpoint. STV is one of the few UK providers with a tidy public channel-info API.

HEADERS = {"User-Agent": "", "stv-drm": "true"} · line 7

Two non-obvious headers:

  • "stv-drm": "true" — tells STV's API to serve the DRM-protected DASH manifest (with embedded Widevine PSSH) rather than the clear-AVC mobile stream. setup_dash_item() then receives a manifest whose Widevine LICENSE URL is inside the MPD itself, so we don't explicitly pass a license_url.
  • User-Agent: "" — empty on this dict; the resolver populates it with get_ua() at runtime.
DEVICE_PRIORITY = {"fvp_dash": 0, "desktop": 1, "mobile": 2} · line 9

Order of preference among the streams[*].device values. fvp_dash (Freeview Play) is the highest-resolution stream STV exposes; desktop is the web tier; mobile is the least-preferred fallback. If none of the known device names match, min(... key=DEVICE_PRIORITY.get(s, 99)) uses 99 → that unknown stream ends up last but is still selected if it's the only one.

Resolver

resolve(item_id, listitem) · line 12

Note one quirks upfront: line 13 rewrites stv_plusone to stv-plus-1 — these are the two channel_ids the addon exposes, but STV's API expects "stv-plus-1" for the +1. Then:

  1. Build headers from the HEADERS template with a populated UA.
  2. HTTP GET the LIVE_API.
  3. Pick the stream with the lowest device-priority via min(streams, key=…).
  4. Inspect the streamUrl: if it ends in .m3u8 or contains .m3u8?, treat as HLS; otherwise MPD.
  5. HLS path: bypass setup_dash_item and set the four listitem properties manually (setPath, inputstream, inputstream.adaptive.manifest_type=hls) — because setup_dash_item defaults to DASH license handling, which isn't appropriate for the simple HLS case.
  6. DASH path: call setup_dash_item with manifest_type="mpd", no license URL (Widevine info is in the MPD itself, picked up by inputstream.adaptive's PSSH parser).
STV live VODs are silent on errors

If data.results.streams is empty, the resolver raises "STV: No streams found". The caller in default.py:_resolve_channel wraps in try/except and shows a notification dialog, so the user gets "STV: No streams found" as a popup. There's no separate error-code path for "outside UK geofence" — STV just returns zero streams in that case, so the failure mode is identical to "service is down".