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_vod.py

easyplaytv-service/…/providers/bbc_vod.py — BBC iPlayer catalogue

Moved: now lives in the standalone service runtime

Since the 3-package split this catalogue scraper ships in the shared runtime at easyplaytv-service/src/easyplaytv_service/vod_sync/providers/bbc_vod.py (and the vendored copy inside service.easyplaytv). The video addon is playback-only and no longer contains a bbc_vod.py.

Path easyplaytv-service/src/easyplaytv_service/vod_sync/providers/bbc_vod.py Lines 191 Type catalogue scraper (HTML + redux-state JSON) ← called by library_sync.py via _provider_module("bbc_vod") (sync episode fetch), providers/__init__.py list_shows("films", 1) → calls bbc.co.uk iplayer HTML pages

Role

Scrapes BBC's iPlayer category/episode pages. Each iPlayer HTML page embeds a JSON blob in window.__IPLAYER_REDUX_STATE__ (the same data property their React app builds on); we parse that JSON directly and turn it into the addon's house dict shape.

Constants

BASE_URL = "https://www.bbc.co.uk/iplayer" · line 13

Root of all iPlayer pages.

CATEGORIES = [("films","Films"), ("drama-and-soaps","Drama & Soaps"), …] · lines 15-28

14 paired slugs/labels. Slugs are BBC's own URL components; labels are display strings. Cross-referenced by genres.py's UNIFIED_GENRES map so the addon's "Drama" tile pulls BBC's drama-and-soaps slug.

IMG_RECIPE_THUMB = "320x180" · IMG_RECIPE_FANART = "1920x1080" · lines 32-33

BBC's image URLs have {recipe} placeholders for size selection; these are the two recipes we substitute.

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

Matches the inline JSON object that powers iPlayer's own UI. Same regex as bbc.py:_get_vpid; duplicated because both modules scrape the same JSON — the/.. BBC scraper is just one-stop.

Helpers

_fetch_html(url) · line 41

Standard gzip-aware HTML fetch with iPlayer's standard HTML accept headers.

_extract_redux(html) → dict | None · line 55

Searches the page for the REDUX_RE regex, json.loads the blob. Returns None on no match.

_img_url(template, recipe) · line 62

Substitutes {recipe} in a BBC image template with one of "320x180" / "1920x1080".

_parse_subtitle_episode(subtitle_text) · line 105

Parses a BBC episode subtitle like Series 3 · Episode 5 and returns (series_number, episode_number). Used by list_episodes when the page doesn't carry numeric series/episode fields directly.

Listing functions

list_categories() · line 66

Returns 14 {id, title} dicts from the hardcoded CATEGORIES — doesn't hit the network.

list_shows(category_id, page=1) → tuple[shows, total_pages, cur_page] · line 70

Fetches https://www.bbc.co.uk/iplayer/categories//all?page=, extracts the redux state, walks state["categories"]["elements"]. Each show dict has the standard addon shape (id, title, image, description, episode_id, standalone, f_name). Notably standalone is set for single-programme shows (films, one-off documentaries) so library_sync.py knows whether to fetch seasons. Returns (shows, total_pages, cur_page) for the pagination loop in library_sync.py.

list_seasons(pid) · line 117

Fetches the episode page at https://www.bbc.co.uk/iplayer/episode/, parses redux state, returns the state["series" list — each season has a series_id and a title. Care: a BBC "pid" in this context is the show pid, not an individual episode's pid. Confusingly the same name is used in bbc.py:_get_vpid for the per-episode version id.

list_episodes(pid, page=1, series_id=None) · line 156

Fetches the per-show episodes-page (URL parameters vary if series_id is set: BBC supports a per-series filter). Returns the same tuple shape as list_shows. The returned episode dicts include episode_id (the episode pid used by bbc.resolve_vod) and subtitle (the human-readable series+episode subtitle used by library_sync.py to build a nice display title).