Additional artwork download from FanartTV!

This commit is contained in:
tomkat83 2016-04-29 12:44:56 +02:00
parent 0d96f6bf4f
commit d879a9d23c
7 changed files with 252 additions and 161 deletions

View file

@ -302,6 +302,7 @@
<string id="30536">Users must log in every time Kodi restarts</string> <string id="30536">Users must log in every time Kodi restarts</string>
<string id="30537">RESTART KODI IF YOU MAKE ANY CHANGES</string> <string id="30537">RESTART KODI IF YOU MAKE ANY CHANGES</string>
<string id="30538">Complete Re-Sync necessary</string> <string id="30538">Complete Re-Sync necessary</string>
<string id="30539">Download additional art from FanArtTV (slower!)</string>
<!-- service add-on --> <!-- service add-on -->
@ -404,6 +405,7 @@
<string id="39058">Extend Plex TV Series "On Deck" view to all shows</string> <string id="39058">Extend Plex TV Series "On Deck" view to all shows</string>
<string id="39059">Recently Added: Append show title to episode</string> <string id="39059">Recently Added: Append show title to episode</string>
<string id="39060">Recently Added: Append season- and episode-number SxxExx</string> <string id="39060">Recently Added: Append season- and episode-number SxxExx</string>
<string id="39061">Would you like to download additional artwork from FanArtTV? Sync will be slower!</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->
<string id="39200">Log-out Plex Home User </string> <string id="39200">Log-out Plex Home User </string>

View file

@ -28,6 +28,8 @@
<string id="30536">Benutzer müssen sich bei jedem Neustart von Kodi neu anmelden</string> <string id="30536">Benutzer müssen sich bei jedem Neustart von Kodi neu anmelden</string>
<string id="30537">BEI ÄNDERUNGEN KODI NEU STARTEN</string> <string id="30537">BEI ÄNDERUNGEN KODI NEU STARTEN</string>
<string id="30538">Komplette Neusynchronisierung nötig</string> <string id="30538">Komplette Neusynchronisierung nötig</string>
<string id="30539">Zusätzliche Bilder von FanArtTV herunterladen (langsamer!)</string>
@ -342,8 +344,7 @@
<string id="39058">Standard Plex Ansicht "Aktuell" auf alle TV Shows erweitern</string> <string id="39058">Standard Plex Ansicht "Aktuell" auf alle TV Shows erweitern</string>
<string id="39059">"Zuletzt hinzugefügt": Serien- an Episoden-Titel anfügen</string> <string id="39059">"Zuletzt hinzugefügt": Serien- an Episoden-Titel anfügen</string>
<string id="39060">"Zuletzt hinzugefügt": Staffel und Episode anfügen, SxxExx</string> <string id="39060">"Zuletzt hinzugefügt": Staffel und Episode anfügen, SxxExx</string>
<string id="39061">Zusätzliche Bilder von FanArtTV herunterladen? Die Synchronisierung wird länger dauern!</string>
<!-- Plex Entrypoint.py --> <!-- Plex Entrypoint.py -->
<string id="39200">Plex Home Benutzer abmelden: </string> <string id="39200">Plex Home Benutzer abmelden: </string>

View file

@ -1825,16 +1825,14 @@ class API():
Output: Output:
{ {
'Primary' : xml key 'thumb' 'Primary'
'Art' : always '' 'Art'
'Banner' : xml key 'banner' 'Banner'
'Logo' : always '' 'Logo'
'Thumb' : xml key 'grandparentThumb' 'Thumb'
'Disc' : always '' 'Disc'
'Backdrop' : LIST with ONE xml key "art" 'Backdrop' : LIST with the first entry xml key "art"
} }
""" """
item = self.item.attrib item = self.item.attrib
@ -1865,161 +1863,255 @@ class API():
self.__getOneArtwork('parentArt')) self.__getOneArtwork('parentArt'))
if not allartworks['Primary']: if not allartworks['Primary']:
allartworks['Primary'] = self.__getOneArtwork('parentThumb') allartworks['Primary'] = self.__getOneArtwork('parentThumb')
# Plex does not get much artwork - go ahead and get the rest from
# fanart tv only for movie or tv show
if utils.settings('FanartTV') == 'true':
if item.get('type') in ('movie', 'show'):
externalId = self.getExternalItemId()
if externalId is not None:
allartworks = self.getFanartTVArt(externalId, allartworks)
return allartworks return allartworks
# TO BE DONE def getExternalItemId(self):
# Plex does not get much artwork - go ahead and get the rest from fanart tv only for movie or tv show """
type = item.get('type') Returns the item's IMDB id for movies or tvdb id for TV shows
if type=='movie' or type=='show':
allartworks = self.getfanartTVimages(allartworks)
if allartworks == None: If not found in item's Plex metadata, check themovidedb.org
self.logMsg('No artwork found for title%s' %str(item.get('title'))) """
return {}
else:
return allartworks
def getfanartTVimages(self,allartworks):
item = self.item.attrib item = self.item.attrib
tmdb_apiKey = "ae06df54334aa653354e9a010f4b81cb" media_type = item.get('type')
externalId = None
if media_type == 'movie':
externalId = self.getProvider('imdb')
elif media_type == 'show':
externalId = self.getProvider('tvdb')
if externalId is not None:
return externalId
self.logMsg('Plex did not provide ID for IMDB or TVDB. Start lookup '
'process', 1)
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1) KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
media_id = None apiKey = "ae06df54334aa653354e9a010f4b81cb"
media_type = None if media_type == 'show':
type = item.get('type') media_type = 'tv'
if type == 'show': title = item.get('title', '')
type = 'tv' # if the title has the year in remove it as tmdb cannot deal with it...
title = item.get('title') # replace e.g. 'The Americans (2015)' with 'The Americans'
# if the title has the year in remove it as tmdb cannot deal with it...making an assumption it is something like The Americans (2015) title = re.sub(r'\s*\(\d{4}\)$', '', title, count=1)
if title.endswith(")"): title = title[:-6] url = 'http://api.themoviedb.org/3/search/%s' % media_type
year = item.get('year') parameters = {
if not type: type="multi" 'api_key': apiKey,
'language': KODILANGUAGE,
'query': title.encode('utf-8', errors='ignore')
}
data = downloadutils.DownloadUtils().downloadUrl(
url,
authenticate=False,
parameters=parameters,
timeout=7)
try: try:
url = 'http://api.themoviedb.org/3/search/%s?api_key=%s&language=%s&query=%s' %(type,tmdb_apiKey,KODILANGUAGE,utils.try_encode(title)) data.get('test')
response = requests.get(url, timeout=5) except:
if response.status_code == 200: self.logMsg('Could not download data from FanartTV', -1)
data = json.loads(response.content.decode('utf-8','replace')) return
#find year match if data.get('results') is None:
if data and year and data.get("results"): self.logMsg('No match found on themoviedb for type: %s, title: %s'
for item in data["results"]: % (media_type, title), 1)
if item.get("first_air_date") and year in item.get("first_air_date"): return
matchFound = item
break
elif item.get("release_date") and year in item.get("release_date"):
matchFound = item
break
#find exact match based on title
if not matchFound and data and data.get("results",None):
for item in data["results"]:
name = item.get("name")
if not name: name = item.get("title")
original_name = item.get("original_name","")
title_alt = title.lower().replace(" ","").replace("-","").replace(":","").replace("&","").replace(",","")
name_alt = name.lower().replace(" ","").replace("-","").replace(":","").replace("&","").replace(",","")
org_name_alt = original_name.lower().replace(" ","").replace("-","").replace(":","").replace("&","").replace(",","")
if name == title or original_name == title:
#match found for exact title name
matchFound = item
break
elif name.split(" (")[0] == title or title_alt == name_alt or title_alt == org_name_alt:
#match found with substituting some stuff
matchFound = item
break
#if a match was not found, we accept the closest match from TMDB year = item.get('year')
if not matchFound and len(data.get("results")) > 0 and not len(data.get("results")) > 5: matchFound = None
matchFound = item = data.get("results")[0] # find year match
if year is not None:
for entry in data["results"]:
if year in entry.get("first_air_date", ""):
matchFound = entry
break
elif year in entry.get("release_date", ""):
matchFound = entry
break
# find exact match based on title, if we haven't found a year match
if matchFound is None:
self.logMsg('No themoviedb match found using year %s' % year, 1)
replacements = (
' ',
'-',
'&',
',',
':',
';'
)
for entry in data["results"]:
name = entry.get("name", entry.get("title", ""))
original_name = entry.get("original_name", "")
title_alt = title.lower()
name_alt = name.lower()
org_name_alt = original_name.lower()
for replaceString in replacements:
title_alt = title_alt.replace(replaceString, '')
name_alt = name_alt.replace(replaceString, '')
org_name_alt = org_name_alt.replace(replaceString, '')
if name == title or original_name == title:
# match found for exact title name
matchFound = entry
break
elif (name.split(" (")[0] == title or title_alt == name_alt
or title_alt == org_name_alt):
# match found with substituting some stuff
matchFound = entry
break
if matchFound: # if a match was not found, we accept the closest match from TMDB
coverUrl = matchFound.get("poster_path","") if matchFound is None and len(data.get("results")) > 0:
fanartUrl = matchFound.get("backdrop_path","") self.logMsg('Using very first match from themoviedb', 1)
id = str(matchFound.get("id","")) matchFound = entry = data.get("results")[0]
media_type = type
if media_type == "multi" and matchFound.get("media_type"):
media_type = matchFound.get("media_type","")
name = item.get("name")
if not name: name = item.get("title")
#lookup external tmdb_id and perform artwork lookup on fanart.tv
if id:
languages = [KODILANGUAGE,"en"]
for language in languages:
if media_type == "movie":
url = 'http://api.themoviedb.org/3/movie/%s?api_key=%s&language=%s&append_to_response=videos' %(id,tmdb_apiKey,language)
elif media_type == "tv":
url = 'http://api.themoviedb.org/3/tv/%s?api_key=%s&append_to_response=external_ids,videos&language=%s' %(id,tmdb_apiKey,language)
response = requests.get(url)
data = json.loads(response.content.decode('utf-8','replace'))
if data:
if not media_id and data.get("imdb_id"):
media_id = str(data.get("imdb_id"))
if not media_id and data.get("external_ids"):
media_id = str(data["external_ids"].get("tvdb_id"))
#lookup artwork on fanart.tv if matchFound is None:
if media_id and media_type: self.logMsg('Still no themoviedb match for type: %s, title: %s, '
#gets fanart.tv images for given id 'year: %s' % (media_type, title, year), 1)
api_key = "639191cb0774661597f28a47e7e2bad5" self.logMsg('themoviedb answer was %s' % data['results'], 1)
return
if type == "movie": self.logMsg('Found themoviedb match for %s: %s'
url = 'http://webservice.fanart.tv/v3/movies/%s?api_key=%s' %(media_id,api_key) % (item.get('title'), matchFound), 1)
else:
url = 'http://webservice.fanart.tv/v3/tv/%s?api_key=%s' %(media_id,api_key)
try:
response = requests.get(url, timeout=15)
except Exception as e:
return allartworks
if response and response.content and response.status_code == 200:
data = json.loads(response.content.decode('utf-8','replace'))
else:
#not found
return allartworks
if data:
#we need to use a little mapping between fanart.tv arttypes and kodi artttypes
fanartTVTypes = [ ("logo","Logo"),("musiclogo","clearlogo"),("disc","Disc"),("clearart","Art"),("banner","Banner"),("clearlogo","Logo"),("background","fanart"),("showbackground","fanart"),("characterart","characterart")]
if type != "artist": fanartTVTypes.append( ("thumb","Thumb") )
if type == "artist": fanartTVTypes.append( ("thumb","folder") )
prefixes = ["",type,"hd","hd"+type]
for fanarttype in fanartTVTypes:
for prefix in prefixes:
fanarttvimage = prefix+fanarttype[0]
if data.has_key(fanarttvimage):
for item in data[fanarttvimage]:
if item.get("lang","") == KODILANGUAGE:
#select image in preferred language
if xbmcvfs.exists(item.get("url")):
allartworks[fanarttype[1]] = item.get("url")
break
if not allartworks.get(fanarttype[1]) or (not "http:" in allartworks.get(fanarttype[1])):
#just grab the first english one as fallback
for item in data[fanarttvimage]:
if item.get("lang","") == "en" or not item.get("lang"):
if xbmcvfs.exists(item.get("url")):
allartworks[fanarttype[1]] = item.get("url")
break
#grab extrafanarts in list
maxfanarts = 10
if "background" in fanarttvimage:
fanartcount = 0
for item in data[fanarttvimage]:
if fanartcount < maxfanarts:
if xbmcvfs.exists(item.get("url")):
allartworks['Backdrop'].append(item.get("url"))
fanartcount += 1
#save extrafanarts as string
return allartworks tmdbId = str(entry.get("id", ""))
if tmdbId == '':
self.logMsg('No themoviedb ID found, aborting', -1)
return
except Exception as e: if media_type == "multi" and entry.get("media_type"):
#no artwork media_type = entry.get("media_type")
self.logMsg('No extra artwork found') name = entry.get("name", entry.get("title"))
# lookup external tmdbId and perform artwork lookup on fanart.tv
parameters = {
'api_key': apiKey
}
mediaId = None
for language in [KODILANGUAGE, "en"]:
parameters['language'] = language
if media_type == "movie":
url = 'http://api.themoviedb.org/3/movie/%s' % tmdbId
parameters['append_to_response'] = 'videos'
elif media_type == "tv":
url = 'http://api.themoviedb.org/3/tv/%s' % tmdbId
parameters['append_to_response'] = 'external_ids,videos'
data = downloadutils.DownloadUtils().downloadUrl(
url,
authenticate=False,
parameters=parameters,
timeout=7)
try:
data.get('test')
except:
self.logMsg('Could not download %s with parameters %s'
% (url, parameters), -1)
continue
if data.get("imdb_id") is not None:
mediaId = str(data.get("imdb_id"))
break
if data.get("external_ids") is not None:
mediaId = str(data["external_ids"].get("tvdb_id"))
break
return mediaId
def getFanartTVArt(self, mediaId, allartworks):
"""
perform artwork lookup on fanart.tv
mediaId: IMDB id for movies, tvdb id for TV shows
"""
item = self.item.attrib
KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1)
api_key = "639191cb0774661597f28a47e7e2bad5"
typus = item.get('type')
if typus == 'show':
typus = 'tv'
if typus == "movie":
url = 'http://webservice.fanart.tv/v3/movies/%s?api_key=%s' \
% (mediaId, api_key)
elif typus == 'tv':
url = 'http://webservice.fanart.tv/v3/tv/%s?api_key=%s' \
% (mediaId, api_key)
else:
# Not supported artwork
return allartworks return allartworks
data = downloadutils.DownloadUtils().downloadUrl(
url,
authenticate=False,
timeout=15)
try:
data.get('test')
except:
self.logMsg('Could not download data from FanartTV', -1)
return allartworks
# we need to use a little mapping between fanart.tv arttypes and kodi
# artttypes
fanartTVTypes = [
("logo", "Logo"),
("musiclogo", "clearlogo"),
("disc", "Disc"),
("clearart", "Art"),
("banner", "Banner"),
("clearlogo", "Logo"),
("background", "fanart"),
("showbackground", "fanart"),
("characterart", "characterart")
]
if typus == "artist":
fanartTVTypes.append(("thumb", "folder"))
else:
fanartTVTypes.append(("thumb", "Thumb"))
prefixes = (
"hd" + typus,
"hd",
typus,
"",
)
for fanarttype in fanartTVTypes:
# Skip the ones we already have
if allartworks.get(fanarttype[1]):
continue
for prefix in prefixes:
fanarttvimage = prefix + fanarttype[0]
if fanarttvimage not in data:
continue
# select image in preferred language
for entry in data[fanarttvimage]:
if entry.get("lang") == KODILANGUAGE:
allartworks[fanarttype[1]] = entry.get("url")
break
# just grab the first english OR undefinded one as fallback
if allartworks.get(fanarttype[1]) is None:
for entry in data[fanarttvimage]:
if entry.get("lang") in ("en", "00"):
allartworks[fanarttype[1]] = entry.get("url")
break
# grab extrafanarts in list
maxfanarts = 10
fanartcount = 0
for prefix in prefixes:
fanarttvimage = prefix + 'background'
if fanarttvimage not in data:
continue
for entry in data[fanarttvimage]:
if fanartcount < maxfanarts:
if xbmcvfs.exists(entry.get("url")):
allartworks['Backdrop'].append(entry.get("url"))
fanartcount += 1
return allartworks
def shouldStream(self): def shouldStream(self):
""" """
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 (True if self.item[0].attrib.get('optimizedForStreaming') == '1' return self.item[0].attrib.get('optimizedForStreaming') == '1'
else False)
def getTranscodeVideoPath(self, action, quality={}): def getTranscodeVideoPath(self, action, quality={}):
""" """

View file

@ -157,7 +157,7 @@ class DownloadUtils():
def downloadUrl(self, url, action_type="GET", postBody=None, def downloadUrl(self, url, action_type="GET", postBody=None,
parameters=None, authenticate=True, headerOptions=None, parameters=None, authenticate=True, headerOptions=None,
verifySSL=True): verifySSL=True, timeout=None):
""" """
Override SSL check with verifySSL=False Override SSL check with verifySSL=False
@ -203,6 +203,8 @@ class DownloadUtils():
kwargs['data'] = postBody kwargs['data'] = postBody
if parameters is not None: if parameters is not None:
kwargs['params'] = parameters kwargs['params'] = parameters
if timeout is not None:
kwargs['timeout'] = timeout
# ACTUAL DOWNLOAD HAPPENING HERE # ACTUAL DOWNLOAD HAPPENING HERE
try: try:

View file

@ -284,6 +284,12 @@ class InitialSetup():
else: else:
utils.advancedSettingsXML() utils.advancedSettingsXML()
# Download additional art from FanArtTV
if dialog.yesno(heading=self.addonName,
line1=string(39016)):
self.logMsg("User opted to use FanArtTV", 1)
utils.settings('FanartTV', value="true")
if goToSettings is False: if goToSettings is False:
# Open Settings page now? You will need to restart! # Open Settings page now? You will need to restart!
goToSettings = dialog.yesno(heading=self.addonName, goToSettings = dialog.yesno(heading=self.addonName,

View file

@ -928,15 +928,3 @@ def deleteNodes():
xbmcvfs.delete(("%s%s" % (path, file.decode('utf-8'))).encode('utf-8')) xbmcvfs.delete(("%s%s" % (path, file.decode('utf-8'))).encode('utf-8'))
except: except:
logMsg("PLEX", "Failed to file: %s" % file.decode('utf-8')) logMsg("PLEX", "Failed to file: %s" % file.decode('utf-8'))
def try_encode(text, encoding="utf-8"):
try:
return text.encode(encoding,"ignore")
except:
return text
def try_decode(text, encoding="utf-8"):
try:
return text.decode(encoding,"ignore")
except:
return text

View file

@ -56,9 +56,9 @@
<setting id="fullSyncInterval" type="number" label="39053" default="60" option="int" /> <setting id="fullSyncInterval" type="number" label="39053" default="60" option="int" />
<setting type="lsep" label="30538" /><!-- Complete Re-Sync necessary --> <setting type="lsep" label="30538" /><!-- Complete Re-Sync necessary -->
<setting id="FanartTV" label="30539" type="bool" default="false" /><!-- Download additional art from FanArtTV -->
<setting id="enableMusic" type="bool" label="30509" default="true" /> <setting id="enableMusic" type="bool" label="30509" default="true" />
<setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="true"/> <!-- Playback mode --> <setting id="useDirectPaths" type="enum" label="30511" values="Addon(Default)|Native(Direct paths)" default="0" visible="true"/> <!-- Playback mode -->
<setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library --> <setting id="streamMusic" type="bool" label="30510" default="false" visible="false" subsetting="true"/> <!-- Direct stream Music library -->
<setting type="lsep" label="30523" visible="false"/> <!-- Music metadata options --> <setting type="lsep" label="30523" visible="false"/> <!-- Music metadata options -->