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 / __init__.py

resources/lib/__init__.py — shared helpers

Path /resources/lib/__init__.py Lines 112 Type shared helpers (no Kodi entry point) ← imported by every other module (plugin + service)

Role

The companion module that makes from resources.lib import work. Holds the cross-module constants (INPUTSTREAM_PROP, INPUTSTREAM_ADDON), the cached provider-map loader, the DASH listitem configurator, and a couple of small utility functions used everywhere.

Constants

INPUTSTREAM_PROP = "inputstream" · INPUTSTREAM_ADDON = "inputstream.adaptive"

The Kodi listitem property name and the addon ID of the inputstream.adaptive addon. Used by setup_dash_item() on every playable item; never re-string these in provider code.

_ADDON · _PROFILE · _PROVIDER_MAP_FILE · lines 11–13

Cached xbmcaddon.Addon("plugin.video.easyplaytv"), its writable profile directory, and the path to provider_map.json inside that profile. Used by load/save_provider_map().

Provider-map persistence

load_provider_map() -> dict · line 19

Reads provider_map.json from the addon profile dir. Caches the result in module globals keyed on the file's mtime — re-reads only if the file has been touched. Returns {} on any error, never raises.

The map's shape is {safe_title: {provider, f_name|pid|slug, ...}} and is used by library_sync.prefix_existing_tvshows() to retrofit the [PROVIDER] prefix onto rows that lack it.

save_provider_map(pmap) · line 37

Writes the map atomically (json.dump) and refreshes the in-memory cache. Silently swallows errors — losing the map is recoverable on the next sync.

Utilities

safe_filename(name) -> str · line 47

Replaces / \\ : * ? " < > | with spaces and strips. Used everywhere a Kodi-show title is going to become a folder name or a file table row's strFilename.

fetch_url(url, user_agent=None) -> str · line 53

The shared HTTP GET used by every scraper module. Sends Accept-Encoding: gzip, deflate, decompresses gzip transparently if the server used it. Returns a UTF-8 string (with errors="ignore" so encoding sniffing never breaks a fetch). Used by BBC, ITVX, C4, STV, Blaze scrapers.

This is NOT a persistent session

Each call opens a fresh urllib.request.urlopen with no cookie jar. Provider modules that need cookies (ITVX auth, My5 corona) manage their own requests.Session or http.cookiejar — see itvx.py for the only example.

is_inputstream_available() -> bool · line 63

Returns System.HasAddon(inputstream.adaptive) via Kodi's condition visibility. Used by default.py:resolve_live as a sanity check before attempting DASH playback (the resolver still short-circuits with a notification dialog if the addon's missing).

get_max_resolution() -> "WxH" | None · line 69

Reads the max_resolution addon setting (0/480/720/1080) and returns a Kodi inputstream.adaptive.chooser_resolution_max value or None for "no cap". Used by setup_dash_item() so the user's cap is enforced on every DASH manifest without each provider having to read the setting itself.

setup_dash_item — the DASH configurator

setup_dash_item(listitem, manifest_url, license_url=None, license_headers=None, license_payload=None, manifest_headers=None, stream_headers=None, manifest_type=None, timeshift=False, max_resolution=None) · line 82

The workhorse. Sets every inputstream.adaptive listitem property in the right shape for Kodi to hand off to the adaptive addon:

  • IsPlayable=true · inputstream=inputstream.adaptive
  • Resolution cap via get_max_resolution() unless caller overrides with max_resolution=
  • If license_url is set: license_type=com.widevine.alpha + a pipe-delimited license_key tuple assembled as license_url | license_headers | (license_payload or "R{SSM}") | (license_payload and "JBlicense" or "R"). The two terminal tokens tell IA whether to POST the payload as-is (JBlicense) or to use a raw challenge response (R).
  • Pass-through headers for manifest / stream fetches (e.g. STV's stv-drm: true header, ITVX's Authorization: Bearer …).
  • play_timeshift_buffer=true when caller requests timeshift=True (used by live channels that expose a rewind buffer).
  • listitem.setPath(manifest_url) as the final step.
Order matters

The resolution cap is set before the license fields — IA reads the chooser_resolution_max property at directory-list-build time, before it opens the manifest, so setting it after the license key has no effect. Don't reorder.

License payload format

When license_payload is provided (My5 Cassie case — see my5.py) the trailing token is JBlicense, meaning the payload is sent verbatim as the body. Without it, the trailing token is R and IA uses the {SSM} placeholder to substitute the Widevine service certificate into the request body. C4 and ITVX use the placeholder paths; My5 is the only one that pre-builds a payload.

Server/client role detection

is_master() -> bool · line 107 (removed in 3-package split)

Legacy: returned _ADDON.getSettingInt("db_source") == 0 to decide whether to schedule syncs (master) or skip them (follower). The master/follower role lived in the Service addon's db_source setting and is gone today — the Service addon is always a server, and client boxes configure pvr.iptvsimple via the video addon's PVR server URL setting instead. The Video addon's pvr_enabled flag now only hides its own Live TV menu item when a PVR server is configured.

get_ua — the shared User-Agent

get_ua() -> str · line 111

One Chrome 131 on Win64 UA string used by every scraper via fetch_url(url). Hard-coded rather than read from a setting because some providers (notably C4) UA-sniff and reject Roku/Android strings — keeping it desktop-Chrome-shaped avoids the sniff without exposing a knob.