From b74706d6501e3bbffbb82acb476832719d7992f7 Mon Sep 17 00:00:00 2001 From: romanvm Date: Wed, 13 Dec 2023 21:26:34 +0000 Subject: [PATCH] [metadata.tvmaze] 1.3.5 --- metadata.tvmaze/addon.xml | 5 +- metadata.tvmaze/libs/actions.py | 42 ++++--- metadata.tvmaze/libs/cache_service.py | 17 +-- metadata.tvmaze/libs/data_service.py | 12 +- metadata.tvmaze/libs/exception_logger.py | 137 +++++++++++++++-------- metadata.tvmaze/libs/imdb_rating.py | 6 +- metadata.tvmaze/libs/tvmaze_api.py | 20 ++-- metadata.tvmaze/libs/utils.py | 53 ++++++--- metadata.tvmaze/main.py | 7 +- 9 files changed, 189 insertions(+), 110 deletions(-) diff --git a/metadata.tvmaze/addon.xml b/metadata.tvmaze/addon.xml index 656d5bd2d..267bf84ff 100644 --- a/metadata.tvmaze/addon.xml +++ b/metadata.tvmaze/addon.xml @@ -1,7 +1,7 @@ @@ -20,8 +20,7 @@ We provide an API that can be used by anyone or service like Kodi to retrieve TV https://www.tvmaze.com https://github.com/romanvm/kodi.tvmaze - 1.3.4: -- Internal changes. + 1.3.4: Fix incompatibility with filename tags in Kodi "Omega". true diff --git a/metadata.tvmaze/libs/actions.py b/metadata.tvmaze/libs/actions.py index 2f642e087..14dce455e 100644 --- a/metadata.tvmaze/libs/actions.py +++ b/metadata.tvmaze/libs/actions.py @@ -16,6 +16,7 @@ """Plugin route actions""" import json +import logging import sys from typing import Optional from urllib import parse as urllib_parse @@ -24,7 +25,7 @@ import xbmcplugin from . import tvmaze_api, data_service -from .utils import logger, get_episode_order, ADDON +from .utils import get_episode_order, ADDON HANDLE = int(sys.argv[1]) @@ -60,7 +61,7 @@ def parse_nfo_file(nfo: str, full_nfo: bool): :param full_nfo: use the info from an NFO and not to try to get the info by the scraper """ is_tvshow_nfo = True - logger.debug(f'Trying to parse NFO file:\n{nfo}') + logging.debug('Trying to parse NFO file:\n%s', nfo) info = None if '' in nfo: if full_nfo: @@ -94,33 +95,40 @@ def parse_nfo_file(nfo: str, full_nfo: bool): ) -def get_details(show_id: str, default_rating: str) -> None: +def get_details(show_id: Optional[str], default_rating: str, unique_ids: Optional[str] = None) -> None: """Get details about a specific show""" - logger.debug(f'Getting details for show id {show_id}') + logging.debug('Getting details for show id %s', show_id) + if not show_id and unique_ids is not None: + show_id = data_service.parse_json_episogeguide(unique_ids) + if not show_id: + xbmcplugin.setResolvedUrl(HANDLE, False, xbmcgui.ListItem(offscreen=True)) + return show_info = tvmaze_api.load_show_info(show_id) if show_info is not None: list_item = xbmcgui.ListItem(show_info['name'], offscreen=True) list_item = data_service.add_main_show_info(list_item, show_info, default_rating=default_rating) xbmcplugin.setResolvedUrl(HANDLE, True, list_item) - else: - xbmcplugin.setResolvedUrl(HANDLE, False, xbmcgui.ListItem(offscreen=True)) + return + xbmcplugin.setResolvedUrl(HANDLE, False, xbmcgui.ListItem(offscreen=True)) def get_episode_list(episodeguide: str, episode_order: str) -> None: # pylint: disable=missing-docstring - logger.debug(f'Getting episode list for episodeguide {episodeguide}, order: {episode_order}') + logging.debug('Getting episode list for episodeguide %s, order: %s', + episodeguide, episode_order) show_id = None if episodeguide.startswith('{'): show_id = data_service.parse_json_episogeguide(episodeguide) if show_id is None: - logger.error(f'Unable to determine TVmaze show ID from episodeguide: {episodeguide}') + logging.error(f'Unable to determine TVmaze show ID from episodeguide: %s', episodeguide) return if show_id is None and not episodeguide.isdigit(): - logger.warning(f'Invalid episodeguide format: {episodeguide} (probably URL).') + logging.warning('Invalid episodeguide format: %s (probably URL).', episodeguide) show_id = data_service.parse_url_episodeguide(episodeguide) if show_id is None and episodeguide.isdigit(): - logger.warning(f'Invalid episodeguide format: {episodeguide} (a numeric string). ' - f'Please consider re-scanning the show to update episodeguide record.') + logging.warning('Invalid episodeguide format: %s (a numeric string). ' + 'Please consider re-scanning the show to update episodeguide record.', + episodeguide) show_id = episodeguide if show_id is not None: episodes_map = data_service.get_episodes_map(show_id, episode_order) @@ -147,7 +155,7 @@ def get_episode_list(episodeguide: str, episode_order: str) -> None: # pylint: def get_episode_details(encoded_ids: str, episode_order: str) -> None: # pylint: disable=missing-docstring encoded_ids = urllib_parse.unquote(encoded_ids) decoded_ids = dict(urllib_parse.parse_qsl(encoded_ids)) - logger.debug(f'Getting episode details for {decoded_ids}') + logging.debug('Getting episode details for %s', decoded_ids) episode_info = data_service.get_episode_info(decoded_ids['show_id'], decoded_ids['episode_id'], decoded_ids['season'], @@ -167,7 +175,7 @@ def get_artwork(show_id: str) -> None: :param show_id: default unique ID set by setUniqueIDs() method """ - logger.debug(f'Getting artwork for show ID {show_id}') + logging.debug('Getting artwork for show ID %s', show_id) if show_id: show_info = tvmaze_api.load_show_info(show_id) if show_info is not None: @@ -186,9 +194,9 @@ def router(paramstring: str) -> None: :raises RuntimeError: on unknown call action """ params = dict(urllib_parse.parse_qsl(paramstring)) - logger.debug(f'Called addon with params: {sys.argv}') + logging.debug('Called addon with params: %s', str(sys.argv)) path_settings = json.loads(params.get('pathSettings') or '{}') - logger.debug(f'Path settings: {path_settings}') + logging.debug('Path settings: %s', path_settings) episode_order = get_episode_order(path_settings) default_rating = path_settings.get('default_rating') if default_rating is None: @@ -201,7 +209,9 @@ def router(paramstring: str) -> None: elif params['action'].lower() == 'nfourl': parse_nfo_file(params['nfo'], full_nfo) elif params['action'] == 'getdetails': - get_details(params['url'], default_rating) + url = params.get('url') + unique_ids = params.get('uniqueIDs') + get_details(url, default_rating, unique_ids) elif params['action'] == 'getepisodelist': get_episode_list(params['url'], episode_order) elif params['action'] == 'getepisodedetails': diff --git a/metadata.tvmaze/libs/cache_service.py b/metadata.tvmaze/libs/cache_service.py index 4cb5b4a00..6cd5212d0 100644 --- a/metadata.tvmaze/libs/cache_service.py +++ b/metadata.tvmaze/libs/cache_service.py @@ -16,6 +16,7 @@ """Cache-related functionality""" import json +import logging import os import time from typing import Optional, Text, Dict, Any, Union @@ -23,14 +24,14 @@ import xbmcgui import xbmcvfs -from .utils import ADDON_ID, logger +from .utils import ADDON_ID EPISODES_CACHE_TTL = 60 * 10 # 10 minutes class MemoryCache: _instance = None - CACHE_KEY = '__tvmaze_scraper__' + CACHE_KEY = f'__{ADDON_ID}_cache__' def __new__(cls): if cls._instance is None: @@ -52,17 +53,17 @@ def set(self, obj_id: Union[int, str], obj: Any) -> None: def get(self, obj_id: Union[int, str]) -> Optional[Any]: cache_json = self._window.getProperty(self.CACHE_KEY) if not cache_json: - logger.debug('Memory cache empty') + logging.debug('Memory cache empty') return None try: cache = json.loads(cache_json) except ValueError as exc: - logger.debug(f'Memory cache error: {exc}') + logging.debug(f'Memory cache error: {exc}') return None if cache['id'] != obj_id or time.time() - cache['timestamp'] > EPISODES_CACHE_TTL: - logger.debug('Memory cache miss') + logging.debug('Memory cache miss') return None - logger.debug('Memory cache hit') + logging.debug('Memory cache hit') return cache['object'] @@ -110,8 +111,8 @@ def load_show_info_from_cache(show_id: Union[int, str]) -> Optional[Dict[str, An with open(os.path.join(CACHE_DIR, file_name), 'r', encoding='utf-8') as fo: cache_json = fo.read() show_info = json.loads(cache_json) - logger.debug('Show info cache hit') + logging.debug('Show info cache hit') return show_info except (IOError, EOFError, ValueError) as exc: - logger.debug(f'Cache error: {type(exc)} {exc}') + logging.debug('Cache error: %s %s', type(exc), exc) return None diff --git a/metadata.tvmaze/libs/data_service.py b/metadata.tvmaze/libs/data_service.py index 97deb2df7..3c1cb9bea 100644 --- a/metadata.tvmaze/libs/data_service.py +++ b/metadata.tvmaze/libs/data_service.py @@ -15,6 +15,7 @@ """Functions to process data""" import json +import logging import re from collections import defaultdict from typing import Optional, Dict, List, Any, Sequence, NamedTuple @@ -26,7 +27,6 @@ from xbmcgui import ListItem from . import tvmaze_api, cache_service as cache -from .utils import logger InfoType = Dict[str, Any] # pylint: disable=invalid-name @@ -47,7 +47,7 @@ ('', '[/I]'), ('

', '[CR]'), ) -SUPPORTED_EXTERNAL_IDS = ('tvdb', 'imdb') +SUPPORTED_EXTERNAL_IDS = ('tvdb', 'thetvdb', 'imdb') class UrlParseResult(NamedTuple): @@ -116,7 +116,7 @@ def get_episode_info(show_id: str, key = f'{episode_id}_{season}_{episode}' episode_info = episodes_map[key] except KeyError as exc: - logger.error(f'Unable to retrieve episode info: {exc}') + logging.error('Unable to retrieve episode info: %s', exc) if episode_info is None: episode_info = tvmaze_api.load_episode_info(episode_id) return episode_info @@ -313,9 +313,9 @@ def parse_url_nfo_contents(nfo: str) -> Optional[UrlParseResult]: if show_id_match is not None: provider = show_id_match.group(1) show_id = show_id_match.group(2) - logger.debug(f'Matched show ID {show_id} by regexp "{regexp}"') + logging.debug('Matched show ID %s by regexp "%s"', show_id, regexp) return UrlParseResult(provider, show_id) - logger.debug('Unable to find show ID in an NFO file') + logging.debug('Unable to find show ID in an NFO file') return None @@ -403,7 +403,7 @@ def _filter_by_year(shows: List[InfoType], year: str) -> Optional[InfoType]: def search_show(title: str, year: str) -> Sequence[InfoType]: - logger.debug(f'Searching for TV show {title} ({year})') + logging.debug(f'Searching for TV show %s (%s)', title, year) raw_search_results = tvmaze_api.search_show(title) search_results = [res['show'] for res in raw_search_results] if len(search_results) > 1 and year: diff --git a/metadata.tvmaze/libs/exception_logger.py b/metadata.tvmaze/libs/exception_logger.py index 40d92e850..928dd8086 100644 --- a/metadata.tvmaze/libs/exception_logger.py +++ b/metadata.tvmaze/libs/exception_logger.py @@ -1,4 +1,4 @@ -# (c) Roman Miroshnychenko 2020 +# (c) Roman Miroshnychenko 2023 # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,16 +13,19 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Exception logger with extended diagnostic info""" + import inspect import sys from contextlib import contextmanager from platform import uname from pprint import pformat -from typing import List, Dict, Callable, Generator, Any +from typing import Any, Dict, Callable, Generator, Iterable, Optional import xbmc -from .utils import logger + +def _log_error(message: str) -> None: + xbmc.log(message, level=xbmc.LOGERROR) def _format_vars(variables: Dict[str, Any]) -> str: @@ -37,15 +40,15 @@ def _format_vars(variables: Dict[str, Any]) -> str: var_list.sort(key=lambda i: i[0]) lines = [] for var, val in var_list: - lines.append(f'{var} = {pformat(val, indent=4)}') + lines.append(f'{var} = {pformat(val)}') return '\n'.join(lines) -def _format_code_context(code_context: List[str], lineno: int, index: int) -> str: +def _format_code_context(frame_info: inspect.FrameInfo) -> str: context = '' - if code_context is not None: - for i, line in enumerate(code_context, lineno - index): - if i == lineno: + if frame_info.code_context is not None: + for i, line in enumerate(frame_info.code_context, frame_info.lineno - frame_info.index): + if i == frame_info.lineno: context += f'{str(i).rjust(5)}:>{line}' else: context += f'{str(i).rjust(5)}: {line}' @@ -64,40 +67,96 @@ def _format_code_context(code_context: List[str], lineno: int, index: int) -> st """ -def _format_frame_info(frame_info: tuple) -> str: +def _format_frame_info(frame_info: inspect.FrameInfo) -> str: return FRAME_INFO_TEMPLATE.format( - file_path=frame_info[1], - lineno=frame_info[2], - code_context=_format_code_context(frame_info[4], frame_info[2], - frame_info[5]), - local_vars=_format_vars(frame_info[0].f_locals) + file_path=frame_info.filename, + lineno=frame_info.lineno, + code_context=_format_code_context(frame_info), + local_vars=_format_vars(frame_info.frame.f_locals) ) +STACK_TRACE_TEMPLATE = """ +#################################################################################################### + Stack Trace +==================================================================================================== +{stack_trace} +************************************* End of diagnostic info *************************************** +""" + + +def _format_stack_trace(frames: Iterable[inspect.FrameInfo]) -> str: + stack_trace = '' + for frame_info in frames: + stack_trace += _format_frame_info(frame_info) + return STACK_TRACE_TEMPLATE.format(stack_trace=stack_trace) + + EXCEPTION_TEMPLATE = """ -*********************************** Unhandled exception detected *********************************** #################################################################################################### - Diagnostic info + Exception Diagnostic Info ---------------------------------------------------------------------------------------------------- -Exception type : {exc_type} -Exception value : {exc} -System info : {system_info} -Python version : {python_version} -Kodi version : {kodi_version} -sys.argv : {sys_argv} +Exception type : {exc_type} +Exception message : {exc} +System info : {system_info} +Python version : {python_version} +Kodi version : {kodi_version} +sys.argv : {sys_argv} ---------------------------------------------------------------------------------------------------- sys.path: {sys_path} -#################################################################################################### - Stack Trace -==================================================================================================== -{stack_trace} -************************************* End of diagnostic info *************************************** +{stack_trace_info} """ +def format_trace(frames_to_exclude: int = 1) -> str: + """ + Returns a pretty stack trace with code context and local variables + + Stack trace info includes the following: + + * File path and line number + * Code fragment + * Local variables + + It allows to inspect execution state at the point of this function call + + :param frames_to_exclude: How many top frames are excluded from the trace + to skip unnecessary info. Since each function call creates a stack frame + you need to exclude at least this function frame. + """ + frames = inspect.stack(5)[frames_to_exclude:] + return _format_stack_trace(reversed(frames)) + + +def format_exception(exc_obj: Optional[Exception] = None) -> str: + """ + Returns a pretty exception stack trace with code context and local variables + + :param exc_obj: exception object (optional) + :raises ValueError: if no exception is being handled + """ + if exc_obj is None: + _, exc_obj, _ = sys.exc_info() + if exc_obj is None: + raise ValueError('No exception is currently being handled') + stack_trace = inspect.getinnerframes(exc_obj.__traceback__, context=5) + stack_trace_info = _format_stack_trace(stack_trace) + message = EXCEPTION_TEMPLATE.format( + exc_type=exc_obj.__class__.__name__, + exc=exc_obj, + system_info=uname(), + python_version=sys.version.replace('\n', ' '), + kodi_version=xbmc.getInfoLabel('System.BuildVersion'), + sys_argv=pformat(sys.argv), + sys_path=pformat(sys.path), + stack_trace_info=stack_trace_info + ) + return message + + @contextmanager -def log_exception(logger_func: Callable[[str], None] = logger.error) -> Generator[None, None, None]: +def catch_exception(logger_func: Callable[[str], None] = _log_error) -> Generator[None, None, None]: """ Diagnostic helper context manager @@ -118,7 +177,7 @@ def log_exception(logger_func: Callable[[str], None] = logger.error) -> Generato Example:: - with debug_exception(): + with catch_exception(): # Some risky code raise RuntimeError('Fatal error!') @@ -128,18 +187,8 @@ def log_exception(logger_func: Callable[[str], None] = logger.error) -> Generato try: yield except Exception as exc: - stack_trace = '' - for frame_info in inspect.trace(5): - stack_trace += _format_frame_info(frame_info) - message = EXCEPTION_TEMPLATE.format( - exc_type=exc.__class__.__name__, - exc=exc, - system_info=uname(), - python_version=sys.version.replace('\n', ' '), - kodi_version=xbmc.getInfoLabel('System.BuildVersion'), - sys_argv=str(sys.argv), - sys_path=pformat(sys.path), - stack_trace=stack_trace - ) - logger_func(message) - raise exc + message = format_exception(exc) + # pylint: disable=line-too-long + logger_func('\n*********************************** Unhandled exception detected ***********************************\n' + + message) + raise diff --git a/metadata.tvmaze/libs/imdb_rating.py b/metadata.tvmaze/libs/imdb_rating.py index ac5a9c91d..65d4e4503 100644 --- a/metadata.tvmaze/libs/imdb_rating.py +++ b/metadata.tvmaze/libs/imdb_rating.py @@ -14,11 +14,11 @@ # along with this program. If not, see . import json +import logging import re from typing import Dict, Union, Optional from . import simple_requests as requests -from .utils import logger IMDB_TITLE_URL = 'https://www.imdb.com/title/{}/' @@ -36,6 +36,6 @@ def get_imdb_rating(imdb_id: str) -> Optional[Dict[str, Union[int, float]]]: rating = aggregate_rating['ratingValue'] votes = aggregate_rating['ratingCount'] return {'rating': rating, 'votes': votes} - logger.debug(f'Unable to get IMDB rating for ID {imdb_id}. ' - f'Status: {response.status_code}, response: {response.text}') + logging.debug('Unable to get IMDB rating for ID %s. Status: %s, response: %s', + imdb_id, response.status_code, response.text) return None diff --git a/metadata.tvmaze/libs/tvmaze_api.py b/metadata.tvmaze/libs/tvmaze_api.py index 5d5f0f3b2..a4b5c0955 100644 --- a/metadata.tvmaze/libs/tvmaze_api.py +++ b/metadata.tvmaze/libs/tvmaze_api.py @@ -15,13 +15,13 @@ """Functions to interact with TVmaze API""" +import logging from pprint import pformat from typing import Text, Optional, Union, List, Dict, Any from . import cache_service as cache from . import simple_requests as requests from .imdb_rating import get_imdb_rating -from .utils import logger InfoType = Dict[str, Any] # pylint: disable=invalid-name @@ -49,12 +49,12 @@ def _load_info(url: str, :return: API response :raises requests.exceptions.HTTPError: if any error happens """ - logger.debug(f'Calling URL "{url}" with params {params}') + logging.debug('Calling URL "%s" with params %s', url, params) response = requests.get(url, params=params, headers=dict(HEADERS)) if not response.ok: response.raise_for_status() json_response = response.json() - logger.debug(f'TVmaze response:\n{pformat(json_response)}') + logging.debug('TVmaze response:\n%s', pformat(json_response)) return json_response @@ -68,7 +68,7 @@ def search_show(title: str) -> List[InfoType]: try: return _load_info(SEARCH_URL, {'q': title}) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) return [] @@ -86,7 +86,7 @@ def load_show_info(show_id: str) -> Optional[InfoType]: try: show_info = _load_info(show_info_url, params) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) return None if isinstance(show_info['_embedded']['images'], list): show_info['_embedded']['images'].sort(key=lambda img: img['main'], @@ -113,7 +113,7 @@ def load_show_info_by_external_id(provider: str, show_id: str) -> Optional[InfoT try: return _load_info(SEARCH_BY_EXTERNAL_ID_URL, query) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) return None @@ -123,7 +123,7 @@ def _get_alternate_episode_list_id(show_id: str, episode_order: str) -> Optional try: alternate_lists = _load_info(url) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) else: for episode_list in alternate_lists: if episode_list.get(episode_order): @@ -140,7 +140,7 @@ def load_alternate_episode_list(show_id: str, episode_order: str) -> Optional[Li try: raw_alternate_episodes = _load_info(url, {'embed': 'episodes'}) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) else: alternate_episodes = [] for episode in raw_alternate_episodes: @@ -163,7 +163,7 @@ def load_episode_list(show_id: str, episode_order: str) -> Optional[List[InfoTyp try: episode_list = _load_info(episode_list_url, {'specials': '1'}) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) return episode_list @@ -172,5 +172,5 @@ def load_episode_info(episode_id: Union[str, int]) -> Optional[InfoType]: try: return _load_info(url) except requests.HTTPError as exc: - logger.error(f'TVmaze returned an error: {exc}') + logging.error('TVmaze returned an error: %s', exc) return None diff --git a/metadata.tvmaze/libs/utils.py b/metadata.tvmaze/libs/utils.py index a512fd4b4..34a258a8c 100644 --- a/metadata.tvmaze/libs/utils.py +++ b/metadata.tvmaze/libs/utils.py @@ -14,6 +14,7 @@ # along with this program. If not, see . """Misc utils""" +import logging from typing import Text, Any, Dict import xbmc @@ -23,6 +24,8 @@ ADDON_ID = ADDON.getAddonInfo('id') VERSION = ADDON.getAddonInfo('version') +LOG_FORMAT = '[{addon_id} v.{addon_version}] {filename}:{lineno} - {message}' + EPISODE_ORDER_MAP = { 0: 'default', 1: 'dvd_release', @@ -34,29 +37,43 @@ } -class logger: - log_message_prefix = f'[{ADDON_ID} ({VERSION})]: ' +class KodiLogHandler(logging.Handler): + """ + Logging handler that writes to the Kodi log with correct levels - @staticmethod - def log(message: str, level: int = xbmc.LOGDEBUG) -> None: - message = logger.log_message_prefix + message - xbmc.log(message, level) + It also adds {addon_id} and {addon_version} variables available to log format. + """ + LEVEL_MAP = { + logging.NOTSET: xbmc.LOGNONE, + logging.DEBUG: xbmc.LOGDEBUG, + logging.INFO: xbmc.LOGINFO, + logging.WARN: xbmc.LOGWARNING, + logging.WARNING: xbmc.LOGWARNING, + logging.ERROR: xbmc.LOGERROR, + logging.CRITICAL: xbmc.LOGFATAL, + } - @classmethod - def info(cls, message: str) -> None: - cls.log(message, xbmc.LOGINFO) + def emit(self, record): + record.addon_id = ADDON_ID + record.addon_version = VERSION + message = self.format(record) + kodi_log_level = self.LEVEL_MAP.get(record.levelno, xbmc.LOGDEBUG) + xbmc.log(message, level=kodi_log_level) - @classmethod - def error(cls, message: str) -> None: - cls.log(message, xbmc.LOGERROR) - @classmethod - def debug(cls, message: str) -> None: - logger.log(message, xbmc.LOGDEBUG) +def initialize_logging(): + """ + Initialize the root logger that writes to the Kodi log - @classmethod - def warning(cls, message: str) -> None: - logger.log(message, xbmc.LOGWARNING) + After initialization, you can use Python logging facilities as usual. + """ + logging.basicConfig( + format=LOG_FORMAT, + style='{', + level=logging.DEBUG, + handlers=[KodiLogHandler()], + force=True + ) def get_episode_order(path_settings: Dict[Text, Any]) -> str: diff --git a/metadata.tvmaze/main.py b/metadata.tvmaze/main.py index 329a354e0..0ad66536d 100644 --- a/metadata.tvmaze/main.py +++ b/metadata.tvmaze/main.py @@ -13,11 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # pylint: disable=missing-docstring +import logging import sys from libs.actions import router -from libs.exception_logger import log_exception +from libs.exception_logger import catch_exception +from libs.utils import initialize_logging if __name__ == '__main__': - with log_exception(): + initialize_logging() + with catch_exception(logger_func=logging.error): router(sys.argv[2][1:])