jellyfin-kodi/resources/lib/API.py

603 lines
24 KiB
Python

# -- coding: utf-8 --
# API.py
# This class helps translate more complex cases from the MediaBrowser API to the XBMC API
from datetime import datetime
from random import randrange
import xbmc
import xbmcgui
import Utils as utils
class API():
def getPeople(self, item):
# Process People
director = []
writer = []
cast = []
try:
people = item['People']
except: pass
else:
for person in people:
type = person['Type']
Name = person['Name']
if "Director" in type:
director.append(Name)
elif "Writing" in type:
writer.append(Name)
elif "Writer" in type:
writer.append(Name)
elif "Actor" in type:
cast.append(Name)
return {
'Director': director,
'Writer': writer,
'Cast': cast
}
def getTimeInfo(self, item):
# Runtime and Resume point
tempRuntime = 0
runtime = 0
resume = 0
try: # Get resume point
userdata = item['UserData']
playbackPosition = userdata['PlaybackPositionTicks']
resume = playbackPosition / 10000000.0
except: pass
try: # Get total runtime
tempRuntime = item['RunTimeTicks']
except:
try: tempRuntime = item['CumulativeRunTimeTicks']
except: pass
finally:
runtime = tempRuntime / 10000000.0
return {
'ResumeTime': resume,
'TotalTime': runtime
}
def getStudios(self, item):
# Process Studio
studios = []
try:
studio = item['SeriesStudio']
studios.append(self.getStudio(studio))
except:
try:
studioArray = item['Studios']
for studio in studioArray:
studios.append(self.getStudio(studio['Name']))
except: pass
return studios
def getStudio(self, studioName):
# Convert studio for Kodi to properly detect them
studios = {
'abc (us)': "ABC",
'fox (us)': "FOX",
'mtv (us)': "MTV",
'showcase (ca)': "Showcase",
'wgn america': "WGN"
}
return studios.get(studioName.lower(), studioName)
def getGenre(self,item):
genre = ""
genres = item.get("Genres")
if genres != None and genres != []:
for genre_string in genres:
if genre == "": #Just take the first genre
genre = genre_string
else:
genre = genre + " / " + genre_string
elif item.get("SeriesGenres") != None and item.get("SeriesGenres") != '':
genres = item.get("SeriesGenres")
if genres != None and genres != []:
for genre_string in genres:
if genre == "": #Just take the first genre
genre = genre_string
else:
genre = genre + " / " + genre_string
return genre
def getMediaStreams(self, item, mediaSources = False):
videotracks = [] # Height, Width, Codec, AspectRatio, AspectFloat, 3D
audiotracks = [] # Codec, Channels, language
subtitlelanguages = [] # Language
if mediaSources:
try:
MediaStreams = item['MediaSources'][0]['MediaStreams']
except:
MediaStreams = None
else:
MediaStreams = item.get('MediaStreams')
if MediaStreams:
# Sort through the Video, Audio, Subtitle tracks
for mediaStream in MediaStreams:
type = mediaStream.get('Type', "")
profile = mediaStream.get('Profile', "").lower()
codec = mediaStream.get('Codec', "").lower()
if "Video" in type:
videotrack = {}
videotrack['videocodec'] = codec
container = item['MediaSources'][0].get('Container', "").lower()
if "msmpeg4" in videotrack['videocodec']:
videotrack['videocodec'] = "divx"
elif "mpeg4" in videotrack['videocodec']:
if "simple profile" in profile or profile == "":
videotrack['videocodec'] = "xvid"
elif "h264" in videotrack['videocodec']:
if container in ("mp4", "mov", "m4v"):
videotrack['videocodec'] = "avc1"
videotrack['height'] = mediaStream.get('Height')
videotrack['width'] = mediaStream.get('Width')
videotrack['Video3DFormat'] = item.get('Video3DFormat')
if item.get('AspectRatio'):
# Metadata aspect ratio
videotrack['aspectratio'] = item['AspectRatio']
else: # File aspect ratio
videotrack['aspectratio'] = mediaStream.get('AspectRatio', "0")
if len(videotrack['aspectratio']) >= 3:
try:
aspectwidth, aspectheight = videotrack['aspectratio'].split(':')
videotrack['aspectratio'] = round(float(aspectwidth) / float(aspectheight), 6)
except:
videotrack['aspectratio'] = 1.85
else:
try:
videotrack['aspectratio'] = round(float(videotrack['width'] / videotrack['height']), 6)
except: # In the event the aspect ratio is missing and the width and height are missing as well.
videotrack['aspectratio'] = 1.85
videotracks.append(videotrack)
elif "Audio" in type:
audiotrack = {}
audiotrack['audiocodec'] = codec
if "dca" in audiotrack['audiocodec'] and "dts-hd ma" in profile:
audiotrack['audiocodec'] = "dtshd_ma"
audiotrack['channels'] = mediaStream.get('Channels')
audiotrack['audiolanguage'] = mediaStream.get('Language')
audiotracks.append(audiotrack)
elif "Subtitle" in type:
try:
subtitlelanguages.append(mediaStream['Language'])
except:
subtitlelanguages.append("Unknown")
return {
'videocodec' : videotracks,
'audiocodec' : audiotracks,
'subtitlelanguage' : subtitlelanguages
}
def getChecksum(self, item):
# use the etags checksum for this if available
# AND the userdata
checksum = ""
if item.get("Etag") != None:
checksum = item.get("Etag")
userData = item.get("UserData")
if(userData != None):
checksum += str(userData.get("Played"))
checksum += str(userData.get("IsFavorite"))
if userData.get('UnplayedItemCount') != None:
checksum += str(userData.get("UnplayedItemCount"))
if userData.get('LastPlayedDate') != None:
checksum += str(userData.get("LastPlayedDate"))
if userData.get('PlaybackPositionTicks') != None:
checksum += str(userData.get("PlaybackPositionTicks"))
return checksum
def getUserData(self, item):
# Default
favorite = False
playcount = None
lastPlayedDate = None
userKey = ""
try:
userdata = item['UserData']
except: # No userdata found.
pass
else:
favorite = userdata['IsFavorite']
userKey = userdata.get('Key', "")
watched = userdata['Played']
if watched:
# Playcount is tied to the watch status
playcount = userdata['PlayCount']
if playcount == 0:
playcount = 1
else:
playcount = None
lastPlayedDate = userdata.get('LastPlayedDate', None)
if lastPlayedDate:
lastPlayedDate = lastPlayedDate.split('.')[0].replace('T', " ")
return {
'Favorite': favorite,
'PlayCount': playcount,
'LastPlayedDate': lastPlayedDate,
'Key': userKey
}
def getRecursiveItemCount(self, item):
if item.get("RecursiveItemCount") != None:
return str(item.get("RecursiveItemCount"))
else:
return "0"
def getOverview(self, item):
overview = ""
try:
overview = item['Overview']
overview = overview.replace("\"", "\'")
overview = overview.replace("\n", " ")
overview = overview.replace("\r", " ")
except: pass
return overview
def getTVInfo(self, item, userData):
TotalSeasons = 0 if item.get("ChildCount")==None else item.get("ChildCount")
TotalEpisodes = 0 if item.get("RecursiveItemCount")==None else item.get("RecursiveItemCount")
WatchedEpisodes = 0 if userData.get("UnplayedItemCount")==None else TotalEpisodes-int(userData.get("UnplayedItemCount"))
UnWatchedEpisodes = 0 if userData.get("UnplayedItemCount")==None else int(userData.get("UnplayedItemCount"))
NumEpisodes = TotalEpisodes
tempEpisode = ""
if (item.get("IndexNumber") != None):
episodeNum = item.get("IndexNumber")
if episodeNum < 10:
tempEpisode = "0" + str(episodeNum)
else:
tempEpisode = str(episodeNum)
tempSeason = ""
if (str(item.get("ParentIndexNumber")) != None):
tempSeason = str(item.get("ParentIndexNumber"))
if item.get("ParentIndexNumber") < 10:
tempSeason = "0" + tempSeason
if item.get("SeriesName") != None:
temp=item.get("SeriesName")
SeriesName=temp.encode('utf-8')
else:
SeriesName=''
return {'TotalSeasons' : str(TotalSeasons),
'TotalEpisodes' : str(TotalEpisodes),
'WatchedEpisodes' : str(WatchedEpisodes),
'UnWatchedEpisodes': str(UnWatchedEpisodes),
'NumEpisodes' : str(NumEpisodes),
'Season' : tempSeason,
'Episode' : tempEpisode,
'SeriesName' : SeriesName
}
def getDateCreated(self, item):
try:
dateadded = item['DateCreated']
dateadded = dateadded.split('.')[0].replace('T', " ")
except:
dateadded = None
return dateadded
def getPremiereDate(self, item):
try:
premiere = item['PremiereDate']
premiere = premiere.split('.')[0].replace('T', " ")
except:
premiere = None
return premiere
def getTagline(self, item):
try:
tagline = item['Taglines'][0]
except:
tagline = None
return tagline
def getProvider(self, item, providername):
# Provider Name: imdb or tvdb
try:
if "imdb" in providername:
provider = item['ProviderIds']['Imdb']
elif "tvdb" in providername:
provider = item['ProviderIds']['Tvdb']
elif "musicBrainzArtist" in providername:
provider = item['ProviderIds']['MusicBrainzArtist']
elif "musicBrainzAlbum" in providername:
provider = item['ProviderIds']['MusicBrainzAlbum']
elif "musicBrainzTrackId" in providername:
provider = item['ProviderIds']['MusicBrainzTrackId']
except:
provider = None
return provider
def getCountry(self, item):
try:
country = item['ProductionLocations'][0]
except:
country = None
return country
def getMpaa(self, item):
# Convert more complex cases
mpaa = item.get('OfficialRating', "")
if mpaa in ("NR", "UR"):
# Kodi seems to not like NR, but will accept Rated Not Rated
mpaa = "Rated Not Rated"
return mpaa
def getAllArtwork(self, item, parentInfo = False):
"""
Get all artwork, it will return an empty string
for the artwork type not found.
Artwork type: Primary, Art, Banner, Logo, Thumb,
Disc, Backdrop
"""
username = utils.window('currUser')
server = utils.window('server%s' % username)
id = item['Id']
artworks = item['ImageTags']
backdrops = item['BackdropImageTags']
maxHeight = 10000
maxWidth = 10000
customquery = ""
if utils.settings('compressArt') == "true":
customquery = "&Quality=90"
if utils.settings('disableCoverArt') == "true":
customquery += "&EnableImageEnhancers=false"
allartworks = {
'Primary': "",
'Art': "",
'Banner': "",
'Logo': "",
'Thumb': "",
'Disc': "",
'Backdrop': []
}
# Process backdrops
backdropIndex = 0
for backdroptag in backdrops:
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, backdropIndex, maxWidth, maxHeight, backdroptag, customquery)
allartworks['Backdrop'].append(artwork)
backdropIndex += 1
# Process the rest of the artwork
for art in artworks:
tag = artworks[art]
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, id, art, maxWidth, maxHeight, tag, customquery)
allartworks[art] = artwork
# Process parent items if the main item is missing artwork
if parentInfo:
# Process backdrops
if not allartworks['Backdrop']:
parentId = item.get('ParentBackdropItemId')
if parentId:
# If there is a parentId, go through the parent backdrop list
parentbackdrops = item['ParentBackdropImageTags']
backdropIndex = 0
for parentbackdroptag in parentbackdrops:
artwork = "%s/mediabrowser/Items/%s/Images/Backdrop/%s?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, backdropIndex, maxWidth, maxHeight, parentbackdroptag, customquery)
allartworks['Backdrop'].append(artwork)
backdropIndex += 1
# Process the rest of the artwork
parentartwork = ['Logo', 'Art', 'Thumb']
for parentart in parentartwork:
if not allartworks[parentart]:
parentId = item.get('Parent%sItemId' % parentart)
if parentId:
parentTag = item['Parent%sImageTag' % parentart]
artwork = "%s/mediabrowser/Items/%s/Images/%s/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, parentart, maxWidth, maxHeight, parentTag, customquery)
allartworks[parentart] = artwork
# Parent album works a bit differently
if not allartworks['Primary']:
parentId = item.get('AlbumId')
if parentId and item.get('AlbumPrimaryImageTag'):
parentTag = item['AlbumPrimaryImageTag']
artwork = "%s/mediabrowser/Items/%s/Images/Primary/0?MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" % (server, parentId, maxWidth, maxHeight, parentTag, customquery)
allartworks['Primary'] = artwork
return allartworks
def getArtwork(self, data, type, mediaType = "", index = "0", userParentInfo = False):
id = data.get("Id")
getSeriesData = False
userData = data.get("UserData")
if type == "tvshow.poster": # Change the Id to the series to get the overall series poster
if data.get("Type") == "Season" or data.get("Type")== "Episode":
id = data.get("SeriesId")
getSeriesData = True
elif type == "poster" and data.get("Type") == "Episode" and utils.settings('useSeasonPoster')=='true': # Change the Id to the Season to get the season poster
id = data.get("SeasonId")
if type == "poster" or type == "tvshow.poster": # Now that the Ids are right, change type to MB3 name
type="Primary"
if data.get("Type") == "Season": # For seasons: primary (poster), thumb and banner get season art, rest series art
if type != "Primary" and type != "Primary2" and type != "Primary3" and type != "Primary4" and type != "Thumb" and type != "Banner" and type!="Thumb3" and type!="Backdrop":
id = data.get("SeriesId")
getSeriesData = True
if data.get("Type") == "Episode": # For episodes: primary (episode thumb) gets episode art, rest series art.
if type != "Primary" and type != "Primary2" and type != "Primary3" and type != "Primary4":
id = data.get("SeriesId")
getSeriesData = True
if type =="Primary2" or type=="Primary3" or type=="Primary4":
id = data.get("SeasonId")
getSeriesData = True
if data.get("SeasonUserData") != None:
userData = data.get("SeasonUserData")
if id == None:
id=data.get("Id")
imageTag = "e3ab56fe27d389446754d0fb04910a34" # a place holder tag, needs to be in this format
originalType = type
if type == "Primary2" or type == "Primary3" or type == "Primary4" or type=="SeriesPrimary":
type = "Primary"
if type == "Backdrop2" or type=="Backdrop3" or type=="BackdropNoIndicators":
type = "Backdrop"
if type == "Thumb2" or type=="Thumb3":
type = "Thumb"
if(data.get("ImageTags") != None and data.get("ImageTags").get(type) != None):
imageTag = data.get("ImageTags").get(type)
if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Logo":
imageTag = data.get("ParentLogoImageTag")
if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Art":
imageTag = data.get("ParentArtImageTag")
if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Backdrop":
if data.get("BackdropImageTags"):
imageTag = data['BackdropImageTags'][0]
if (data.get("Type") == "Episode" and originalType=="Thumb3"):
imageTag = data.get("SeriesThumbImageTag")
if (data.get("Type") == "Season" and originalType=="Thumb3" and imageTag=="e3ab56fe27d389446754d0fb04910a34"):
imageTag = data.get("ParentThumbImageTag")
id = data.get("SeriesId")
# for music we return the parent art if no image exists
if (data.get("Type") == "MusicAlbum" or data.get("Type") == "Audio") and type=="Backdrop" and not data.get("BackdropImageTags"):
data["BackdropImageTags"] = data.get("ParentBackdropImageTags")
id = data.get("ParentBackdropItemId")
if (data.get("Type") == "MusicAlbum" or data.get("Type") == "Audio") and type=="Logo" and (not imageTag or imageTag == "e3ab56fe27d389446754d0fb04910a34"):
imageTag = data.get("ParentLogoImageTag")
id = data.get("ParentLogoItemId")
if (data.get("Type") == "MusicAlbum" or data.get("Type") == "Audio") and type=="Art" and (not imageTag or imageTag == "e3ab56fe27d389446754d0fb04910a34"):
imageTag = data.get("ParentArtImageTag")
id = data.get("ParentArtItemId")
query = ""
maxHeight = "10000"
maxWidth = "10000"
height = ""
width = ""
played = "0"
totalbackdrops = 0
if utils.settings('coverArtratio') == "true":
if mediaType in ("movie","boxset","tvshow"):
if "Primary" in type:
# Only force ratio for cover art for main covers
aspectratio = data.get("PrimaryImageAspectRatio")
width = "&Width=1000"
height = "&Height=1480"
if originalType =="BackdropNoIndicators" and index == "0" and data.get("BackdropImageTags") != None:
totalbackdrops = len(data.get("BackdropImageTags"))
if totalbackdrops != 0:
index = str(randrange(0,totalbackdrops))
# use the local image proxy server that is made available by this addons service
# Load user information set by UserClient
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
server = WINDOW.getProperty('server%s' % username)
if utils.settings('compressArt')=='true':
query = query + "&Quality=90"
if imageTag == None:
imageTag = "e3ab56fe27d389446754d0fb04910a34"
artwork = "%s/mediabrowser/Items/%s/Images/%s/%s?MaxWidth=%s&MaxHeight=%s%s%s&Format=original&Tag=%s%s" % (server, id, type, index, maxWidth, maxHeight, height, width, imageTag, query)
#artwork = "%s/mediabrowser/Items/%s/Images/%s/%s/%s/original/%s/%s/%s?%s" % (server, id, type, index, imageTag, width, height, played, query) <- broken
if utils.settings('disableCoverArt')=='true':
artwork = artwork + "&EnableImageEnhancers=false"
# do not return non-existing images
if ( (type!="Backdrop" and imageTag=="e3ab56fe27d389446754d0fb04910a34") | #Remember, this is the placeholder tag, meaning we didn't find a valid tag
(type=="Backdrop" and data.get("BackdropImageTags") != None and len(data.get("BackdropImageTags")) == 0) |
(type=="Backdrop" and data.get("BackdropImageTag") != None and len(data.get("BackdropImageTag")) == 0)
):
if type != "Backdrop" or (type=="Backdrop" and getSeriesData==True and data.get("ParentBackdropImageTags") == None) or (type=="Backdrop" and getSeriesData!=True):
artwork=''
return artwork
def imageUrl(self, id, type, index, width, height):
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
server = WINDOW.getProperty('server%s' % username)
# For people image - actors, directors, writers
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (server, id, type, width, height, index)
def getUserArtwork(self, data, type, index = "0"):
# Load user information set by UserClient
WINDOW = xbmcgui.Window(10000)
username = WINDOW.getProperty('currUser')
server = WINDOW.getProperty('server%s' % username)
id = data.get("Id")
artwork = "%s/mediabrowser/Users/%s/Images/%s?Format=original" % (server, id, type)
return artwork