easyplaytv-service/…/providers/stv_vod.py — STV catalogue
The full catalogue scraper ships in the shared runtime at easyplaytv-service/src/easyplaytv_service/vod_sync/providers/stv_vod.py (and the vendored copy inside service.easyplaytv). The video addon keeps only the playback half (resources/lib/stv_vod.py: resolve_vod + _get_brightcove_credentials); the Brightcove bundle/playback constants live there now. Functions and line numbers below refer to the runtime copy.
Role
STV's catalogue sits behind a clean JSON API at
player.api.stv.tv/v1. This module wraps it — pure catalogue
scraping. Playback (Brightcove account/player/policy-key resolution via
bundle.js, resolve_vod) lives in the video addon's
resources/lib/stv_vod.py.
Constants
STV's API roots.
STV-specific genre slugs (keys) → addon unified genres (values). STV has more finely-sliced genres than any other provider (soaps, history-hit, wonder, real-stories, real-life, real-crime, crime-drama, legal-drama, thrillers, comedy-drama, daytime) — most map onto the same unified buckets per the genres.py:UNIFIED_GENRES mapping.
The reverse map — given an addon unified genre, find the STV API genre
slug. Used by list_shows to translate the user's UI tap into
an STV-API-friendly slug.
API helpers
gzip-aware JSON fetch, with the STV-standard "stv-drm: true" header so the API yields DASH+DRM streams. Same header as stv.py:HEADERS for live.
STV's image API redirects to a sized rendition. Default 300x400 portrait poster is the addon's standard size.
Reaches into the images array on a programme/episode — handles
two formats: modern (with id) and legacy (with
_filepath placeholders for width/height interpolation).
Listing functions
Fetches /categories and filters out a list of categories we don't expose in the UI (line 90's set: top-picks, radio-podcasts, audio-described, visually-signed, archive, new-talent, so-real, paranormal) — they're either internally-curated collections rather than genres (top-picks) or radio/metadata we don't have UI for.
Per-category programme listing. Uses a large limit (200) so most STV genres return in one page; if a genre has more, the caller paginates. Returns a tuple (shows, total_entries). Each show dict has guid (programme GUID), f_name, title, description, image, standalone (single-episode flag).
Iterates every STV category and accumulates all shows — used by library_sync.py when the caller passes the genre-bypass / "show all STV shows" route. Dedupes by f_name across the multiple genres a programme may appear in.
Per-programme seasons. Returns a list with each season's guid, season_number, and title.
Per-programme episode listing. If series_guid is set,
filters to that single season. Returns 50-per-page (parameter chosen
to keep responses under ~3MB on big soaps like Emmerdale).
episode_number=0 are real
STV returns episodes with episode_number=0 when
playerSeries is null — single-episode documentaries AND
entire programmes without per-episode index info (Coronation Street,
Emmerdale daily strips). library_sync drops episode_number=0
via its if en_int < 1: continue guard, which would otherwise
drop hundreds of valid episodes per sync. The pre-pass in
_sync_series
(lines 1090-1115) renumbers them sequentially from 1 within their season
block before the insert loop runs. The cataloguer doesn't lie about the
episode numbers — STV's metadata is incomplete; the renumber is the
addon's workaround.
STV cache
This module caches per-show episode JSON responses in a
stv_cache/ directory (under the runtime cookie path) —
listing every STV catalogue page every sync used to cost hundreds of HTTP
calls which is wasteful since the catalogue only changes weekly. The cache
is managed by _ep_cache_path, _load_ep_cache and
_save_ep_cache (lines 171-190), with a 6-hour (21600s)
TTL before a cached response is refetched.