API code optimization

This commit is contained in:
croneter 2018-02-12 08:10:39 +01:00
parent e02e9bcd1f
commit 0b5cd46d6c
2 changed files with 71 additions and 131 deletions

View file

@ -102,7 +102,7 @@ class API(object):
""" """
Returns the type of media, e.g. 'movie' or 'clip' for trailers Returns the type of media, e.g. 'movie' or 'clip' for trailers
""" """
return self.item.attrib.get('type') return self.item.get('type')
def checksum(self): def checksum(self):
""" """
@ -110,21 +110,19 @@ class API(object):
WATCH OUT - time in Plex, not Kodi ;-) WATCH OUT - time in Plex, not Kodi ;-)
""" """
# Include a letter to prohibit saving as an int! # Include a letter to prohibit saving as an int!
checksum = "K%s%s" % (self.plex_id(), return "K%s%s" % (self.plex_id(), self.item.get('updatedAt', ''))
self.item.attrib.get('updatedAt', ''))
return checksum
def plex_id(self): def plex_id(self):
""" """
Returns the Plex ratingKey such as '246922' as a string or None Returns the Plex ratingKey such as '246922' as a string or None
""" """
return self.item.attrib.get('ratingKey') return self.item.get('ratingKey')
def path_and_plex_id(self): def path_and_plex_id(self):
""" """
Returns the Plex key such as '/library/metadata/246922' or None Returns the Plex key such as '/library/metadata/246922' or None
""" """
return self.item.attrib.get('key') return self.item.get('key')
def plex_media_streams(self): def plex_media_streams(self):
""" """
@ -170,9 +168,9 @@ class API(object):
# Let Plex transcode # Let Plex transcode
# max width/height supported by plex image transcoder is 1920x1080 # max width/height supported by plex image transcoder is 1920x1080
path = self.server + PF.transcode_image_path( path = self.server + PF.transcode_image_path(
self.item[0][0].attrib.get('key'), self.item[0][0].get('key'),
window('pms_token'), window('pms_token'),
"%s%s" % (self.server, self.item[0][0].attrib.get('key')), "%s%s" % (self.server, self.item[0][0].get('key')),
1920, 1920,
1080) 1080)
else: else:
@ -191,14 +189,14 @@ class API(object):
res = None res = None
for child in self.item: for child in self.item:
if child.tag == 'Location': if child.tag == 'Location':
res = child.attrib.get('path') res = child.get('path')
return res return res
def season_number(self): def season_number(self):
""" """
Returns the 'index' of an PMS XML reply. Depicts e.g. season number. Returns the 'index' of an PMS XML reply. Depicts e.g. season number.
""" """
return self.item.attrib.get('index') return self.item.get('index')
def date_created(self): def date_created(self):
""" """
@ -206,7 +204,7 @@ class API(object):
If not found, returns 2000-01-01 10:00:00 If not found, returns 2000-01-01 10:00:00
""" """
res = self.item.attrib.get('addedAt') res = self.item.get('addedAt')
if res is not None: if res is not None:
res = unix_date_to_kodi(res) res = unix_date_to_kodi(res)
else: else:
@ -343,8 +341,8 @@ class API(object):
name = child.attrib['tag'] name = child.attrib['tag']
name_id = child.attrib['id'] name_id = child.attrib['id']
typus = PEOPLE_OF_INTEREST[child.tag] typus = PEOPLE_OF_INTEREST[child.tag]
url = child.attrib.get('thumb') url = child.get('thumb')
role = child.attrib.get('role') role = child.get('role')
people.append({ people.append({
'Name': name, 'Name': name,
'Type': typus, 'Type': typus,
@ -373,9 +371,8 @@ class API(object):
Return IMDB, e.g. "tt0903624". Returns None if not found Return IMDB, e.g. "tt0903624". Returns None if not found
""" """
item = self.item.attrib
try: try:
item = item['guid'] item = self.item.attrib['guid']
except KeyError: except KeyError:
return None return None
@ -402,29 +399,29 @@ class API(object):
sorttitle = title, if no sorttitle is found sorttitle = title, if no sorttitle is found
""" """
title = self.item.attrib.get('title', 'Missing Title Name') title = self.item.get('title', 'Missing Title Name')
sorttitle = self.item.attrib.get('titleSort', title) sorttitle = self.item.get('titleSort', title)
return title, sorttitle return title, sorttitle
def plot(self): def plot(self):
""" """
Returns the plot or None. Returns the plot or None.
""" """
return self.item.attrib.get('summary', None) return self.item.get('summary')
def tagline(self): def tagline(self):
""" """
Returns a shorter tagline or None Returns a shorter tagline or None
""" """
return self.item.attrib.get('tagline', None) return self.item.get('tagline')
def audience_rating(self): def audience_rating(self):
""" """
Returns the audience rating, 'rating' itself or 0.0 Returns the audience rating, 'rating' itself or 0.0
""" """
res = self.item.attrib.get('audienceRating') res = self.item.get('audienceRating')
if res is None: if res is None:
res = self.item.attrib.get('rating') res = self.item.get('rating')
try: try:
res = float(res) res = float(res)
except (ValueError, TypeError): except (ValueError, TypeError):
@ -435,7 +432,7 @@ class API(object):
""" """
Returns the production(?) year ("year") or None Returns the production(?) year ("year") or None
""" """
return self.item.attrib.get('year', None) return self.item.get('year')
def resume_point(self): def resume_point(self):
""" """
@ -456,17 +453,14 @@ class API(object):
Output is the tuple: Output is the tuple:
resume, runtime as ints. 0 if not found resume, runtime as ints. 0 if not found
""" """
item = self.item.attrib
try: try:
runtime = float(item['duration']) runtime = float(self.item.attrib['duration'])
except (KeyError, ValueError): except (KeyError, ValueError):
runtime = 0.0 runtime = 0.0
try: try:
resume = float(item['viewOffset']) resume = float(self.item.attrib['viewOffset'])
except (KeyError, ValueError): except (KeyError, ValueError):
resume = 0.0 resume = 0.0
runtime = int(runtime * v.PLEX_TO_KODI_TIMEFACTOR) runtime = int(runtime * v.PLEX_TO_KODI_TIMEFACTOR)
resume = int(resume * v.PLEX_TO_KODI_TIMEFACTOR) resume = int(resume * v.PLEX_TO_KODI_TIMEFACTOR)
return resume, runtime return resume, runtime
@ -475,7 +469,7 @@ class API(object):
""" """
Get the content rating or None Get the content rating or None
""" """
mpaa = self.item.attrib.get('contentRating', None) mpaa = self.item.get('contentRating', None)
# Convert more complex cases # Convert more complex cases
if mpaa in ("NR", "UR"): if mpaa in ("NR", "UR"):
# Kodi seems to not like NR, but will accept Rated Not Rated # Kodi seems to not like NR, but will accept Rated Not Rated
@ -496,13 +490,13 @@ class API(object):
""" """
Returns the "originallyAvailableAt" or None Returns the "originallyAvailableAt" or None
""" """
return self.item.attrib.get('originallyAvailableAt') return self.item.get('originallyAvailableAt')
def music_studio(self): def music_studio(self):
""" """
Returns the 'studio' or None Returns the 'studio' or None
""" """
return self.item.attrib.get('studio') return self.item.get('studio')
def music_studio_list(self): def music_studio_list(self):
""" """
@ -532,7 +526,7 @@ class API(object):
@staticmethod @staticmethod
def list_to_string(listobject): def list_to_string(listobject):
""" """
Smart-joins the listobject into a single string using a " / " separator. Smart-joins the listobject into a single string using a " / " separator
If the list is empty, smart_join returns an empty string. If the list is empty, smart_join returns an empty string.
""" """
string = " / ".join(listobject) string = " / ".join(listobject)
@ -542,7 +536,7 @@ class API(object):
""" """
Returns the 'parentRatingKey' as a string or None Returns the 'parentRatingKey' as a string or None
""" """
return self.item.attrib.get('parentRatingKey') return self.item.get('parentRatingKey')
def episode_data(self): def episode_data(self):
""" """
@ -581,13 +575,9 @@ class API(object):
Returns current playQueueItemID or if unsuccessful the playListItemID Returns current playQueueItemID or if unsuccessful the playListItemID
If not found, None is returned If not found, None is returned
""" """
try: answ = self.item.get('playQueueItemID')
answ = self.item.attrib['playQueueItemID'] if answ is None:
except KeyError: answ = self.item.get('playListItemID')
try:
answ = self.item.attrib['playListItemID']
except KeyError:
answ = None
return answ return answ
def _data_from_part_or_media(self, key): def _data_from_part_or_media(self, key):
@ -597,17 +587,10 @@ class API(object):
If all fails, None is returned. If all fails, None is returned.
""" """
media = self.item[0].attrib answ = self.item[0][self.part].get(key)
part = self.item[0][self.part].attrib if answ is None:
answ = self.item[0].get(key)
try: return answ
try:
value = part[key]
except KeyError:
value = media[key]
except KeyError:
value = None
return value
def video_codec(self): def video_codec(self):
""" """
@ -638,53 +621,24 @@ class API(object):
'container': self._data_from_part_or_media('container'), 'container': self._data_from_part_or_media('container'),
} }
try: try:
answ['bitDepth'] = self.item[0][self.part][self.mediastream].get('bitDepth') answ['bitDepth'] = self.item[0][self.part][self.mediastream].get(
'bitDepth')
except (TypeError, AttributeError, KeyError, IndexError): except (TypeError, AttributeError, KeyError, IndexError):
answ['bitDepth'] = None answ['bitDepth'] = None
return answ return answ
def extras_list(self): def trailer_id(self):
""" """
Currently ONLY returns the very first trailer found! Returns the ratingKey (plex_id) of the trailer or None
Returns a list of trailer and extras from PMS XML. Returns [] if
no extras are found.
Extratypes:
1: Trailer
5: Behind the scenes
Output: list of dicts with one entry of the form:
'ratingKey': e.g. '12345'
'title':
'thumb': artwork
'duration':
'extraType':
'originallyAvailableAt':
'year':
""" """
elements = [] for extra in self.item.iterfind('Extras'):
extras = self.item.find('Extras')
if extras is None:
return elements
for extra in extras:
try: try:
typus = int(extra.attrib['extraType']) typus = int(extra.attrib['extraType'])
except (KeyError, TypeError): except (KeyError, TypeError):
typus = None typus = None
if typus != 1: if typus != 1:
continue continue
duration = float(extra.attrib.get('duration', 0.0)) return extra.get('ratingKey')
elements.append({
'ratingKey': extra.attrib.get('ratingKey'),
'title': extra.attrib.get('title'),
'thumb': extra.attrib.get('thumb'),
'duration': int(duration * v.PLEX_TO_KODI_TIMEFACTOR),
'extraType': typus,
'originallyAvailableAt': extra.attrib.get('originallyAvailableAt'),
'year': extra.attrib.get('year')
})
break
return elements
def mediastreams(self): def mediastreams(self):
""" """
@ -704,7 +658,7 @@ class API(object):
subtitlelanguages = [] subtitlelanguages = []
try: try:
# Sometimes, aspectratio is on the "toplevel" # Sometimes, aspectratio is on the "toplevel"
aspect = self.item[0].attrib.get('aspectRatio') aspect = self.item[0].get('aspectRatio')
except IndexError: except IndexError:
# There is no stream info at all, returning empty # There is no stream info at all, returning empty
return { return {
@ -714,7 +668,7 @@ class API(object):
} }
# Loop over parts # Loop over parts
for child in self.item[0]: for child in self.item[0]:
container = child.attrib.get('container') container = child.get('container')
# Loop over Streams # Loop over Streams
for grandchild in child: for grandchild in child:
stream = grandchild.attrib stream = grandchild.attrib
@ -789,30 +743,19 @@ class API(object):
} }
""" """
allartworks = { allartworks = {
'Primary': "", # corresponds to Plex poster ('thumb') 'Primary': self._one_artwork('thumb'),
'Art': "", 'Art': "",
'Banner': "", # corresponds to Plex banner ('banner') for series 'Banner': self._one_artwork('banner'),
'Logo': "", 'Logo': "",
'Thumb': "", # corresponds to Plex (grand)parent posters (thumb) 'Thumb': self._one_artwork('grandparentThumb'),
'Disc': "", 'Disc': "",
'Backdrop': [] # Corresponds to Plex fanart ('art') 'Backdrop': [self._one_artwork('art')]
} }
# Process backdrops
# Get background artwork URL
allartworks['Backdrop'].append(self._one_artwork('art'))
# Get primary "thumb" pictures:
allartworks['Primary'] = self._one_artwork('thumb')
# Banner (usually only on tv series level)
allartworks['Banner'] = self._one_artwork('banner')
# For e.g. TV shows, get series thumb
allartworks['Thumb'] = self._one_artwork('grandparentThumb')
# Process parent items if the main item is missing artwork # Process parent items if the main item is missing artwork
if parent_info: if parent_info:
# Process parent backdrops # Process parent backdrops
if not allartworks['Backdrop']: if not allartworks['Backdrop']:
allartworks['Backdrop'].append( allartworks['Backdrop'].append(self._one_artwork('parentArt'))
self._one_artwork('parentArt'))
if not allartworks['Primary']: if not allartworks['Primary']:
allartworks['Primary'] = self._one_artwork('parentThumb') allartworks['Primary'] = self._one_artwork('parentThumb')
return allartworks return allartworks
@ -863,7 +806,7 @@ class API(object):
LOG.info('Plex did not provide ID for IMDB or TVDB. Start ' LOG.info('Plex did not provide ID for IMDB or TVDB. Start '
'lookup process') 'lookup process')
else: else:
LOG.info('Start movie set/collection lookup on themoviedb using %s', LOG.info('Start movie set/collection lookup on themoviedb with %s',
item.get('title', '')) item.get('title', ''))
api_key = settings('themoviedbAPIKey') api_key = settings('themoviedbAPIKey')
@ -936,7 +879,7 @@ class API(object):
break break
# if a match was not found, we accept the closest match from TMDB # if a match was not found, we accept the closest match from TMDB
if match_found is None and len(data.get("results")): if match_found is None and data.get("results"):
LOG.info('Using very first match from themoviedb') LOG.info('Using very first match from themoviedb')
match_found = entry = data.get("results")[0] match_found = entry = data.get("results")[0]
@ -1068,14 +1011,16 @@ class API(object):
# select image in preferred language # select image in preferred language
for entry in data[fanarttvimage]: for entry in data[fanarttvimage]:
if entry.get("lang") == v.KODILANGUAGE: if entry.get("lang") == v.KODILANGUAGE:
allartworks[fanarttype[1]] = entry.get("url", "").replace(' ', '%20') allartworks[fanarttype[1]] = \
entry.get("url", "").replace(' ', '%20')
break break
# just grab the first english OR undefinded one as fallback # just grab the first english OR undefinded one as fallback
# (so we're actually grabbing the more popular one) # (so we're actually grabbing the more popular one)
if not allartworks.get(fanarttype[1]): if not allartworks.get(fanarttype[1]):
for entry in data[fanarttvimage]: for entry in data[fanarttvimage]:
if entry.get("lang") in ("en", "00"): if entry.get("lang") in ("en", "00"):
allartworks[fanarttype[1]] = entry.get("url", "").replace(' ', '%20') allartworks[fanarttype[1]] = \
entry.get("url", "").replace(' ', '%20')
break break
# grab extrafanarts in list # grab extrafanarts in list
@ -1148,7 +1093,7 @@ class API(object):
Returns True if the item's 'optimizedForStreaming' is set, False other- Returns True if the item's 'optimizedForStreaming' is set, False other-
wise wise
""" """
return self.item[0].attrib.get('optimizedForStreaming') == '1' return self.item[0].get('optimizedForStreaming') == '1'
def mediastream_number(self): def mediastream_number(self):
""" """
@ -1158,17 +1103,17 @@ class API(object):
""" """
# How many streams do we have? # How many streams do we have?
count = 0 count = 0
for entry in self.item.findall('./Media'): for entry in self.item.iterfind('./Media'):
count += 1 count += 1
if (count > 1 and ( if (count > 1 and (
(self.plex_type() != 'clip' and (self.plex_type() != 'clip' and
settings('bestQuality') == 'false') settings('bestQuality') == 'false')
or or
(self.plex_type() == 'clip' and (self.plex_type() == 'clip' and
settings('bestTrailer') == 'false'))): settings('bestTrailer') == 'false'))):
# Several streams/files available. # Several streams/files available.
dialoglist = [] dialoglist = []
for entry in self.item.findall('./Media'): for entry in self.item.iterfind('./Media'):
# Get additional info (filename / languages) # Get additional info (filename / languages)
filename = None filename = None
if 'file' in entry[0].attrib: if 'file' in entry[0].attrib:
@ -1189,17 +1134,17 @@ class API(object):
option = '%s: ' % try_encode(languages) option = '%s: ' % try_encode(languages)
if 'videoResolution' in entry.attrib: if 'videoResolution' in entry.attrib:
option = '%s%sp ' % (option, option = '%s%sp ' % (option,
entry.attrib.get('videoResolution')) entry.get('videoResolution'))
if 'videoCodec' in entry.attrib: if 'videoCodec' in entry.attrib:
option = '%s%s' % (option, option = '%s%s' % (option,
entry.attrib.get('videoCodec')) entry.get('videoCodec'))
option = option.strip() + ' - ' option = option.strip() + ' - '
if 'audioProfile' in entry.attrib: if 'audioProfile' in entry.attrib:
option = '%s%s ' % (option, option = '%s%s ' % (option,
entry.attrib.get('audioProfile')) entry.get('audioProfile'))
if 'audioCodec' in entry.attrib: if 'audioCodec' in entry.attrib:
option = '%s%s ' % (option, option = '%s%s ' % (option,
entry.attrib.get('audioCodec')) entry.get('audioCodec'))
dialoglist.append(option) dialoglist.append(option)
media = dialog('select', 'Select stream', dialoglist) media = dialog('select', 'Select stream', dialoglist)
else: else:
@ -1262,7 +1207,7 @@ class API(object):
'directPlay': 0, 'directPlay': 0,
'directStream': 1, 'directStream': 1,
'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls' 'protocol': 'hls', # seen in the wild: 'dash', 'http', 'hls'
'session': window('plex_client_Id'), 'session': window('plex_client_Id'),
'fastSeek': 1, 'fastSeek': 1,
'path': path, 'path': path,
'mediaIndex': self.mediastream, 'mediaIndex': self.mediastream,
@ -1293,16 +1238,16 @@ class API(object):
for stream in mediastreams: for stream in mediastreams:
# Since plex returns all possible tracks together, have to pull # Since plex returns all possible tracks together, have to pull
# only external subtitles - only for these a 'key' exists # only external subtitles - only for these a 'key' exists
if stream.attrib.get('streamType') != "3": if stream.get('streamType') != "3":
# Not a subtitle # Not a subtitle
continue continue
# Only set for additional external subtitles NOT lying beside video # Only set for additional external subtitles NOT lying beside video
key = stream.attrib.get('key') key = stream.get('key')
# Only set for dedicated subtitle files lying beside video # Only set for dedicated subtitle files lying beside video
# ext = stream.attrib.get('format') # ext = stream.attrib.get('format')
if key: if key:
# We do know the language - temporarily download # We do know the language - temporarily download
if stream.attrib.get('languageCode') is not None: if stream.get('languageCode') is not None:
path = self.download_external_subtitles( path = self.download_external_subtitles(
"{server}%s" % key, "{server}%s" % key,
"subtitle%02d.%s.%s" % (fileindex, "subtitle%02d.%s.%s" % (fileindex,
@ -1390,9 +1335,9 @@ class API(object):
listitem.setLabel(title) listitem.setLabel(title)
metadata = { metadata = {
'date': self.kodi_premiere_date(), 'date': self.kodi_premiere_date(),
'size': long(self.item[0][0].attrib.get('size', 0)), 'size': long(self.item[0][0].get('size', 0)),
'exif:width': self.item[0].attrib.get('width', ''), 'exif:width': self.item[0].get('width', ''),
'exif:height': self.item[0].attrib.get('height', ''), 'exif:height': self.item[0].get('height', ''),
} }
listitem.setInfo(type='image', infoLabels=metadata) listitem.setInfo(type='image', infoLabels=metadata)
listitem.setProperty('plot', self.plot()) listitem.setProperty('plot', self.plot())

View file

@ -255,15 +255,10 @@ class Movies(Items):
except IndexError: except IndexError:
studio = None studio = None
# Find one trailer trailer = api.trailer_id()
trailer = None if trailer:
extras = api.extras_list() trailer = ('plugin://%s?plex_id=%s&plex_type=%s&mode=play'
for extra in extras: % (v.ADDON_ID, trailer, v.PLEX_TYPE_CLIP))
# Only get 1st trailer element
if extra['extraType'] == 1:
trailer = ('plugin://%s?plex_id=%s&plex_type=%s&mode=play'
% (v.ADDON_ID, extra['ratingKey'], v.PLEX_TYPE_CLIP))
break
# GET THE FILE AND PATH ##### # GET THE FILE AND PATH #####
do_indirect = not state.DIRECT_PATHS do_indirect = not state.DIRECT_PATHS