From 69612953e3deb71657a242c2959be0b5101c9c62 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 12 Oct 2025 09:03:17 -0600 Subject: [PATCH 1/2] make tmdb.py testable xbmc.log is optional --- python/lib/tmdbscraper/api_utils.py | 4 ++-- python/lib/tmdbscraper/tmdbapi.py | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/python/lib/tmdbscraper/api_utils.py b/python/lib/tmdbscraper/api_utils.py index ad28326..d722332 100644 --- a/python/lib/tmdbscraper/api_utils.py +++ b/python/lib/tmdbscraper/api_utils.py @@ -66,8 +66,8 @@ def load_info(url, params=None, default=None, resp_type = 'json'): url = url + '?' + urlencode(params) if xbmc: xbmc.log('Calling URL "{}"'.format(url), xbmc.LOGDEBUG) - if HEADERS: - xbmc.log(str(HEADERS), xbmc.LOGDEBUG) + if HEADERS: + xbmc.log(str(HEADERS), xbmc.LOGDEBUG) req = Request(url, headers=HEADERS) try: response = urlopen(req) diff --git a/python/lib/tmdbscraper/tmdbapi.py b/python/lib/tmdbscraper/tmdbapi.py index 3808d69..ac095cb 100644 --- a/python/lib/tmdbscraper/tmdbapi.py +++ b/python/lib/tmdbscraper/tmdbapi.py @@ -20,7 +20,11 @@ import unicodedata from . import api_utils -import xbmc +try: + import xbmc +except ModuleNotFoundError: + # only used for logging HTTP calls, not available nor needed for testing + xbmc = None try: from typing import Optional, Text, Dict, List, Any # pylint: disable=unused-import InfoType = Dict[Text, Any] # pylint: disable=invalid-name @@ -41,6 +45,9 @@ COLLECTION_URL = BASE_URL.format('collection/{}') CONFIG_URL = BASE_URL.format('configuration') +def log(message): + if xbmc: + xbmc.log(message, xbmc.LOGDEBUG) def search_movie(query, year=None, language=None, page=None): # type: (Text) -> List[InfoType] @@ -54,7 +61,7 @@ def search_movie(query, year=None, language=None, page=None): :return: a list with found movies """ query = unicodedata.normalize('NFC', query) - xbmc.log('using title of %s to find movie' % query, xbmc.LOGDEBUG) + log('using title of %s to find movie' % query) theurl = SEARCH_URL params = _set_params(None, language) params['query'] = query @@ -75,7 +82,7 @@ def find_movie_by_external_id(external_id, language=None): :param language: the language filter for TMDb (optional) :return: the movie or error """ - xbmc.log('using external id of %s to find movie' % external_id, xbmc.LOGDEBUG) + log('using external id of %s to find movie' % external_id) theurl = FIND_URL.format(external_id) params = _set_params(None, language) params['external_source'] = 'imdb_id' @@ -94,7 +101,7 @@ def get_movie(mid, language=None, append_to_response=None): :append_to_response: the additional data to get from TMDb (optional) :return: the movie or error """ - xbmc.log('using movie id of %s to get movie details' % mid, xbmc.LOGDEBUG) + log('using movie id of %s to get movie details' % mid) theurl = MOVIE_URL.format(mid) api_utils.set_headers(dict(HEADERS)) return api_utils.load_info(theurl, params=_set_params(append_to_response, language)) @@ -110,7 +117,7 @@ def get_collection(collection_id, language=None, append_to_response=None): :append_to_response: the additional data to get from TMDb (optional) :return: the movie or error """ - xbmc.log('using collection id of %s to get collection details' % collection_id, xbmc.LOGDEBUG) + log('using collection id of %s to get collection details' % collection_id) theurl = COLLECTION_URL.format(collection_id) api_utils.set_headers(dict(HEADERS)) return api_utils.load_info(theurl, params=_set_params(append_to_response, language)) @@ -123,7 +130,7 @@ def get_configuration(): :return: configuration details or error """ - xbmc.log('getting configuration details', xbmc.LOGDEBUG) + log('getting configuration details') api_utils.set_headers(dict(HEADERS)) return api_utils.load_info(CONFIG_URL, params=TMDB_PARAMS.copy()) From 9208d90137a856576341d16c822dccf9b5988510 Mon Sep 17 00:00:00 2001 From: Ryan Rector Date: Sun, 12 Oct 2025 09:04:02 -0600 Subject: [PATCH 2/2] look for fanart with either language 'xx' or None --- python/lib/tmdbscraper/tmdb.py | 29 +++++++------- test/unittests/test_tmdb_buildimagelist.py | 45 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 test/unittests/test_tmdb_buildimagelist.py diff --git a/python/lib/tmdbscraper/tmdb.py b/python/lib/tmdbscraper/tmdb.py index 57c6380..2d4e429 100644 --- a/python/lib/tmdbscraper/tmdb.py +++ b/python/lib/tmdbscraper/tmdb.py @@ -186,39 +186,42 @@ def _parse_artwork(movie, collection, urlbases, language): fanart = [] if 'images' in movie: - posters = _get_images_with_fallback(movie['images']['posters'], urlbases, language) - landscape = _get_images_with_fallback(movie['images']['backdrops'], urlbases, language) - logos = _get_images_with_fallback(movie['images']['logos'], urlbases, language) - fanart = _get_images(movie['images']['backdrops'], urlbases, 'xx') + posters = _build_image_list_with_fallback(movie['images']['posters'], urlbases, language) + landscape = _build_image_list_with_fallback(movie['images']['backdrops'], urlbases, language) + logos = _build_image_list_with_fallback(movie['images']['logos'], urlbases, language) + fanart = _build_fanart_list(movie['images']['backdrops'], urlbases) setposters = [] setlandscape = [] setfanart = [] if collection and 'images' in collection: - setposters = _get_images_with_fallback(collection['images']['posters'], urlbases, language) - setlandscape = _get_images_with_fallback(collection['images']['backdrops'], urlbases, language) - setfanart = _get_images(collection['images']['backdrops'], urlbases, 'xx') + setposters = _build_image_list_with_fallback(collection['images']['posters'], urlbases, language) + setlandscape = _build_image_list_with_fallback(collection['images']['backdrops'], urlbases, language) + setfanart = _build_fanart_list(collection['images']['backdrops'], urlbases) return {'poster': posters, 'landscape': landscape, 'fanart': fanart, 'set.poster': setposters, 'set.landscape': setlandscape, 'set.fanart': setfanart, 'clearlogo': logos} -def _get_images_with_fallback(imagelist, urlbases, language, language_fallback='en'): - images = _get_images(imagelist, urlbases, language) +def _build_image_list_with_fallback(imagelist, urlbases, language, language_fallback='en'): + images = _build_image_list(imagelist, urlbases, [language]) # Add backup images if language != language_fallback: - images.extend(_get_images(imagelist, urlbases, language_fallback)) + images.extend(_build_image_list(imagelist, urlbases, [language_fallback])) # Add any images if nothing set so far if not images: - images = _get_images(imagelist, urlbases) + images = _build_image_list(imagelist, urlbases) return images -def _get_images(imagelist, urlbases, language='_any'): +def _build_fanart_list(imagelist, urlbases): + return _build_image_list(imagelist, urlbases, ['xx', None]) + +def _build_image_list(imagelist, urlbases, languages=[]): result = [] for img in imagelist: - if language != '_any' and img['iso_639_1'] != language: + if languages and img['iso_639_1'] not in languages: continue if img['file_path'].endswith('.svg'): continue diff --git a/test/unittests/test_tmdb_buildimagelist.py b/test/unittests/test_tmdb_buildimagelist.py new file mode 100644 index 0000000..42c5c4b --- /dev/null +++ b/test/unittests/test_tmdb_buildimagelist.py @@ -0,0 +1,45 @@ +import unittest +from python.lib.tmdbscraper.tmdb import _build_fanart_list, _build_image_list + +class TestBuildImageList(unittest.TestCase): + def setUp(self): + self.urlbases = { + 'original': 'o', + 'preview': 'p' + } + + def test_build_image_list_no_language_filter(self): + imagelist = [ + {'file_path': '/img1.jpg', 'iso_639_1': 'en'}, + {'file_path': '/img2.jpg', 'iso_639_1': 'fr'} + ] + result = _build_image_list(imagelist, self.urlbases) + + self.assertEqual(len(result), 2) + + def test_build_image_list_with_language_filter(self): + imagelist = [ + {'file_path': '/img1.jpg', 'iso_639_1': 'en'}, + {'file_path': '/img2.jpg', 'iso_639_1': 'fr'} + ] + result = _build_image_list(imagelist, self.urlbases, ['fr']) + + self.assertEqual(len(result), 1) + + def test_build_fanart_list_from_language_none(self): + imagelist = [ + {'file_path': '/img1.jpg', 'iso_639_1': None}, + {'file_path': '/img3.jpg', 'iso_639_1': 'en'} + ] + result = _build_fanart_list(imagelist, self.urlbases) + + self.assertEqual(len(result), 1) + + def test_build_fanart_list_from_language_xx(self): + imagelist = [ + {'file_path': '/img1.jpg', 'iso_639_1': 'xx'}, + {'file_path': '/img3.jpg', 'iso_639_1': 'en'} + ] + result = _build_fanart_list(imagelist, self.urlbases) + + self.assertEqual(len(result), 1)