From 32b4ac3a3003fd5885e4e9dcd99d0a867f550294 Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Wed, 21 Oct 2026 04:43:34 +0000 Subject: [PATCH] Add a timeout to API requests load_info() called urlopen() without a timeout, and only handled errors when opening the URL. A connection that stalls while the response is read blocks the scraper, and with it the library scan, indefinitely. This happens for example behind a VPN exit node that drops some responses to requests sent with "Connection: close", which urllib always sends. Use a 15 second timeout, matching the TV show scraper, and treat timeouts and socket errors while reading the response like failures to reach the site: return the default or an error, so the scraper reports the failure instead of hanging. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Christian Hewitt --- python/lib/tmdbscraper/api_utils.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/python/lib/tmdbscraper/api_utils.py b/python/lib/tmdbscraper/api_utils.py index d722332..8a171c3 100644 --- a/python/lib/tmdbscraper/api_utils.py +++ b/python/lib/tmdbscraper/api_utils.py @@ -20,6 +20,7 @@ from __future__ import absolute_import, unicode_literals import json +import socket try: import xbmc @@ -43,6 +44,7 @@ pass HEADERS = {} +TIMEOUT = 15 def set_headers(headers): @@ -70,7 +72,8 @@ def load_info(url, params=None, default=None, resp_type = 'json'): xbmc.log(str(HEADERS), xbmc.LOGDEBUG) req = Request(url, headers=HEADERS) try: - response = urlopen(req) + response = urlopen(req, timeout=TIMEOUT) + body = response.read().decode('utf-8') except URLError as e: if hasattr(e, 'reason'): theerror = {'error': 'failed to reach the remote site\nReason: {}'.format(e.reason)} @@ -80,9 +83,15 @@ def load_info(url, params=None, default=None, resp_type = 'json'): return default else: return theerror + except (socket.timeout, socket.error) as e: + theerror = {'error': 'failed to read from the remote site\nReason: {}'.format(e)} + if default is not None: + return default + else: + return theerror if resp_type.lower() == 'json': - resp = json.loads(response.read().decode('utf-8')) + resp = json.loads(body) else: - resp = response.read().decode('utf-8') + resp = body # xbmc.log('the api response:\n{}'.format(pformat(resp)), xbmc.LOGDEBUG) return resp