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

default.py — plugin entry point & router

Path /default.py Lines 341 Type python.pluginsource (Kodi-invoked) Stateless? yes — re-imported per call ← invoked by Kodi (one fresh interpreter per call)

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.

Movies & Series are not browsed through the addon

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)

BASE_URL, HANDLE, _ADDON · lines 3–5

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)

NameShapeUsed by
BBC_LIVE_CHANNELStuple of (live_id, label, icon_name)list_live() — gated on has_tv_license
CUTV_UK_CHANNELStuple of (channel_id, label, provider_module, icon_name)list_live() — Sky News, STV +1, C4 family, My5 family, Blaze
ITVX_LIVE_CHANNELStuple of (channel_code, label, icon_name)list_live() — ITV1–ITV4 + ITVBe (resolved by itvx.resolve())

Routing helpers

build_url(**kwargs) -> str · line 61

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

_resolve_channel(resolver_module, resolver_fn, channel_id, channel_name) · line 65

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.

resolve must not swallow the manifest URL

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.

_resolve_vod(provider, episode_id, **kwargs) · line 107

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:

ProviderResolver call
my5my5.resolve_vod(episode_id, li, standalone=…, f_name=…, show_f_name=…)
itvxitvx.resolve_vod(episode_id, li, playlist_url)
bbcbbc.resolve_vod(episode_id, li)
c4c4.resolve_vod(episode_id, asset_id, li)
stvstv_vod.resolve_vod(episode_id, li, drm_enabled=…)
blazeblaze_vod.resolve_vod(stream_base, stream_key, episode_id, li)

Directory-listing functions

list_main() · line 187

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).

_live_channel_item(chan_id, chan_name, icon_name, resolver, **extra_params) · line 198

Helper for one live-channel row. Builds a ListItem with IsPlayable=true (line 200), attaches the bundled PNG icon from resources/icons/.png (via _icon_path()), and returns (listitem, build_url(action='resolve', resolver=…, channel_id=…, channel_name=…)).

list_live() · line 209

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:

KeyPurposeExamples
action=One-shot verbs — resolve-and-play, sync, login, maintenance helpersresolve, resolve_vod, itvx_login, my5_login, sync, backfill_art, prefix_titles
mode=Directory listings — Kodi opens a folderlive_tv

Manual sync & action routes

RouteEffect
action=sync&provider=

Kicks a sync — see below for blocking vs daemon
action=itvx_login · itvx_logoutTriggers ITVX OAuth — see itvx.login()
action=my5_login · my5_logoutTriggers 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_artlibrary_sync.backfill_series_art() — refresh missing posters/fanart
action=prefix_titlesprefix_existing_movies() + prefix_existing_tvshows() + fix_sort_titles() — maintenance helper to retrofit the [PROVIDER] prefix onto rows pre-dating the convention
Cache-to-disc caveat for sync via JSON-RPC

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.

Sync has two modes

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)

RouteEffect
action=resolve&resolver=&channel_id=…&channel_name=…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.