default.py — plugin entry point & router
Role
Kodi's plugin entry-point. Kodi launches a new Python interpreter, runs
this script with sys.argv shaped like
["plugin://plugin.video.easyplaytv/", handle, "?mode=…&action=…&…"],
and expects the script to either populate a directory listing for
handle (with xbmcplugin.addDirectoryItem) or hand
back a playable ListItem via
xbmcplugin.setResolvedUrl.
The script is a single dispatch table — it pulls a routing key from the
query string and calls the matching list_* / resolve_*
helper. Everything substantive (sync engine, provider resolvers, TMDb
enrichment) lives in resources/lib/; this file just
routes.
The addon's plugin UI offers only one tile — Live TV.
Movies and TV Shows are browsed through Kodi's own library windows after
library_sync.py has written
the catalogue directly into the MariaDB video database. When the user
clicks a movie or episode in Kodi's library, Kodi re-invokes this script
with ?action=resolve_vod&provider=…&episode_id=… (the
extension stored in the files.strFilename column); the
dispatch table routes that to _resolve_vod
below.
Module-level constants (lines 1–53)
Standard Kodi plugin boilerplate. BASE_URL is the
plugin:// URL Kodi invoked us under; HANDLE is
the integer directory handle your addDirectoryItem calls must
target; _ADDON is the cached
xbmcaddon.Addon("plugin.video.easyplaytv") used to read
settings (has_tv_license, ITVX creds, max_resolution, …).
Channel tables (lines 11–52)
| Name | Shape | Used by |
|---|---|---|
BBC_LIVE_CHANNELS | tuple of (live_id, label, icon_name) | list_live() — gated on has_tv_license |
CUTV_UK_CHANNELS | tuple of (channel_id, label, provider_module, icon_name) | list_live() — Sky News, STV +1, C4 family, My5 family, Blaze |
ITVX_LIVE_CHANNELS | tuple of (channel_code, label, icon_name) | list_live() — ITV1–ITV4 + ITVBe (resolved by itvx.resolve()) |
Routing helpers
Builds a plugin URL by urlencoding kwargs onto BASE_URL.
Used for every ListItem path so Kodi can route back through
this script on click. ← called by every list_* and _live_channel_item
Wraps "import the provider module, call its resolver, set up DASH,
resolve the ListItem". Short-circuits with a notification if BBC is
selected and has_tv_license is false (line 67). Calls
setup_dash_item() with the manifest +
license data returned by the provider.
The resolver returns a tuple of (manifest_url, license_url, headers, payload, …);
the caller passes all of it onward as kwargs. If you add a new provider
here, be sure its resolver actually returns a manifest URL (not a
top-level M3U8 to be played natively) — only DASH via
inputstream.adaptive is supported.
VOD play dispatcher. Imports the matching provider module
(my5, c4, itvx, bbc,
stv, blaze) and invokes its
resolve_vod(episode_id, listitem, …), then hands the
populated listitem to xbmcplugin.setResolvedUrl.
Before dispatch, it composes a display label via
PROVIDER_LABELS + the
show_title / title kwargs so the player-side
listitem carries the same "[ITVX] Ackley Bridge - 1x01. Pilot"
string the folder-side listitem had. This matters because Kodi caches the
player's listitem metadata as the cached folder listitem once playback
ends — if the labels don't match, the folder row's
SxxExx prefix disappears.
The function also bakes setInfo{season, episode, title}
into the listitem. The skin computes the "1x01." prefix
zero-padded from those fields. The comment at lines 167–220 explains the
ordering: setInfo must be applied after the provider
resolver runs because setup_dash_item calls
listitem.setPath(), and on Kodi 21 setPath()
resets the listitem's VideoInfoTag, wiping any setInfo set
before.
Providers dispatched:
Directory-listing functions
Top-level menu when Kodi opens the addon root. Renders a single
directory item — Live TV — pointing at
?mode=live_tv. Movies and Series are deliberately not listed
here; the user browses those through Kodi's own library views (which
read the rows library_sync
wrote into the DB).
Helper for one live-channel row. Builds a ListItem with
IsPlayable=true (line 200), attaches the bundled PNG icon
from resources/icons/ (via
_icon_path()), and returns (listitem, build_url(action='resolve', resolver=…, channel_id=…, channel_name=…)).
Stitches the three channel tables (BBC_LIVE_CHANNELS,
CUTV_UK_CHANNELS, ITVX_LIVE_CHANNELS) into a
single flat directory listing. BBC rows are gated on
has_tv_license. After all rows are added, forces Kodi's
"List" view-mode (50) so the skin's right-side media-info panel doesn't
pop up — channel icons render correctly in the left-side list.
The dispatch table (lines ~231–285)
The if __name__ == "__main__" block at the bottom is one
long action == '…' / elif action == '…' chain with a small
mode == '…' chain for directory-listing routes. Two routing
keys are used:
| Key | Purpose | Examples |
|---|---|---|
action= | One-shot verbs — resolve-and-play, sync, login, maintenance helpers | resolve, resolve_vod, itvx_login, my5_login, sync, backfill_art, prefix_titles |
mode= | Directory listings — Kodi opens a folder | live_tv |
Manual sync & action routes
| Route | Effect |
|---|---|
action=sync&provider= | Kicks a sync — see below for blocking vs daemon |
action=itvx_login · itvx_logout | Triggers ITVX OAuth — see itvx.login() |
action=my5_login · my5_logout | Triggers My5 / Channel5 user sign-in via AWS Cognito — see my5.login(). The settings UI exposes these as buttons (the addon's settings.xml uses RunPlugin(plugin://plugin.video.easyplaytv/?action=my5_login) as the button action). Credentials are read from my5_email / my5_password in settings. |
action=backfill_art | library_sync.backfill_series_art() — refresh missing posters/fanart |
action=prefix_titles | prefix_existing_movies() + prefix_existing_tvshows() + fix_sort_titles() — maintenance helper to retrofit the [PROVIDER] prefix onto rows pre-dating the convention |
Lines 269–73 explicitly call xbmcplugin.endOfDirectory(HANDLE,
succeeded=False, cacheToDisc=False) when triggering
?action=sync&provider=…. Without this, Kodi's CGUIMediaWindow
treats the URL as the current folder and re-fetches it on the 7-minute
auto-refresh — which would re-fire the sync every 7 min.
If ?action=sync&blocking=1 the sync runs in the calling
thread (so the script interpreter stays alive until completion — needed
when firing via JSON-RPC RunPlugin). Without
blocking=1 it spawns a daemon thread and returns immediately.
Daemon threads die when the calling script returns, so fire-and-forget
only works from within Kodi's UI navigation; from JSON-RPC you
must use blocking=1 or use the marker-file mechanism in
service.py instead.
Resolve routes (Kodi's player calls these)
| Route | Effect |
|---|---|
action=resolve&resolver= | Live-TV channel play. Dispatches via _resolve_channel to the named provider module's resolve(channel_id, listitem). |
action=resolve_vod&provider=…&episode_id=…&title=…&season=…&episode=…&… | VOD episode / movie play. Dispatches via _resolve_vod to the matching provider's resolve_vod. Kodi's library invokes this when the user clicks an episode / movie row — the URL in files.strFilename is the query-string of this same plugin URL. |