Troubleshooting & known issues
Recipes for symptoms observed in production. Each entry follows the form symptom → cause → fix → reference into the docs.
Database & sync
Cause: two provider-sync daemon threads sharing the
same pymysql connection. The MySQL protocol is per-connection;
interleaved packets from two threads corrupt each other.
Fix: each worker thread owns its own connection. The
implementing code in
library_sync._get_db uses
threading.local() + a guard that only the main thread can
fall back to the module-level _sync_db. Don't break the
threading.main_thread() check — any path that lets a worker
thread pick up _sync_db reintroduces the corruption.
Cause: same thing from the other thread's perspective
— its socket reader got closed out from under it by the other thread's
ping() failure.
Fix: same — per-thread connections.
Cause: Kodi's TMDB scanner recursing into the addon's
plugin URL path row, or into a disk-folder path row that hasn't been
marked exclude=1.
Fix: the sync's path reconciliation
(_ensure_path in library_sync) creates the series root with
exclude=1 on the first run, so no new duplicates should appear
going forward. Remove any already-created duplicate rows (rows the scanner
made, without the [PROVIDER] prefix) and let the next sync
rebuild the prefixed ones — the library sync is insert-only, so correcting
wrong rows means deleting them and letting the scheduler rebuild.
Cause: the provider's list_episodes call
threw an exception (transient HTTP failure or partial outage). The sync
insert guard skips shows with no episodes, but if the fetch fails the
show stays out of the library until the next sync slot.
Fix:
_sync_series fetches
episodes before calling _insert_tvshow. If the
fetch fails the show is simply skipped on this run and retried on the
next scheduled pass.
Symptom: old tvshow rows visible in Kodi's
library with poster and metadata but zero episodes underneath.
Cause: the rows were inserted by a sync run whose subsequent episode fetch failed partway through — the show exists but the episode rows were never written.
Fix: the sync no longer inserts empty tvshow rows
(current _sync_series fetch-first ordering prevents them), so
these are legacy rows from an older run. Delete the show and let the next
scheduled sync rebuild it from scratch: delete from
tvshow WHERE idShow = tvshowlinkpath, genre_link, uniqueid,
art, seasons, episode with the same
idShow/files; then trigger a sync for that provider.
Cause: STV returns episodes with
episode_number=0 when playerSeries is null
(no per-episode metadata). The
_sync_series insert loop
drops episode_number=0 via the if en_int < 1: continue
guard, so documentaries and entire programmes without per-episode index
info (Coronation Street, Emmerdale daily strips) would be missing from
the library.
Fix: a renumber pre-pass in _sync_series at lines 1090–1112 groups episodes by season, finds the set of already-used episode numbers ≥1, and renumbers zero-numbered episodes to the next available slot within their season block. If season is also 0/missing, it's bumped to 1 so the show appears as Season 1 rather than the Kodi specials block (S00).
Cause: TMDb's search-by-title re-ranks similar-named
siblings dynamically. C4's Buried → TMDb can return Buried Hearts
with a different tmdb_id on a different day. Each subsequent sync resolves
the same title to a different canonical name and inserts episodes into the
new row, fragmenting one show across multiple tvshow rows.
Fix (two layers): the TMDb disambiguation scorer
(_pick_tmdb_tv_result) now ranks new-row candidates by
title-similarity first (token Jaccard ×100), with year and overview as
tie-breaks — so Buried (title similarity 1.0) beats Buried Hearts
(0.29) when the query is "Buried". On subsequent syncs,
_insert_tvshow looks up
existing rows by the dir-name (prefixed_dirname) first, falling
back to the TMDb canonical name. This keeps the local show identity stable
even if TMDb's search re-ranks the title on a later pass.
Cause: these daily strips produce
__PARAMS__ JSON blocks over 600 KB. The
c4_vod._extract_brand_data walker
caps its scan at 4 MB; lines longer than the cap silently drop the brand
data and fall through to "no episodes".
Fix: the cap sits at line 50 — raise it if you see a
bigger show. The regex still bails at the first } that
returns the brace depth to 0, so the cap is a safety bound, not the
correctness boundary.
Playback
Cause: not actually an ITVX issue — this is the My5 symptom that's been mismarbled in your logs. If you're seeing it on My5 (Cassie) the cause is the C5 player JS has rotated the AES/HMAC keys, and the addon's cached key is the previous one.
Fix:
my5._fetch_cassie already self-heals
this — on a 403 with body "Failed to authenticate HMAC" it re-extracts
keys from the live C5 player JS and retries once. If you're still seeing
403s after that, the path
https://player.akamaized.net/html5player/core/html5-c5-player.js
is no longer the right URL — C5 may have moved hosts. Check the network
tab of the C5 web player to find the new JS bundle URL.
Symptom: VOD content (especially non-ITV produced shows like Disney+ originals, "The Affair", "Downton Abbey Celebrates the Finale") plays video but audio is missing. The player's audio settings show "NONE". Affects all Kodi platforms — Pi, CoreELEC, Windows. The same content plays with audio in Chrome/Edge browsers.
Cause: The Irdeto license server assigns VMP entitlements
(itvx_ctv_soft_protect_hd) per-content. On L3 Widevine (e.g.
Raspberry Pi), VMP cannot be satisfied — the audio key oscillates between
kUsable and kExpired/SystemCode5, so audio decryption never completes.
This is NOT bypassable by changing platformTag — both
"dotcom" and "ctv" receive the same entitlement.
Video works because V4L2 hardware decode bypasses the CDM entirely; audio
has no hardware path. Browsers satisfy VMP natively; Kodi's
inputstream.adaptive cannot. See
viwX #182
and Kodi forum #191, #202.
Current state: EasyPlayTV uses "platformTag": "dotcom"
which limits VOD to 720p (no 1080p). Audio still fails on L3 — this is the
same issue affecting viwX and all other L3 Widevine Kodi addons.
Live channels are unaffected (already use "dotcom").
See IA PR #2021
and xbmc PR #28204
for the upstream fix implementing the CDM audio decoder interface that will
bypass the single-decrypt VMP check (targeting Kodi v23, no ETA).
Cause: refresh token expired (they live ~30 days). The user's saved credentials in addon settings are still valid but the refresh-token grant needs a fresh login.
Fix: open the addon's settings → ITVX section → sign
out, then sign in again. The addon calls
itvx.login which uses the
password grant directly (the cookie-based web flow isn't used
because the API directly accepts password grant for the
app.10ft.itv.com origin).
Cause: the saved Cognito session in
addon_data/plugin.video.easyplaytv/my5_session has expired
and the refresh token can no longer renew the access token (Cognito
refresh tokens stay valid until revoked by GlobalSignOut, but server-side
session invalidation can still revoke them — e.g. if you sign out via
the My5 website).
Fix: open the addon's settings → Channel5 section →
sign out, then sign in again. The addon calls
my5.login which
InitiateAuth(USER_PASSWORD_AUTH)'s against AWS Cognito
directly and persists a fresh session dict (containing a new
refresh_token) to my5_session. Note that
free-to-air content will keep playing in the meantime — the
_fetch_cassie retry
logic drops the Authorization: Bearer header on 401/403 and
re-issues the request anonymously.
Cause: the Cognito InitiateAuth response
came back with a ChallengeName instead of an
AuthenticationResult. My5's hosted-UI web flow handles
these (MFA, new-device, SRP, password-reset) but the addon's direct
USER_PASSWORD_AUTH path does not.
Fix: sign in to channel5.com in a browser first and
complete the challenge there. Once the account is in a state Cognito
accepts for direct USER_PASSWORD_AUTH (typically: MFA
disabled, password confirmed), the addon's
?action=my5_login will succeed and persist the session
locally.
Cause: Brightcove's policy key rotation can ignore the
X-Forwarded-For: 2.97.0.0 header
sky.resolve sends. When the current
Sky policy stops trusting the XFF header, the addon returns AU/US manifest
URLs that don't play on a UK Kodi box.
Fix: if you're not on a UK IP, the addon has no remedy — Sky News only ships a UK stream. Run from a UK-IP egress (a VPN at the network layer) instead of relying on the XFF spoof. The XFF spoof is a per-policy workaround; assume it can break on any policy rotation.
Cause: Blaze's HTML page scraper (blaze_vod.list_shows) regexes broke — Blaze changed class names or HTML structure on their static page.
Fix: open
https://watch.blaze.tv/series in a browser, view source,
grep for col-auto layout-carousel-column. If the class names
changed, update the regex at
blaze_vod.list_shows line 13
and the per-card regexes (lines 15-40). No JSON-API fallback exists.
OSMC / Pi-specific hardware
Cause: inputstream.adaptive's secure-decoder path is broken on the Pi's V4L2 mem2mem hardware decoder — it tries to allocate secure surfaces that the Pi's V4L2 path can't honour, fails with EGL_BAD_SURFACE, and bails out of playback entirely.
Fix: set NOSECUREDECODER=true in
inputstream.adaptive's settings on OSMC to force the V4L2 mem2mem
hardware decoder instead of the broken secure decoder path for ITVX
Widevine content. ITVX startup drops to ~5–8 s from click-to-play
rather than the 30–60 s of EGL dithering.
Cause: a stray malformed (unquoted XML)
verboselog=true setting in the EasyPlayTV parent Series path
row of MyVideos.path. Kodi's TMDb scraper logs 20K+ lines
per /tv/ API call when the setting is on
(pformat of the entire JSON response), costing ~22 s per season on RPi
SD-card I/O — e.g. ~13 min for WWE Raw alone (35 seasons).
Fix:
library_sync._sanitize_scraper_settings_xml
quotes unquoted attributes and pins verboselog=false. If you
see verbose log spam after a Kodi upgrade, manually update the
strSettings column for the affected path row in the
path table to set verboselog=false and re-quote
any unquoted XML attributes.
Cause: GPU/CMA memory exhaustion. Kodi's library
scanner fires ffmpeg probes for every entry in the video DB on boot —
with myvideos.extractflags and
myvideos.extractchapterthumbs set to true
(Kodi's defaults), each probe allocates a V4L2 decoder context + scratch
frames. With a few hundred large .mkv files this exhausts
the 256 MiB CMA pool on the Pi in a few seconds. The kernel driver
fails the mmap, the GPU is reset, and Kodi's mediacenter
service restarts — only to repeat the cycle on the next boot's scan.
Symptoms: journalctl -u mediacenter shows repeated
mmap of bo N (offset …, size …) failed entries;
dmesg shows [drm] Resetting GPU.;
kodi.log is filled with LoadFile: Failed to load ….mkv:
out of memory lines.
Fix: turn off the ffmpeg-probe settings in
guisettings.xml:
false
false
…then restart mediacenter.service. The EasyPlayTV sync
already writes its own poster/art rows directly via SQL, so Kodi's
ffmpeg-probe is not needed for the addon's library entries. Only the
disk-based .mkv library (for non-addon content you also
host locally) loses auto-generated thumbnails — a small price to pay for
a stable boot.
Manual syncs / external triggers
Earlier releases supported forcing a sync via a marker file
(force_sync_<provider> in the video addon's profile), an HTTP
POST /sync/vod/<provider> endpoint, and a --sync
CLI flag. These were all removed — the only sync path is now the scheduled
sequential batch (providers one-after-another, then PVR refresh), capped by
the sync interval (VOD sync hours / vod_sync.interval_hours).
To sync more often, lower that interval (1 = hourly).