This commit is contained in:
2025-10-25 13:21:06 +02:00
parent eb57506d39
commit 033ffb21f5
8388 changed files with 484789 additions and 16 deletions

View File

@@ -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)