From 8272a67b5fb3fdc28c0989823fc9f50e4c85ef29 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 4 Mar 2018 13:39:18 +0100 Subject: [PATCH] Artwork overhaul part 2 --- resources/lib/PlexAPI.py | 158 ++++++++-------------- resources/lib/artwork.py | 211 ++++++++++-------------------- resources/lib/itemtypes.py | 67 ++++++---- resources/lib/kodidb_functions.py | 56 +------- resources/lib/variables.py | 29 +++- 5 files changed, 196 insertions(+), 325 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 1710a093..4be62b40 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -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 + + The last two might be None if not found. Generally None is returned + if unsuccessful. - If not found in item's Plex metadata, check themovidedb.org - - 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): """ diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index 8c3ed719..1c39b806 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -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))) diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index d73bd5f2..5232018b 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -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 = ?", diff --git a/resources/lib/kodidb_functions.py b/resources/lib/kodidb_functions.py index cb535e83..34fbff2a 100644 --- a/resources/lib/kodidb_functions.py +++ b/resources/lib/kodidb_functions.py @@ -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 diff --git a/resources/lib/variables.py b/resources/lib/variables.py index e1ed163e..6f440f92 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -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