Artwork overhaul part 2

This commit is contained in:
croneter 2018-03-04 13:39:18 +01:00
parent b4716ba511
commit 8272a67b5f
5 changed files with 196 additions and 325 deletions

View file

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

View file

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

View file

@ -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 = ?",

View file

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

View file

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