Skip to content

Commit

Permalink
[metadata.tvmaze] 1.3.5
Browse files Browse the repository at this point in the history
  • Loading branch information
romanvm committed Dec 13, 2023
1 parent e08620c commit a0b3b08
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 109 deletions.
5 changes: 2 additions & 3 deletions metadata.tvmaze/addon.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="metadata.tvmaze"
name="TVmaze"
version="1.3.4"
version="1.3.5"
provider-name="Roman V.M.">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
Expand All @@ -20,8 +20,7 @@ We provide an API that can be used by anyone or service like Kodi to retrieve TV
</assets>
<website>https://www.tvmaze.com</website>
<source>https://github.com/romanvm/kodi.tvmaze</source>
<news>1.3.4:
- Internal changes.
<news>1.3.4: Fix incompatibility with filename tags in Kodi "Omega".
</news>
<reuselanguageinvoker>true</reuselanguageinvoker>
</extension>
Expand Down
42 changes: 26 additions & 16 deletions metadata.tvmaze/libs/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"""Plugin route actions"""

import json
import logging
import sys
from typing import Optional
from urllib import parse as urllib_parse
Expand All @@ -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])

Expand Down Expand Up @@ -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 '<episodedetails>' in nfo:
if full_nfo:
Expand Down Expand Up @@ -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)
Expand All @@ -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'],
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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':
Expand Down
17 changes: 9 additions & 8 deletions metadata.tvmaze/libs/cache_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,22 @@
"""Cache-related functionality"""

import json
import logging
import os
import time
from typing import Optional, Text, Dict, Any, Union

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:
Expand All @@ -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']


Expand Down Expand Up @@ -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
10 changes: 5 additions & 5 deletions metadata.tvmaze/libs/data_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit a0b3b08

Please sign in to comment.