Artwork overhaul part 2
This commit is contained in:
parent
b4716ba511
commit
8272a67b5f
5 changed files with 196 additions and 325 deletions
|
@ -54,19 +54,6 @@ LOG = getLogger("PLEX." + __name__)
|
|||
REGEX_IMDB = re_compile(r'''/(tt\d+)''')
|
||||
REGEX_TVDB = re_compile(r'''thetvdb:\/\/(.+?)\?''')
|
||||
|
||||
# we need to use a little mapping between fanart.tv arttypes and kodi
|
||||
# artttypes
|
||||
FANART_TV_TYPES = [
|
||||
("logo", "Logo"),
|
||||
("musiclogo", "clearlogo"),
|
||||
("disc", "Disc"),
|
||||
("clearart", "Art"),
|
||||
("banner", "Banner"),
|
||||
("clearlogo", "Logo"),
|
||||
("background", "fanart"),
|
||||
("showbackground", "fanart"),
|
||||
("characterart", "characterart")
|
||||
]
|
||||
###############################################################################
|
||||
|
||||
|
||||
|
@ -755,10 +742,9 @@ class API(object):
|
|||
'banner'
|
||||
'clearart'
|
||||
'clearlogo'
|
||||
'landscape'
|
||||
'icon'
|
||||
'fanart'
|
||||
}
|
||||
'landscape' and 'icon' might be implemented later
|
||||
"""
|
||||
if kodi_id:
|
||||
# in Kodi database, potentially with additional e.g. clearart
|
||||
|
@ -783,7 +769,7 @@ class API(object):
|
|||
if art:
|
||||
artworks['tvshow.poster'] = art
|
||||
# Get parent item artwork if the main item is missing artwork
|
||||
if 'fanart1' not in artworks:
|
||||
if 'fanart' not in artworks:
|
||||
art = self._one_artwork('parentArt')
|
||||
if art:
|
||||
artworks['fanart1'] = art
|
||||
|
@ -791,39 +777,31 @@ class API(object):
|
|||
art = self._one_artwork('parentThumb')
|
||||
if art:
|
||||
artworks['poster'] = art
|
||||
LOG.debug('artworks: %s', artworks)
|
||||
return artworks
|
||||
|
||||
def fanart_artwork(self, allartworks):
|
||||
def fanart_artwork(self, artworks):
|
||||
"""
|
||||
Downloads additional fanart from third party sources (well, link to
|
||||
fanart only).
|
||||
|
||||
allartworks = {
|
||||
'Primary': "",
|
||||
'Art': "",
|
||||
'Banner': "",
|
||||
'Logo': "",
|
||||
'Thumb': "",
|
||||
'Disc': "",
|
||||
'Backdrop': []
|
||||
}
|
||||
"""
|
||||
external_id = self.retrieve_external_item_id()
|
||||
if external_id is not None:
|
||||
allartworks = self.lookup_fanart_tv(external_id, allartworks)
|
||||
return allartworks
|
||||
artworks = self.lookup_fanart_tv(external_id[0], artworks)
|
||||
LOG.debug('fanart artworks: %s', artworks)
|
||||
return artworks
|
||||
|
||||
def retrieve_external_item_id(self, collection=False):
|
||||
"""
|
||||
Returns the item's IMDB id for movies or tvdb id for TV shows
|
||||
Returns the set
|
||||
media_id [unicode]: the item's IMDB id for movies or tvdb id for
|
||||
TV shows
|
||||
poster [unicode]: path to the item's poster artwork
|
||||
background [unicode]: path to the item's background artwork
|
||||
|
||||
If not found in item's Plex metadata, check themovidedb.org
|
||||
The last two might be None if not found. Generally None is returned
|
||||
if unsuccessful.
|
||||
|
||||
collection=True will try to return the three-tuple:
|
||||
collection ID, poster-path, background-path
|
||||
|
||||
None is returned if unsuccessful
|
||||
If not found in item's Plex metadata, check themovidedb.org.
|
||||
"""
|
||||
item = self.item.attrib
|
||||
media_type = item.get('type')
|
||||
|
@ -836,7 +814,7 @@ class API(object):
|
|||
elif media_type == v.PLEX_TYPE_SHOW:
|
||||
media_id = self.provider('tvdb')
|
||||
if media_id is not None:
|
||||
return media_id
|
||||
return media_id, None, None
|
||||
LOG.info('Plex did not provide ID for IMDB or TVDB. Start '
|
||||
'lookup process')
|
||||
else:
|
||||
|
@ -935,9 +913,8 @@ class API(object):
|
|||
media_type = entry.get("media_type")
|
||||
name = entry.get("name", entry.get("title"))
|
||||
# lookup external tmdb_id and perform artwork lookup on fanart.tv
|
||||
parameters = {
|
||||
'api_key': api_key
|
||||
}
|
||||
parameters = {'api_key': api_key}
|
||||
media_id, poster, background = None, None, None
|
||||
for language in [v.KODILANGUAGE, "en"]:
|
||||
parameters['language'] = language
|
||||
if media_type == "movie":
|
||||
|
@ -977,7 +954,7 @@ class API(object):
|
|||
try:
|
||||
data.get('poster_path')
|
||||
except AttributeError:
|
||||
LOG.info('Could not find TheMovieDB poster paths for %s in'
|
||||
LOG.info('Could not find TheMovieDB poster paths for %s in '
|
||||
'the language %s', title, language)
|
||||
continue
|
||||
else:
|
||||
|
@ -985,23 +962,21 @@ class API(object):
|
|||
data.get('poster_path'))
|
||||
background = ('https://image.tmdb.org/t/p/original%s' %
|
||||
data.get('backdrop_path'))
|
||||
media_id = media_id, poster, background
|
||||
break
|
||||
return media_id
|
||||
return media_id, poster, background
|
||||
|
||||
def lookup_fanart_tv(self, media_id, allartworks, set_info=False):
|
||||
def lookup_fanart_tv(self, media_id, artworks, set_info=False):
|
||||
"""
|
||||
perform artwork lookup on fanart.tv
|
||||
|
||||
media_id: IMDB id for movies, tvdb id for TV shows
|
||||
"""
|
||||
item = self.item.attrib
|
||||
api_key = settings('FanArtTVAPIKey')
|
||||
typus = item.get('type')
|
||||
if typus == 'show':
|
||||
typus = self.plex_type()
|
||||
if typus == v.PLEX_TYPE_SHOW:
|
||||
typus = 'tv'
|
||||
|
||||
if typus == "movie":
|
||||
if typus == v.PLEX_TYPE_MOVIE:
|
||||
url = 'http://webservice.fanart.tv/v3/movies/%s?api_key=%s' \
|
||||
% (media_id, api_key)
|
||||
elif typus == 'tv':
|
||||
|
@ -1009,24 +984,20 @@ class API(object):
|
|||
% (media_id, api_key)
|
||||
else:
|
||||
# Not supported artwork
|
||||
return allartworks
|
||||
data = DU().downloadUrl(url,
|
||||
authenticate=False,
|
||||
timeout=15)
|
||||
return artworks
|
||||
data = DU().downloadUrl(url, authenticate=False, timeout=15)
|
||||
try:
|
||||
data.get('test')
|
||||
except AttributeError:
|
||||
LOG.error('Could not download data from FanartTV')
|
||||
return allartworks
|
||||
return artworks
|
||||
|
||||
fanart_tv_types = list(FANART_TV_TYPES)
|
||||
fanart_tv_types = list(v.FANART_TV_TO_KODI_TYPE)
|
||||
|
||||
if typus == "artist":
|
||||
if typus == v.PLEX_TYPE_ARTIST:
|
||||
fanart_tv_types.append(("thumb", "folder"))
|
||||
else:
|
||||
fanart_tv_types.append(("thumb", "Thumb"))
|
||||
if set_info:
|
||||
fanart_tv_types.append(("poster", "Primary"))
|
||||
fanart_tv_types.append(("thumb", "thumb"))
|
||||
|
||||
prefixes = (
|
||||
"hd" + typus,
|
||||
|
@ -1034,32 +1005,31 @@ class API(object):
|
|||
typus,
|
||||
"",
|
||||
)
|
||||
for fanarttype in fanart_tv_types:
|
||||
for fanart_tv_type, kodi_type in fanart_tv_types:
|
||||
# Skip the ones we already have
|
||||
if allartworks.get(fanarttype[1]):
|
||||
if kodi_type in artworks:
|
||||
continue
|
||||
for prefix in prefixes:
|
||||
fanarttvimage = prefix + fanarttype[0]
|
||||
fanarttvimage = prefix + fanart_tv_type
|
||||
if fanarttvimage not in data:
|
||||
continue
|
||||
# select image in preferred language
|
||||
for entry in data[fanarttvimage]:
|
||||
if entry.get("lang") == v.KODILANGUAGE:
|
||||
allartworks[fanarttype[1]] = \
|
||||
artworks[kodi_type] = \
|
||||
entry.get("url", "").replace(' ', '%20')
|
||||
break
|
||||
# just grab the first english OR undefinded one as fallback
|
||||
# (so we're actually grabbing the more popular one)
|
||||
if not allartworks.get(fanarttype[1]):
|
||||
if kodi_type not in artworks:
|
||||
for entry in data[fanarttvimage]:
|
||||
if entry.get("lang") in ("en", "00"):
|
||||
allartworks[fanarttype[1]] = \
|
||||
artworks[kodi_type] = \
|
||||
entry.get("url", "").replace(' ', '%20')
|
||||
break
|
||||
|
||||
# grab extrafanarts in list
|
||||
maxfanarts = 10
|
||||
fanartcount = 0
|
||||
fanartcount = 1 if 'fanart' in artworks else ''
|
||||
for prefix in prefixes:
|
||||
fanarttvimage = prefix + 'background'
|
||||
if fanarttvimage not in data:
|
||||
|
@ -1067,60 +1037,38 @@ class API(object):
|
|||
for entry in data[fanarttvimage]:
|
||||
if entry.get("url") is None:
|
||||
continue
|
||||
if fanartcount > maxfanarts:
|
||||
artworks['fanart%s' % fanartcount] = \
|
||||
entry['url'].replace(' ', '%20')
|
||||
try:
|
||||
fanartcount += 1
|
||||
except TypeError:
|
||||
fanartcount = 1
|
||||
if fanartcount >= v.MAX_BACKGROUND_COUNT:
|
||||
break
|
||||
allartworks['Backdrop'].append(
|
||||
entry['url'].replace(' ', '%20'))
|
||||
fanartcount += 1
|
||||
return allartworks
|
||||
return artworks
|
||||
|
||||
def set_artwork(self):
|
||||
"""
|
||||
Gets the URLs to the Plex artwork, or empty string if not found.
|
||||
parentInfo=True will check for parent's artwork if None is found
|
||||
|
||||
Only call on movies
|
||||
|
||||
Output:
|
||||
{
|
||||
'Primary'
|
||||
'Art'
|
||||
'Banner'
|
||||
'Logo'
|
||||
'Thumb'
|
||||
'Disc'
|
||||
'Backdrop' : LIST with the first entry xml key "art"
|
||||
}
|
||||
"""
|
||||
allartworks = {
|
||||
'Primary': "",
|
||||
'Art': "",
|
||||
'Banner': "",
|
||||
'Logo': "",
|
||||
'Thumb': "",
|
||||
'Disc': "",
|
||||
'Backdrop': []
|
||||
}
|
||||
|
||||
artworks = {}
|
||||
# Plex does not get much artwork - go ahead and get the rest from
|
||||
# fanart tv only for movie or tv show
|
||||
external_id = self.retrieve_external_item_id(collection=True)
|
||||
if external_id is not None:
|
||||
try:
|
||||
external_id, poster, background = external_id
|
||||
except TypeError:
|
||||
poster, background = None, None
|
||||
external_id, poster, background = external_id
|
||||
if poster is not None:
|
||||
allartworks['Primary'] = poster
|
||||
artworks['poster'] = poster
|
||||
if background is not None:
|
||||
allartworks['Backdrop'].append(background)
|
||||
allartworks = self.lookup_fanart_tv(external_id,
|
||||
allartworks,
|
||||
set_info=True)
|
||||
artworks['fanart'] = background
|
||||
artworks = self.lookup_fanart_tv(external_id,
|
||||
artworks,
|
||||
set_info=True)
|
||||
else:
|
||||
LOG.info('Did not find a set/collection ID on TheMovieDB using %s.'
|
||||
' Artwork will be missing.', self.titles()[0])
|
||||
return allartworks
|
||||
return artworks
|
||||
|
||||
def should_stream(self):
|
||||
"""
|
||||
|
|
|
@ -21,9 +21,8 @@ LOG = getLogger("PLEX." + __name__)
|
|||
|
||||
# Disable annoying requests warnings
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
###############################################################################
|
||||
|
||||
ARTWORK_QUEUE = Queue()
|
||||
###############################################################################
|
||||
|
||||
|
||||
def double_urlencode(text):
|
||||
|
@ -88,26 +87,25 @@ class Image_Cache_Thread(Thread):
|
|||
# Server thinks its a DOS attack, ('error 10053')
|
||||
# Wait before trying again
|
||||
if sleeptime > 5:
|
||||
LOG.error('Repeatedly got ConnectionError for url %s'
|
||||
% double_urldecode(url))
|
||||
LOG.error('Repeatedly got ConnectionError for url %s',
|
||||
double_urldecode(url))
|
||||
break
|
||||
LOG.debug('Were trying too hard to download art, server '
|
||||
'over-loaded. Sleep %s seconds before trying '
|
||||
'again to download %s'
|
||||
% (2**sleeptime, double_urldecode(url)))
|
||||
'again to download %s',
|
||||
2**sleeptime, double_urldecode(url))
|
||||
sleep((2**sleeptime)*1000)
|
||||
sleeptime += 1
|
||||
continue
|
||||
except Exception as e:
|
||||
LOG.error('Unknown exception for url %s: %s'
|
||||
% (double_urldecode(url), e))
|
||||
LOG.error('Unknown exception for url %s: %s'.
|
||||
double_urldecode(url), e)
|
||||
import traceback
|
||||
LOG.error("Traceback:\n%s" % traceback.format_exc())
|
||||
LOG.error("Traceback:\n%s", traceback.format_exc())
|
||||
break
|
||||
# We did not even get a timeout
|
||||
break
|
||||
queue.task_done()
|
||||
LOG.debug('Cached art: %s' % double_urldecode(url))
|
||||
# Sleep for a bit to reduce CPU strain
|
||||
sleep(sleep_between)
|
||||
LOG.info("---===### Stopped Image_Cache_Thread ###===---")
|
||||
|
@ -135,7 +133,7 @@ class Artwork():
|
|||
path = try_decode(translatePath("special://thumbnails/"))
|
||||
if exists_dir(path):
|
||||
rmtree(path, ignore_errors=True)
|
||||
self.restoreCacheDirectories()
|
||||
self.restore_cache_directories()
|
||||
|
||||
# remove all existing data from texture DB
|
||||
connection = kodi_sql('texture')
|
||||
|
@ -158,167 +156,91 @@ class Artwork():
|
|||
cursor.execute(query, ('actor', ))
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
LOG.info("Image cache sync about to process %s video images" % total)
|
||||
LOG.info("Image cache sync about to process %s video images", total)
|
||||
connection.close()
|
||||
|
||||
for url in result:
|
||||
self.cacheTexture(url[0])
|
||||
self.cache_texture(url[0])
|
||||
# Cache all entries in music DB
|
||||
connection = kodi_sql('music')
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SELECT url FROM art")
|
||||
result = cursor.fetchall()
|
||||
total = len(result)
|
||||
LOG.info("Image cache sync about to process %s music images" % total)
|
||||
LOG.info("Image cache sync about to process %s music images", total)
|
||||
connection.close()
|
||||
for url in result:
|
||||
self.cacheTexture(url[0])
|
||||
self.cache_texture(url[0])
|
||||
|
||||
def cacheTexture(self, url):
|
||||
# Cache a single image url to the texture cache
|
||||
def cache_texture(self, url):
|
||||
'''
|
||||
Cache a single image url to the texture cache
|
||||
'''
|
||||
if url and self.enableTextureCache:
|
||||
self.queue.put(double_urlencode(try_encode(url)))
|
||||
|
||||
def addArtwork(self, artwork, kodiId, mediaType, cursor):
|
||||
# Kodi conversion table
|
||||
kodiart = {
|
||||
'Primary': ["thumb", "poster"],
|
||||
'Banner': "banner",
|
||||
'Logo': "clearlogo",
|
||||
'Art': "clearart",
|
||||
'Thumb': "landscape",
|
||||
'Disc': "discart",
|
||||
'Backdrop': "fanart",
|
||||
'BoxRear': "poster"
|
||||
}
|
||||
def modify_artwork(self, artworks, kodi_id, kodi_type, cursor):
|
||||
"""
|
||||
Pass in an artworks dict (see PlexAPI) to set an items artwork.
|
||||
"""
|
||||
for kodi_art, url in artworks.iteritems():
|
||||
self.modify_art(url, kodi_id, kodi_type, kodi_art, cursor)
|
||||
|
||||
# Artwork is a dictionary
|
||||
for art in artwork:
|
||||
if art == "Backdrop":
|
||||
# Backdrop entry is a list
|
||||
# Process extra fanart for artwork downloader (fanart, fanart1,
|
||||
# fanart2...)
|
||||
backdrops = artwork[art]
|
||||
backdropsNumber = len(backdrops)
|
||||
|
||||
query = ' '.join((
|
||||
"SELECT url",
|
||||
"FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type LIKE ?"
|
||||
))
|
||||
cursor.execute(query, (kodiId, mediaType, "fanart%",))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
if len(rows) > backdropsNumber:
|
||||
# More backdrops in database. Delete extra fanart.
|
||||
query = ' '.join((
|
||||
"DELETE FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type LIKE ?"
|
||||
))
|
||||
cursor.execute(query, (kodiId, mediaType, "fanart_",))
|
||||
|
||||
# Process backdrops and extra fanart
|
||||
index = ""
|
||||
for backdrop in backdrops:
|
||||
self.addOrUpdateArt(
|
||||
imageUrl=backdrop,
|
||||
kodiId=kodiId,
|
||||
mediaType=mediaType,
|
||||
imageType="%s%s" % ("fanart", index),
|
||||
cursor=cursor)
|
||||
|
||||
if backdropsNumber > 1:
|
||||
try: # Will only fail on the first try, str to int.
|
||||
index += 1
|
||||
except TypeError:
|
||||
index = 1
|
||||
|
||||
elif art == "Primary":
|
||||
# Primary art is processed as thumb and poster for Kodi.
|
||||
for artType in kodiart[art]:
|
||||
self.addOrUpdateArt(
|
||||
imageUrl=artwork[art],
|
||||
kodiId=kodiId,
|
||||
mediaType=mediaType,
|
||||
imageType=artType,
|
||||
cursor=cursor)
|
||||
|
||||
elif kodiart.get(art):
|
||||
# Process the rest artwork type that Kodi can use
|
||||
self.addOrUpdateArt(
|
||||
imageUrl=artwork[art],
|
||||
kodiId=kodiId,
|
||||
mediaType=mediaType,
|
||||
imageType=kodiart[art],
|
||||
cursor=cursor)
|
||||
|
||||
def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor):
|
||||
if not imageUrl:
|
||||
# Possible that the imageurl is an empty string
|
||||
return
|
||||
|
||||
query = ' '.join((
|
||||
"SELECT url",
|
||||
"FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type = ?"
|
||||
))
|
||||
cursor.execute(query, (kodiId, mediaType, imageType,))
|
||||
def modify_art(self, url, kodi_id, kodi_type, kodi_art, cursor):
|
||||
"""
|
||||
Adds or modifies the artwork of kind kodi_art (e.g. 'poster') in the
|
||||
Kodi art table for item kodi_id/kodi_type. Will also cache everything
|
||||
except actor portraits.
|
||||
"""
|
||||
query = '''
|
||||
SELECT url FROM art
|
||||
WHERE media_id = ? AND media_type = ? AND type = ?
|
||||
LIMIT 1
|
||||
'''
|
||||
cursor.execute(query, (kodi_id, kodi_type, kodi_art,))
|
||||
try:
|
||||
# Update the artwork
|
||||
url = cursor.fetchone()[0]
|
||||
old_url = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
# Add the artwork
|
||||
LOG.debug("Adding Art Link for kodiId: %s (%s)"
|
||||
% (kodiId, imageUrl))
|
||||
query = (
|
||||
'''
|
||||
LOG.debug('Adding Art Link for %s kodi_id %s, kodi_type %s: %s',
|
||||
kodi_art, kodi_id, kodi_type, url)
|
||||
query = '''
|
||||
INSERT INTO art(media_id, media_type, type, url)
|
||||
VALUES (?, ?, ?, ?)
|
||||
'''
|
||||
)
|
||||
cursor.execute(query, (kodiId, mediaType, imageType, imageUrl))
|
||||
'''
|
||||
cursor.execute(query, (kodi_id, kodi_type, kodi_art, url))
|
||||
else:
|
||||
if url == imageUrl:
|
||||
if url == old_url:
|
||||
# Only cache artwork if it changed
|
||||
return
|
||||
# Only for the main backdrop, poster
|
||||
if (window('plex_initialScan') != "true" and
|
||||
imageType in ("fanart", "poster")):
|
||||
# Delete current entry before updating with the new one
|
||||
self.deleteCachedArtwork(url)
|
||||
LOG.debug("Updating Art url for %s kodiId %s %s -> (%s)"
|
||||
% (imageType, kodiId, url, imageUrl))
|
||||
query = ' '.join((
|
||||
"UPDATE art",
|
||||
"SET url = ?",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type = ?"
|
||||
))
|
||||
cursor.execute(query, (imageUrl, kodiId, mediaType, imageType))
|
||||
|
||||
self.delete_cached_artwork(old_url)
|
||||
LOG.debug("Updating Art url for %s kodi_id %s, kodi_type %s to %s",
|
||||
kodi_art, kodi_id, kodi_type, url)
|
||||
query = '''
|
||||
UPDATE art SET url = ?
|
||||
WHERE media_id = ? AND media_type = ? AND type = ?
|
||||
'''
|
||||
cursor.execute(query, (url, kodi_id, kodi_type, kodi_art))
|
||||
# Cache fanart and poster in Kodi texture cache
|
||||
if mediaType != 'actor':
|
||||
self.cacheTexture(imageUrl)
|
||||
if kodi_type != 'actor':
|
||||
self.cache_texture(url)
|
||||
|
||||
def deleteArtwork(self, kodiId, mediaType, cursor):
|
||||
def delete_artwork(self, kodiId, mediaType, cursor):
|
||||
query = 'SELECT url FROM art WHERE media_id = ? AND media_type = ?'
|
||||
cursor.execute(query, (kodiId, mediaType,))
|
||||
for row in cursor.fetchall():
|
||||
self.deleteCachedArtwork(row[0])
|
||||
self.delete_cached_artwork(row[0])
|
||||
|
||||
def deleteCachedArtwork(self, url):
|
||||
# Only necessary to remove and apply a new backdrop or poster
|
||||
@staticmethod
|
||||
def delete_cached_artwork(url):
|
||||
"""
|
||||
Deleted the cached artwork with path url (if it exists)
|
||||
"""
|
||||
connection = kodi_sql('texture')
|
||||
cursor = connection.cursor()
|
||||
try:
|
||||
cursor.execute("SELECT cachedurl FROM texture WHERE url = ?",
|
||||
cursor.execute("SELECT cachedurl FROM texture WHERE url=? LIMIT 1",
|
||||
(url,))
|
||||
cachedurl = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
|
@ -327,7 +249,7 @@ class Artwork():
|
|||
else:
|
||||
# Delete thumbnail as well as the entry
|
||||
path = translatePath("special://thumbnails/%s" % cachedurl)
|
||||
LOG.debug("Deleting cached thumbnail: %s" % path)
|
||||
LOG.debug("Deleting cached thumbnail: %s", path)
|
||||
if exists(path):
|
||||
rmtree(try_decode(path), ignore_errors=True)
|
||||
cursor.execute("DELETE FROM texture WHERE url = ?", (url,))
|
||||
|
@ -336,8 +258,11 @@ class Artwork():
|
|||
connection.close()
|
||||
|
||||
@staticmethod
|
||||
def restoreCacheDirectories():
|
||||
def restore_cache_directories():
|
||||
LOG.info("Restoring cache directories...")
|
||||
paths = ("","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","Video","plex")
|
||||
for p in paths:
|
||||
makedirs(try_decode(translatePath("special://thumbnails/%s" % p)))
|
||||
paths = ("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"a", "b", "c", "d", "e", "f",
|
||||
"Video", "plex")
|
||||
for path in paths:
|
||||
makedirs(try_decode(translatePath("special://thumbnails/%s"
|
||||
% path)))
|
||||
|
|
|
@ -82,11 +82,11 @@ class Items(object):
|
|||
allartworks = None
|
||||
else:
|
||||
with kodidb.GetKodiDB('video') as kodi_db:
|
||||
allartworks = kodi_db.existingArt(kodi_id, kodi_type)
|
||||
allartworks = kodi_db.get_art(kodi_id, kodi_type)
|
||||
# Check if we even need to get additional art
|
||||
needsupdate = False
|
||||
for key, value in allartworks.iteritems():
|
||||
if not value and not key == 'BoxRear':
|
||||
for key in v.ALL_KODI_ARTWORK:
|
||||
if key not in allartworks:
|
||||
needsupdate = True
|
||||
break
|
||||
if needsupdate is False:
|
||||
|
@ -107,19 +107,19 @@ class Items(object):
|
|||
api = API(xml[0])
|
||||
if allartworks is None:
|
||||
allartworks = api.artwork()
|
||||
self.artwork.addArtwork(api.fanart_artwork(allartworks),
|
||||
kodi_id,
|
||||
kodi_type,
|
||||
self.kodicursor)
|
||||
self.artwork.modify_artwork(api.fanart_artwork(allartworks),
|
||||
kodi_id,
|
||||
kodi_type,
|
||||
self.kodicursor)
|
||||
# Also get artwork for collections/movie sets
|
||||
if kodi_type == v.KODI_TYPE_MOVIE:
|
||||
for setname in api.collection_list():
|
||||
LOG.debug('Getting artwork for movie set %s', setname)
|
||||
setid = self.kodi_db.createBoxset(setname)
|
||||
self.artwork.addArtwork(api.set_artwork(),
|
||||
setid,
|
||||
v.KODI_TYPE_SET,
|
||||
self.kodicursor)
|
||||
self.artwork.modify_artwork(api.set_artwork(),
|
||||
setid,
|
||||
v.KODI_TYPE_SET,
|
||||
self.kodicursor)
|
||||
self.kodi_db.assignBoxset(setid, kodi_id)
|
||||
return True
|
||||
|
||||
|
@ -445,7 +445,10 @@ class Movies(Items):
|
|||
# Process genres
|
||||
self.kodi_db.modify_genres(movieid, v.KODI_TYPE_MOVIE, genres)
|
||||
# Process artwork
|
||||
artwork.addArtwork(api.artwork(), movieid, "movie", kodicursor)
|
||||
artwork.modify_artwork(api.artwork(),
|
||||
movieid,
|
||||
v.KODI_TYPE_MOVIE,
|
||||
kodicursor)
|
||||
# Process stream details
|
||||
self.kodi_db.modify_streams(fileid, api.mediastreams(), runtime)
|
||||
# Process studios
|
||||
|
@ -482,7 +485,7 @@ class Movies(Items):
|
|||
# Remove the plex reference
|
||||
plex_db.removeItem(itemid)
|
||||
# Remove artwork
|
||||
artwork.deleteArtwork(kodi_id, kodi_type, kodicursor)
|
||||
artwork.delete_artwork(kodi_id, kodi_type, kodicursor)
|
||||
|
||||
if kodi_type == v.KODI_TYPE_MOVIE:
|
||||
set_id = self.kodi_db.get_set_id(kodi_id)
|
||||
|
@ -743,7 +746,10 @@ class TVShows(Items):
|
|||
|
||||
self.kodi_db.modify_people(showid, v.KODI_TYPE_SHOW, api.people_list())
|
||||
self.kodi_db.modify_genres(showid, v.KODI_TYPE_SHOW, genres)
|
||||
artwork.addArtwork(api.artwork(), showid, v.KODI_TYPE_SHOW, kodicursor)
|
||||
artwork.modify_artwork(api.artwork(),
|
||||
showid,
|
||||
v.KODI_TYPE_SHOW,
|
||||
kodicursor)
|
||||
# Process studios
|
||||
self.kodi_db.modify_studios(showid, v.KODI_TYPE_SHOW, studios)
|
||||
# Process tags: view, PMS collection tags
|
||||
|
@ -784,7 +790,10 @@ class TVShows(Items):
|
|||
|
||||
# Process artwork
|
||||
allartworks = api.artwork()
|
||||
artwork.addArtwork(allartworks, seasonid, "season", kodicursor)
|
||||
artwork.modify_artwork(allartworks,
|
||||
seasonid,
|
||||
v.KODI_TYPE_SEASON,
|
||||
kodicursor)
|
||||
|
||||
if update_item:
|
||||
# Update a reference: checksum in plex table
|
||||
|
@ -1095,8 +1104,8 @@ class TVShows(Items):
|
|||
if poster:
|
||||
poster = api.attach_plex_token_to_url(
|
||||
"%s%s" % (self.server, poster))
|
||||
artwork.addOrUpdateArt(
|
||||
poster, episodeid, "episode", "thumb", kodicursor)
|
||||
artwork.modify_art(
|
||||
poster, episodeid, v.KODI_TYPE_EPISODE, "thumb", kodicursor)
|
||||
|
||||
# Process stream details
|
||||
streams = api.mediastreams()
|
||||
|
@ -1223,7 +1232,7 @@ class TVShows(Items):
|
|||
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.artwork.deleteArtwork(kodi_id, v.KODI_TYPE_SHOW, kodicursor)
|
||||
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_SHOW, kodicursor)
|
||||
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,))
|
||||
if v.KODIVERSION >= 17:
|
||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
||||
|
@ -1235,7 +1244,7 @@ class TVShows(Items):
|
|||
Remove a season, and only a season, not the show or episodes
|
||||
"""
|
||||
kodicursor = self.kodicursor
|
||||
self.artwork.deleteArtwork(kodi_id, "season", kodicursor)
|
||||
self.artwork.delete_artwork(kodi_id, "season", kodicursor)
|
||||
kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?",
|
||||
(kodi_id,))
|
||||
LOG.info("Removed season: %s.", kodi_id)
|
||||
|
@ -1248,7 +1257,7 @@ class TVShows(Items):
|
|||
self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
||||
self.kodi_db.modify_streams(file_id)
|
||||
self.kodi_db.delete_playstate(file_id)
|
||||
self.artwork.deleteArtwork(kodi_id, "episode", kodicursor)
|
||||
self.artwork.delete_artwork(kodi_id, "episode", kodicursor)
|
||||
kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?",
|
||||
(kodi_id,))
|
||||
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
|
||||
|
@ -1359,7 +1368,10 @@ class Music(Items):
|
|||
dateadded, artistid))
|
||||
|
||||
# Update artwork
|
||||
artwork.addArtwork(artworks, artistid, v.KODI_TYPE_ARTIST, kodicursor)
|
||||
artwork.modify_artwork(artworks,
|
||||
artistid,
|
||||
v.KODI_TYPE_ARTIST,
|
||||
kodicursor)
|
||||
|
||||
@catch_exceptions(warnuser=True)
|
||||
def add_updateAlbum(self, item, viewtag=None, viewid=None, children=None,
|
||||
|
@ -1553,7 +1565,7 @@ class Music(Items):
|
|||
# Add genres
|
||||
self.kodi_db.addMusicGenres(albumid, self.genres, v.KODI_TYPE_ALBUM)
|
||||
# Update artwork
|
||||
artwork.addArtwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
||||
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
||||
# Add all children - all tracks
|
||||
if scan_children:
|
||||
for child in children:
|
||||
|
@ -1847,10 +1859,11 @@ class Music(Items):
|
|||
# Add genres
|
||||
if genres:
|
||||
self.kodi_db.addMusicGenres(songid, genres, v.KODI_TYPE_SONG)
|
||||
artwork.addArtwork(api.artwork(), songid, v.KODI_TYPE_SONG, kodicursor)
|
||||
artworks = api.artwork()
|
||||
artwork.modify_artwork(artworks, songid, v.KODI_TYPE_SONG, kodicursor)
|
||||
if item.get('parentKey') is None:
|
||||
# Update album artwork
|
||||
artwork.addArtwork(allart, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
||||
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
||||
|
||||
def remove(self, itemid):
|
||||
"""
|
||||
|
@ -1937,7 +1950,7 @@ class Music(Items):
|
|||
"""
|
||||
Remove song, and only the song
|
||||
"""
|
||||
self.artwork.deleteArtwork(kodiid, v.KODI_TYPE_SONG, self.kodicursor)
|
||||
self.artwork.delete_artwork(kodiid, v.KODI_TYPE_SONG, self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM song WHERE idSong = ?",
|
||||
(kodiid,))
|
||||
|
||||
|
@ -1945,7 +1958,7 @@ class Music(Items):
|
|||
"""
|
||||
Remove an album, and only the album
|
||||
"""
|
||||
self.artwork.deleteArtwork(kodiid, v.KODI_TYPE_ALBUM, self.kodicursor)
|
||||
self.artwork.delete_artwork(kodiid, v.KODI_TYPE_ALBUM, self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM album WHERE idAlbum = ?",
|
||||
(kodiid,))
|
||||
|
||||
|
@ -1953,7 +1966,7 @@ class Music(Items):
|
|||
"""
|
||||
Remove an artist, and only the artist
|
||||
"""
|
||||
self.artwork.deleteArtwork(kodiid,
|
||||
self.artwork.delete_artwork(kodiid,
|
||||
v.KODI_TYPE_ARTIST,
|
||||
self.kodicursor)
|
||||
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?",
|
||||
|
|
|
@ -406,7 +406,7 @@ class KodiDBMethods(object):
|
|||
self.cursor.execute(query_actor_delete, (person[0],))
|
||||
if kind == 'actor':
|
||||
# Delete any associated artwork
|
||||
self.artwork.deleteArtwork(person[0], 'actor', self.cursor)
|
||||
self.artwork.delete_artwork(person[0], 'actor', self.cursor)
|
||||
# Save new people to Kodi DB by iterating over the remaining entries
|
||||
if kind == 'actor':
|
||||
query = 'INSERT INTO actor_link VALUES (?, ?, ?, ?, ?)'
|
||||
|
@ -454,11 +454,11 @@ class KodiDBMethods(object):
|
|||
'VALUES (?, ?)',
|
||||
(actor_id, name))
|
||||
if art_url:
|
||||
self.artwork.addOrUpdateArt(art_url,
|
||||
actor_id,
|
||||
'actor',
|
||||
"thumb",
|
||||
self.cursor)
|
||||
self.artwork.modify_art(art_url,
|
||||
actor_id,
|
||||
'actor',
|
||||
'thumb',
|
||||
self.cursor)
|
||||
return actor_id
|
||||
|
||||
def get_art(self, kodi_id, kodi_type):
|
||||
|
@ -468,11 +468,11 @@ class KodiDBMethods(object):
|
|||
'thumb'
|
||||
'poster'
|
||||
'banner'
|
||||
'fanart'
|
||||
'clearart'
|
||||
'clearlogo'
|
||||
'landscape'
|
||||
'icon'
|
||||
'fanart' and also potentially more fanart 'fanart1', 2, 3, ...
|
||||
}
|
||||
Missing fanart will not appear in the dict.
|
||||
"""
|
||||
|
@ -480,48 +480,6 @@ class KodiDBMethods(object):
|
|||
self.cursor.execute(query, (kodi_id, kodi_type))
|
||||
return dict(self.cursor.fetchall())
|
||||
|
||||
def existingArt(self, kodiId, mediaType, refresh=False):
|
||||
"""
|
||||
For kodiId, returns an artwork dict with already existing art from
|
||||
the Kodi db
|
||||
"""
|
||||
# Only get EITHER poster OR thumb (should have same URL)
|
||||
kodiToPKC = {
|
||||
'banner': 'Banner',
|
||||
'clearart': 'Art',
|
||||
'clearlogo': 'Logo',
|
||||
'discart': 'Disc',
|
||||
'landscape': 'Thumb',
|
||||
'thumb': 'Primary'
|
||||
}
|
||||
# BoxRear yet unused
|
||||
result = {'BoxRear': ''}
|
||||
for art in kodiToPKC:
|
||||
query = ' '.join((
|
||||
"SELECT url",
|
||||
"FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type = ?"
|
||||
))
|
||||
self.cursor.execute(query, (kodiId, mediaType, art,))
|
||||
try:
|
||||
url = self.cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
url = ""
|
||||
result[kodiToPKC[art]] = url
|
||||
# There may be several fanart URLs saved
|
||||
query = ' '.join((
|
||||
"SELECT url",
|
||||
"FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?",
|
||||
"AND type LIKE ?"
|
||||
))
|
||||
data = self.cursor.execute(query, (kodiId, mediaType, "fanart%",))
|
||||
result['Backdrop'] = [d[0] for d in data]
|
||||
return result
|
||||
|
||||
def modify_streams(self, fileid, streamdetails=None, runtime=None):
|
||||
"""
|
||||
Leave streamdetails and runtime empty to delete all stream entries for
|
||||
|
|
|
@ -321,9 +321,36 @@ PLEX_TYPE_FROM_WEBSOCKET = {
|
|||
KODI_TO_PLEX_ARTWORK = {
|
||||
'poster': 'thumb',
|
||||
'banner': 'banner',
|
||||
'fanart1': 'art'
|
||||
'fanart': 'art'
|
||||
}
|
||||
|
||||
# Might be implemented in the future: 'icon', 'landscape' (16:9)
|
||||
ALL_KODI_ARTWORK = (
|
||||
'thumb',
|
||||
'poster',
|
||||
'banner',
|
||||
'clearart',
|
||||
'clearlogo',
|
||||
'fanart',
|
||||
'discart'
|
||||
)
|
||||
|
||||
# we need to use a little mapping between fanart.tv arttypes and kodi artttypes
|
||||
FANART_TV_TO_KODI_TYPE = [
|
||||
('poster', 'poster'),
|
||||
('logo', 'clearlogo'),
|
||||
('musiclogo', 'clearlogo'),
|
||||
('disc', 'discart'),
|
||||
('clearart', 'clearart'),
|
||||
('banner', 'banner'),
|
||||
('clearlogo', 'clearlogo'),
|
||||
('background', 'fanart'),
|
||||
('showbackground', 'fanart'),
|
||||
('characterart', 'characterart')
|
||||
]
|
||||
# How many different backgrounds do we want to load from fanart.tv?
|
||||
MAX_BACKGROUND_COUNT = 10
|
||||
|
||||
|
||||
# extensions from:
|
||||
# http://kodi.wiki/view/Features_and_supported_codecs#Format_support (RAW image
|
||||
|
|
Loading…
Reference in a new issue