-
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
#
|
||||
# Copyright (C) 2020, Team Kodi
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# pylint: disable=missing-docstring
|
||||
#
|
||||
# This is based on the metadata.tvmaze scrapper by Roman Miroshnychenko aka Roman V.M.
|
||||
|
||||
"""Plugin route actions"""
|
||||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import sys
|
||||
import json
|
||||
import urllib.parse
|
||||
import xbmcgui
|
||||
import xbmcplugin
|
||||
from . import tmdb, data_utils
|
||||
from .utils import logger, safe_get
|
||||
try:
|
||||
from typing import Optional, Text, Union, ByteString # pylint: disable=unused-import
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
HANDLE = int(sys.argv[1]) # type: int
|
||||
|
||||
|
||||
def find_show(title, year=None):
|
||||
# type: (Union[Text, bytes], Optional[Text]) -> None
|
||||
"""Find a show by title"""
|
||||
if not isinstance(title, str):
|
||||
title = title.decode('utf-8')
|
||||
logger.debug('Searching for TV show {} ({})'.format(title, year))
|
||||
search_results = tmdb.search_show(title, year)
|
||||
for search_result in search_results:
|
||||
show_name = search_result['name']
|
||||
if safe_get(search_result, 'first_air_date') is not None:
|
||||
show_name += ' ({})'.format(search_result['first_air_date'][:4])
|
||||
list_item = xbmcgui.ListItem(show_name, offscreen=True)
|
||||
show_info = search_result
|
||||
list_item = data_utils.add_main_show_info(
|
||||
list_item, show_info, full_info=False)
|
||||
# Below "url" is some unique ID string (may be an actual URL to a show page)
|
||||
# that is used to get information about a specific TV show.
|
||||
xbmcplugin.addDirectoryItem(
|
||||
HANDLE,
|
||||
url=str(search_result['id']),
|
||||
listitem=list_item,
|
||||
isFolder=True
|
||||
)
|
||||
|
||||
|
||||
def get_show_id_from_nfo(nfo):
|
||||
# type: (Text) -> None
|
||||
"""
|
||||
Get show ID by NFO file contents
|
||||
|
||||
This function is called first instead of find_show
|
||||
if a NFO file is found in a TV show folder.
|
||||
|
||||
:param nfo: the contents of a NFO file
|
||||
"""
|
||||
if isinstance(nfo, bytes):
|
||||
nfo = nfo.decode('utf-8', 'replace')
|
||||
logger.debug('Parsing NFO file:\n{}'.format(nfo))
|
||||
parse_result, named_seasons = data_utils.parse_nfo_url(nfo)
|
||||
if parse_result:
|
||||
if parse_result.provider == 'tmdb':
|
||||
show_info = tmdb.load_show_info(
|
||||
parse_result.show_id, ep_grouping=parse_result.ep_grouping, named_seasons=named_seasons)
|
||||
else:
|
||||
show_info = None
|
||||
if show_info is not None:
|
||||
list_item = xbmcgui.ListItem(show_info['name'], offscreen=True)
|
||||
# "url" is some string that unique identifies a show.
|
||||
# It may be an actual URL of a TV show page.
|
||||
xbmcplugin.addDirectoryItem(
|
||||
HANDLE,
|
||||
url=str(show_info['id']),
|
||||
listitem=list_item,
|
||||
isFolder=True
|
||||
)
|
||||
|
||||
|
||||
def get_show_id(unique_ids):
|
||||
"""
|
||||
Get show ID by unique IDs
|
||||
|
||||
In case there is a tmdb identifier in the unique IDs, use that.
|
||||
Else use the find_by_id method to get the show ID by an external ID.
|
||||
|
||||
:param unique_ids: dictionary of unique IDs
|
||||
"""
|
||||
if unique_ids.get('tmdb'):
|
||||
return unique_ids['tmdb']
|
||||
else:
|
||||
res = tmdb.find_by_id(unique_ids)
|
||||
if len(res) > 0:
|
||||
return res[0].get('id')
|
||||
return None
|
||||
|
||||
|
||||
def get_details(show_id):
|
||||
# type: (Text) -> None
|
||||
"""Get details about a specific show"""
|
||||
logger.debug('Getting details for show id {}'.format(show_id))
|
||||
show_info = tmdb.load_show_info(show_id)
|
||||
if show_info is not None:
|
||||
list_item = xbmcgui.ListItem(show_info['name'], offscreen=True)
|
||||
list_item = data_utils.add_main_show_info(
|
||||
list_item, show_info, full_info=True)
|
||||
xbmcplugin.setResolvedUrl(HANDLE, True, list_item)
|
||||
else:
|
||||
xbmcplugin.setResolvedUrl(
|
||||
HANDLE, False, xbmcgui.ListItem(offscreen=True))
|
||||
|
||||
|
||||
def get_episode_list(show_ids): # pylint: disable=missing-docstring
|
||||
# type: (Text) -> None
|
||||
# Kodi has a bug: when a show directory contains an XML NFO file with
|
||||
# episodeguide URL, that URL is always passed here regardless of
|
||||
# the actual parsing result in get_show_from_nfo()
|
||||
# so much of this weird logic is to deal with that
|
||||
try:
|
||||
all_ids = json.loads(show_ids)
|
||||
show_id = all_ids.get('tmdb')
|
||||
if not show_id:
|
||||
for key, value in all_ids.items():
|
||||
show_id = data_utils._convert_ext_id(key, value)
|
||||
if show_id:
|
||||
show_id = str(show_id)
|
||||
break
|
||||
if not show_id:
|
||||
show_id = str(show_ids)
|
||||
except (ValueError, AttributeError):
|
||||
show_id = str(show_ids)
|
||||
if show_id.isdigit():
|
||||
logger.error(
|
||||
'using deprecated episodeguide format, this show should be refreshed or rescraped')
|
||||
if not show_id:
|
||||
raise RuntimeError(
|
||||
'No TMDb TV show id found in episode guide, this show should be refreshed or rescraped')
|
||||
elif not show_id.isdigit():
|
||||
parse_result, named_seasons = data_utils.parse_nfo_url(show_id)
|
||||
if parse_result:
|
||||
show_id = parse_result.show_id
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'No TMDb TV show id found in episode guide, this show should be refreshed or rescraped')
|
||||
logger.debug('Getting episode list for show id {}'.format(show_id))
|
||||
show_info = tmdb.load_show_info(show_id)
|
||||
if show_info is not None:
|
||||
theindex = 0
|
||||
for episode in show_info['episodes']:
|
||||
epname = episode.get('name', 'Episode ' +
|
||||
str(episode['episode_number']))
|
||||
list_item = xbmcgui.ListItem(epname, offscreen=True)
|
||||
list_item = data_utils.add_episode_info(
|
||||
list_item, episode, full_info=False)
|
||||
encoded_ids = urllib.parse.urlencode(
|
||||
{'show_id': str(show_info['id']), 'episode_id': str(theindex)}
|
||||
)
|
||||
theindex = theindex + 1
|
||||
# Below "url" is some unique ID string (may be an actual URL to an episode page)
|
||||
# that allows to retrieve information about a specific episode.
|
||||
url = urllib.parse.quote(encoded_ids)
|
||||
xbmcplugin.addDirectoryItem(
|
||||
HANDLE,
|
||||
url=url,
|
||||
listitem=list_item,
|
||||
isFolder=True
|
||||
)
|
||||
else:
|
||||
logger.error(
|
||||
'unable to get show information using show id {}'.format(show_id))
|
||||
logger.error('you may need to refresh the show to get a valid show id')
|
||||
|
||||
|
||||
def get_episode_details(encoded_ids): # pylint: disable=missing-docstring
|
||||
# type: (Text) -> None
|
||||
encoded_ids = urllib.parse.unquote(encoded_ids)
|
||||
decoded_ids = dict(urllib.parse.parse_qsl(encoded_ids))
|
||||
logger.debug('Getting episode details for {}'.format(decoded_ids))
|
||||
episode_info = tmdb.load_episode_info(
|
||||
decoded_ids['show_id'], decoded_ids['episode_id']
|
||||
)
|
||||
if episode_info:
|
||||
list_item = xbmcgui.ListItem(episode_info['name'], offscreen=True)
|
||||
list_item = data_utils.add_episode_info(
|
||||
list_item, episode_info, full_info=True)
|
||||
xbmcplugin.setResolvedUrl(HANDLE, True, list_item)
|
||||
else:
|
||||
xbmcplugin.setResolvedUrl(
|
||||
HANDLE, False, xbmcgui.ListItem(offscreen=True))
|
||||
|
||||
|
||||
def get_artwork(show_id):
|
||||
# type: (Text) -> None
|
||||
"""
|
||||
Get available artwork for a show
|
||||
|
||||
:param show_id: default unique ID set by setUniqueIDs() method
|
||||
"""
|
||||
if not show_id:
|
||||
return
|
||||
logger.debug('Getting artwork for show ID {}'.format(show_id))
|
||||
show_info = tmdb.load_show_info(show_id)
|
||||
if show_info is not None:
|
||||
list_item = xbmcgui.ListItem(show_info['name'], offscreen=True)
|
||||
list_item = data_utils.set_show_artwork(show_info, list_item)
|
||||
xbmcplugin.setResolvedUrl(HANDLE, True, list_item)
|
||||
else:
|
||||
xbmcplugin.setResolvedUrl(
|
||||
HANDLE, False, xbmcgui.ListItem(offscreen=True))
|
||||
|
||||
|
||||
def router(paramstring):
|
||||
# type: (Text) -> None
|
||||
"""
|
||||
Route addon calls
|
||||
|
||||
:param paramstring: url-encoded query string
|
||||
:raises RuntimeError: on unknown call action
|
||||
"""
|
||||
params = dict(urllib.parse.parse_qsl(paramstring))
|
||||
logger.debug('Called addon with params: {}'.format(sys.argv))
|
||||
if params['action'] == 'find':
|
||||
logger.debug('performing find action')
|
||||
find_show(params['title'], params.get('year'))
|
||||
elif params['action'].lower() == 'nfourl':
|
||||
logger.debug('performing nfourl action')
|
||||
get_show_id_from_nfo(params['nfo'])
|
||||
elif params['action'] == 'getdetails':
|
||||
logger.debug('performing getdetails action')
|
||||
show_id = params.get('url') or get_show_id(json.loads(params.get('uniqueIDs')))
|
||||
if show_id:
|
||||
get_details(show_id)
|
||||
elif params['action'] == 'getepisodelist':
|
||||
logger.debug('performing getepisodelist action')
|
||||
get_episode_list(params['url'])
|
||||
elif params['action'] == 'getepisodedetails':
|
||||
logger.debug('performing getepisodedetails action')
|
||||
get_episode_details(params['url'])
|
||||
elif params['action'] == 'getartwork':
|
||||
logger.debug('performing getartwork action')
|
||||
get_artwork(params.get('id'))
|
||||
else:
|
||||
raise RuntimeError('Invalid addon call: {}'.format(sys.argv))
|
||||
xbmcplugin.endOfDirectory(HANDLE)
|
||||
Reference in New Issue
Block a user