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

resources/lib/bbc.py — BBC iPlayer resolver

Path /resources/lib/bbc.py Lines 71 Type playback resolver (clear DASH) ← called by default.py → calls BBC MediaSelector 6 + iPlayer episode-page scraper, setup_dash_item

Role

Resolves live TV channels (BBC One, Two, Three, Four, CBBC, CBeebies, News, Parliament, Alba, Scotland, S4C) and on-demand iPlayer episodes to DASH manifests. BBC streams have nodrm — these are clear DASH, so no license URL is involved.

BBC requires TV license

The plugin entry point in default.py has a _bbc_enabled() gate that reads the has_tv_license addon setting before allowing BBC content. See default.py:_resolve_channel line 119. This is purely advisory (an honour-system popup) — BBC's MediaSelector only geofences by UK-IP — but it's clearly the right UX choice if you're a UK developer shipping a Kodi addon.

Constants

MEDIASELECTOR_URL = "https://open.live.bbc.co.uk/mediaselector/6/select/version/2.0/mediaset/pc/vpid//format/json/cors/1" · line 7

The same MediaSelector 6 JSON endpoint that bbc.co.uk's own player uses. mediaset=pc is the desktop PC profile; mobile/tablet would also work but with lower max resolution.

CDN_PRIORITY = {"akamai":1, "llnw":2, "limelight":2, "bidi":3, "cloudfront":4} · line 9

Which CDN supplier is preferred when multiple DASH streams are returned. Akamai > Limelight/Limelight Bidi > Bidi > CloudFront. The choice is mostly historical; on tor (OSMC, UK-IP) Akamai tends to give the lowest start-up latency.

REDUX_RE = re.compile(r'window\.__IPLAYER_REDUX_STATE__\s*=\s*({.*?});\s*') · line 11

Regex that matches the inline JSON the iPlayer episode page writes into the page as a JS global. This is the same JSON the web player uses; we parse it to get the version list (each iPlayer episode has multiple "versions" for signed, audio-described, HD, etc.) — we pick the first one and use its id as the vpid for MediaSelector.

Resolution helpers

_get_vpid(pid) → str · line 17

For VOD episodes: takes the show's URL slug (e.g. m000abc1) and fetches the iPlayer episode HTML page, parses the __IPLAYER_REDUX_STATE__ JSON, picks the first version's id. This is needed because MediaSelector expects the version id (vpid), not the page slug. Throws "BBC: could not load episode page" or "BBC: no versions found" on parse failure.

_select_dash_stream(vpid) → str · line 30

For live channels the channel_id (e.g. bbc_one_hd) is the vpid. For VOD episodes, the vpid comes from _get_vpid. Hits MediaSelector, detects the "result":"geolocation" response and raises "BBC: Geoblocked" — there's no in-addon IP-tunnel fallback, so non-UK access just fails.

Iterates data["media"], picks media entries where kind starts with "video" (filters out audio-only). For each, looks at its connection entries, restricts to protocol == "https" + transferFormat == "dash", computes a CDN priority via the CDN_PRIORITY table, sorts ascending, returns the first (lowest-priority-rank) stream URL.

All-kind matching

MediaSelector returns many connection entries per media — only those with the exact kind "video_X" (where X is e.g. "ek360", "iptv-all") are considered. Audio-only connections (kind "audio" or "audio_video") are silently skipped — Kodi picks up audio tracks from the DASH manifest itself.

Resolvers

resolve(channel_id, listitem) · line 59 — for live

BBC live channels don't go through _get_vpid (the channel id is already the vpid). Calls _select_dash_stream and hands the manifest to setup_dash_item() with manifest_type="mpd" and no license URL (clear DASH).

resolve_vod(episode_id, listitem) · line 64 — for VOD

Powering the linked-to-from-the-library VOD play URLs. Two HTTP hops: _get_vpid(episode_id) → iPlayer page → MediaSelector. Heavy debug logging at lines 65/67/69 (every step writes to kodi.log) — useful when chasing intermittent failures. Hands off via setup_dash_item(), no license.