diff --git a/modules/builder.py b/modules/builder.py index b491fc1e..2a5f4ac9 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -1,7 +1,7 @@ import logging, os, re from datetime import datetime, timedelta from modules import anidb, anilist, imdb, letterboxd, mal, plex, radarr, sonarr, tautulli, tmdb, trakttv, tvdb, util -from modules.util import Failed +from modules.util import Failed, Image from plexapi.exceptions import BadRequest, NotFound from plexapi.video import Movie, Show from urllib.parse import quote @@ -1742,42 +1742,47 @@ class CollectionBuilder: for p in self.posters: logger.info(f"Method: {p} Poster: {self.posters[p]}") - if "url_poster" in self.posters: self.library.upload_image("url_poster", self.obj, self.posters["url_poster"]) - elif "file_poster" in self.posters: self.library.upload_image("file_poster", self.obj, self.posters["file_poster"], url=False) - elif "tmdb_poster" in self.posters: self.library.upload_image("tmdb_poster", self.obj, self.posters["tmdb_poster"]) - elif "tmdb_profile" in self.posters: self.library.upload_image("tmdb_poster", self.obj, self.posters["tmdb_profile"]) - elif "tvdb_poster" in self.posters: self.library.upload_image("tvdb_poster", self.obj, self.posters["tvdb_poster"]) - elif "asset_directory" in self.posters: self.library.upload_image("asset_directory", self.obj, self.posters["asset_directory"], url=False) - elif "tmdb_person" in self.posters: self.library.upload_image("tmdb_person", self.obj, self.posters["tmdb_person"]) - elif "tmdb_collection_details" in self.posters: self.library.upload_image("tmdb_collection_details", self.obj, self.posters["tmdb_collection_details"]) - elif "tmdb_actor_details" in self.posters: self.library.upload_image("tmdb_actor_details", self.obj, self.posters["tmdb_actor_details"]) - elif "tmdb_crew_details" in self.posters: self.library.upload_image("tmdb_crew_details", self.obj, self.posters["tmdb_crew_details"]) - elif "tmdb_director_details" in self.posters: self.library.upload_image("tmdb_director_details", self.obj, self.posters["tmdb_director_details"]) - elif "tmdb_producer_details" in self.posters: self.library.upload_image("tmdb_producer_details", self.obj, self.posters["tmdb_producer_details"]) - elif "tmdb_writer_details" in self.posters: self.library.upload_image("tmdb_writer_details", self.obj, self.posters["tmdb_writer_details"]) - elif "tmdb_movie_details" in self.posters: self.library.upload_image("tmdb_movie_details", self.obj, self.posters["tmdb_movie_details"]) - elif "tvdb_movie_details" in self.posters: self.library.upload_image("tvdb_movie_details", self.obj, self.posters["tvdb_movie_details"]) - elif "tvdb_show_details" in self.posters: self.library.upload_image("tvdb_show_details", self.obj, self.posters["tvdb_show_details"]) - elif "tmdb_show_details" in self.posters: self.library.upload_image("tmdb_show_details", self.obj, self.posters["tmdb_show_details"]) - else: logger.info("No poster to update") - if len(self.backgrounds) > 1: logger.info(f"{len(self.backgrounds)} backgrounds found:") for b in self.backgrounds: logger.info(f"Method: {b} Background: {self.backgrounds[b]}") - if "url_background" in self.backgrounds: self.library.upload_image("url_background", self.obj, self.backgrounds["url_background"], poster=False) - elif "file_background" in self.backgrounds: self.library.upload_image("file_background", self.obj, self.backgrounds["file_background"], poster=False, url=False) - elif "tmdb_background" in self.backgrounds: self.library.upload_image("tmdb_background", self.obj, self.backgrounds["tmdb_background"], poster=False) - elif "tvdb_background" in self.backgrounds: self.library.upload_image("tvdb_background", self.obj, self.backgrounds["tvdb_background"], poster=False) - elif "asset_directory" in self.backgrounds: self.library.upload_image("asset_directory", self.obj, self.backgrounds["asset_directory"], poster=False, url=False) - elif "tmdb_collection_details" in self.backgrounds: self.library.upload_image("tmdb_collection_details", self.obj, self.backgrounds["tmdb_collection_details"], poster=False) - elif "tmdb_movie_details" in self.backgrounds: self.library.upload_image("tmdb_movie_details", self.obj, self.backgrounds["tmdb_movie_details"], poster=False) - elif "tvdb_movie_details" in self.backgrounds: self.library.upload_image("tvdb_movie_details", self.obj, self.backgrounds["tvdb_movie_details"], poster=False) - elif "tvdb_show_details" in self.backgrounds: self.library.upload_image("tvdb_show_details", self.obj, self.backgrounds["tvdb_show_details"], poster=False) - elif "tmdb_show_details" in self.backgrounds: self.library.upload_image("tmdb_show_details", self.obj, self.backgrounds["tmdb_show_details"], poster=False) + poster = None + if "url_poster" in self.posters: poster = Image("url_poster", self.posters["url_poster"]) + elif "file_poster" in self.posters: poster = Image("file_poster", self.posters["file_poster"], is_url=False) + elif "tmdb_poster" in self.posters: poster = Image("tmdb_poster", self.posters["tmdb_poster"]) + elif "tmdb_profile" in self.posters: poster = Image("tmdb_poster", self.posters["tmdb_profile"]) + elif "tvdb_poster" in self.posters: poster = Image("tvdb_poster", self.posters["tvdb_poster"]) + elif "asset_directory" in self.posters: poster = Image("asset_directory", self.posters["asset_directory"], is_url=False) + elif "tmdb_person" in self.posters: poster = Image("tmdb_person", self.posters["tmdb_person"]) + elif "tmdb_collection_details" in self.posters: poster = Image("tmdb_collection_details", self.posters["tmdb_collection_details"]) + elif "tmdb_actor_details" in self.posters: poster = Image("tmdb_actor_details", self.posters["tmdb_actor_details"]) + elif "tmdb_crew_details" in self.posters: poster = Image("tmdb_crew_details", self.posters["tmdb_crew_details"]) + elif "tmdb_director_details" in self.posters: poster = Image("tmdb_director_details", self.posters["tmdb_director_details"]) + elif "tmdb_producer_details" in self.posters: poster = Image("tmdb_producer_details", self.posters["tmdb_producer_details"]) + elif "tmdb_writer_details" in self.posters: poster = Image("tmdb_writer_details", self.posters["tmdb_writer_details"]) + elif "tmdb_movie_details" in self.posters: poster = Image("tmdb_movie_details", self.posters["tmdb_movie_details"]) + elif "tvdb_movie_details" in self.posters: poster = Image("tvdb_movie_details", self.posters["tvdb_movie_details"]) + elif "tvdb_show_details" in self.posters: poster = Image("tvdb_show_details", self.posters["tvdb_show_details"]) + elif "tmdb_show_details" in self.posters: poster = Image("tmdb_show_details", self.posters["tmdb_show_details"]) + else: logger.info("No poster to update") + + background = None + if "url_background" in self.backgrounds: background = Image("url_background", self.backgrounds["url_background"], is_poster=False) + elif "file_background" in self.backgrounds: background = Image("file_background", self.backgrounds["file_background"], is_poster=False, is_url=False) + elif "tmdb_background" in self.backgrounds: background = Image("tmdb_background", self.backgrounds["tmdb_background"], is_poster=False) + elif "tvdb_background" in self.backgrounds: background = Image("tvdb_background", self.backgrounds["tvdb_background"], is_poster=False) + elif "asset_directory" in self.backgrounds: background = Image("asset_directory", self.backgrounds["asset_directory"], is_poster=False, is_url=False) + elif "tmdb_collection_details" in self.backgrounds: background = Image("tmdb_collection_details", self.backgrounds["tmdb_collection_details"], is_poster=False) + elif "tmdb_movie_details" in self.backgrounds: background = Image("tmdb_movie_details", self.backgrounds["tmdb_movie_details"], is_poster=False) + elif "tvdb_movie_details" in self.backgrounds: background = Image("tvdb_movie_details", self.backgrounds["tvdb_movie_details"], is_poster=False) + elif "tvdb_show_details" in self.backgrounds: background = Image("tvdb_show_details", self.backgrounds["tvdb_show_details"], is_poster=False) + elif "tmdb_show_details" in self.backgrounds: background = Image("tmdb_show_details", self.backgrounds["tmdb_show_details"], is_poster=False) else: logger.info("No background to update") + if poster or background: + self.library.upload_images(self.obj, poster=poster, background=background) + def run_collections_again(self): self.obj = self.library.get_collection(self.name) name, collection_items = self.library.get_collection_name_and_items(self.obj, self.smart_label_collection) diff --git a/modules/cache.py b/modules/cache.py index 59cebd8f..75e58202 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -246,5 +246,5 @@ class Cache: with sqlite3.connect(self.cache_path) as connection: connection.row_factory = sqlite3.Row with closing(connection.cursor()) as cursor: - cursor.execute("INSERT OR IGNORE INTO image_map(rating_key, library) VALUES(?, ?)", (rating_key, library)) + cursor.execute("INSERT OR IGNORE INTO image_map(rating_key, library, type) VALUES(?, ?, ?)", (rating_key, library, image_type)) cursor.execute("UPDATE image_map SET location = ?, compare = ? WHERE rating_key = ? AND library = ? AND type = ?", (location, compare, rating_key, library, image_type)) diff --git a/modules/meta.py b/modules/meta.py index 0b95cd83..5bd04df5 100644 --- a/modules/meta.py +++ b/modules/meta.py @@ -1,7 +1,7 @@ import logging, os, re, requests from datetime import datetime from modules import plex, util -from modules.util import Failed +from modules.util import Failed, Image from plexapi.exceptions import NotFound from ruamel import yaml @@ -141,21 +141,26 @@ class Metadata: return self.library.edit_tags(attr, obj, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags) return False - def set_image(attr, obj, group, alias, poster=True, url=True): + def set_image(attr, group, alias, is_poster=True, is_url=True): if group[alias[attr]]: - self.library.upload_image(attr, obj, group[alias[attr]], poster=poster, url=url) + return Image(attr, group[alias[attr]], is_poster=is_poster, is_url=is_url) else: logger.error(f"Metadata Error: {attr} attribute is blank") def set_images(obj, group, alias): + poster = None + background = None if "url_poster" in alias: - set_image("url_poster", obj, group, alias) + poster = set_image("url_poster", group, alias) elif "file_poster" in alias: - set_image("file_poster", obj, group, alias, url=False) + poster = set_image("file_poster", group, alias, is_url=False) if "url_background" in alias: - set_image("url_background", obj, group, alias, poster=False) + background = set_image("url_background", group, alias, is_poster=False) elif "file_background" in alias: - set_image("file_background", obj, group, alias, poster=False, url=False) + background = set_image("file_background", group, alias, is_poster=False, is_url=False) + + if poster or background: + self.library.upload_images(obj, poster=poster, background=background) logger.info("") util.separator() diff --git a/modules/plex.py b/modules/plex.py index 32402e28..11489083 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -1,7 +1,7 @@ import glob, logging, os, requests from modules import builder, util from modules.meta import Metadata -from modules.util import Failed +from modules.util import Failed, Image import plexapi from plexapi import utils from plexapi.exceptions import BadRequest, NotFound, Unauthorized @@ -426,39 +426,59 @@ class PlexAPI: self.reload(item) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) - def _upload_image(self, item, location, poster=True, url=True): - if poster and url: - item.uploadPoster(url=location) - elif poster: - item.uploadPoster(filepath=location) - elif url: - item.uploadArt(url=location) + def _upload_image(self, item, image): + if image.is_poster and image.is_url: + item.uploadPoster(url=image.location) + elif image.is_poster: + item.uploadPoster(filepath=image.location) + elif image.is_url: + item.uploadArt(url=image.location) else: - item.uploadArt(filepath=location) + item.uploadArt(filepath=image.location) + self.reload(item) - def upload_image(self, attr, item, location, name="", poster=True, url=True): - image_type = "poster" if poster else "background" - message = f"{name}{image_type} to [{'URL' if url else 'File'}] {location}" - try: - image = None - if self.config.Cache: - image, image_compare = self.config.Cache.query_image_map(item.ratingKey, self.original_mapping_name, image_type) - compare = location if url else os.stat(location).st_size - if compare != image_compare: - image = None - if image is None \ - or (image_type == "poster" and image != item.thumb) \ - or (image_type == "background" and image != item.art): - self._upload_image(item, location, poster=poster, url=url) + def upload_images(self, item, poster=None, background=None): + poster_uploaded = False + if poster is not None: + try: + image = None if self.config.Cache: - self.reload(item) - self.config.Cache.update_image_map(item.ratingKey, self.original_mapping_name, image_type, item.thumb if image_type == "poster" else item.art, compare) - logger.info(f"Detail: {attr} updated {message}") - else: - logger.info(f"Detail: {name}{image_type} update not needed") - except BadRequest: - util.print_stacktrace() - logger.error(f"Detail: {attr} failed to update {message}") + image, image_compare = self.config.Cache.query_image_map(item.ratingKey, self.original_mapping_name, "poster") + if str(poster.compare) != str(image_compare): + image = None + if image is None or image != item.thumb: + self._upload_image(item, poster) + poster_uploaded = True + logger.info(f"Detail: {poster.attribute} updated {poster.message}") + else: + logger.info(f"Detail: {poster.prefix}poster update not needed") + except BadRequest: + util.print_stacktrace() + logger.error(f"Detail: {poster.attribute} failed to update {poster.message}") + + background_uploaded = False + if background is not None: + try: + image = None + if self.config.Cache: + image, image_compare = self.config.Cache.query_image_map(item.ratingKey, self.original_mapping_name, "background") + if str(background.compare) != str(image_compare): + image = None + if image is None or image != item.art: + self._upload_image(item, background) + background_uploaded = True + logger.info(f"Detail: {background.attribute} updated {background.message}") + else: + logger.info(f"Detail: {background.prefix}background update not needed") + except BadRequest: + util.print_stacktrace() + logger.error(f"Detail: {background.attribute} failed to update {background.message}") + + if self.config.Cache: + if poster_uploaded: + self.config.Cache.update_image_map(item.ratingKey, self.original_mapping_name, "poster", item.thumb, poster.compare) + if background_uploaded: + self.config.Cache.update_image_map(item.ratingKey, self.original_mapping_name, "background", item.art, background.compare) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_search_choices(self, search_name, title=True): @@ -736,6 +756,8 @@ class PlexAPI: elif not name: name = os.path.basename(os.path.dirname(item.locations[0]) if self.is_movie else item.locations[0]) for ad in dirs: + poster = None + background = None poster_image = None background_image = None if self.asset_folders: @@ -750,12 +772,14 @@ class PlexAPI: if len(matches) > 0: poster_image = os.path.abspath(matches[0]) if upload: - self.upload_image("asset_directory", item, poster_image, name=f"{item.title}'s ", url=False) + poster = Image("asset_directory", poster_image, prefix=f"{item.title}'s ", is_url=False) matches = glob.glob(background_filter) if len(matches) > 0: background_image = os.path.abspath(matches[0]) if upload: - self.upload_image("asset_directory", item, background_image, name=f"{item.title}'s ", poster=False, url=False) + background = Image("asset_directory", background_image, prefix=f"{item.title}'s ", is_poster=False, is_url=False) + if poster or background: + self.upload_images(item, poster=poster, background=background) if collection_mode: for ite in self.query(item.items): self.update_item_from_assets(ite, dirs=[os.path.join(ad, name)]) @@ -770,7 +794,7 @@ class PlexAPI: matches = glob.glob(season_filter) if len(matches) > 0: season_path = os.path.abspath(matches[0]) - self.upload_image("asset_directory", season, season_path, name=f"{item.title} Season {season.seasonNumber}'s ", url=False) + self.upload_images(season, poster=Image("asset_directory", season_path, prefix=f"{item.title} Season {season.seasonNumber}'s ", is_url=False)) for episode in self.query(season.episodes): if self.asset_folders: episode_filter = os.path.join(ad, name, f"{episode.seasonEpisode.upper()}.*") @@ -779,5 +803,5 @@ class PlexAPI: matches = glob.glob(episode_filter) if len(matches) > 0: episode_path = os.path.abspath(matches[0]) - self.upload_image("asset_directory", episode, episode_path, name=f"{item.title} {episode.seasonEpisode.upper()}'s ", url=False) + self.upload_images(episode, poster=Image("asset_directory", episode_path, prefix=f"{item.title} {episode.seasonEpisode.upper()}'s ", is_url=False)) return None, None diff --git a/modules/util.py b/modules/util.py index a6f6105b..e53fb410 100644 --- a/modules/util.py +++ b/modules/util.py @@ -1,4 +1,4 @@ -import logging, re, signal, sys, time, traceback +import logging, os, re, signal, sys, time, traceback from datetime import datetime from pathvalidate import is_valid_filename, sanitize_filename from plexapi.exceptions import BadRequest, NotFound, Unauthorized @@ -18,6 +18,16 @@ class TimeoutExpired(Exception): class Failed(Exception): pass +class Image: + def __init__(self, attribute, location, prefix="", is_poster=True, is_url=True): + self.attribute = attribute + self.location = location + self.prefix = prefix + self.is_poster = is_poster + self.is_url = is_url + self.compare = location if is_url else os.stat(location).st_size + self.message = f"{prefix}{'poster' if is_poster else 'background'} to [{'URL' if is_url else 'File'}] {location}" + def retry_if_not_failed(exception): return not isinstance(exception, Failed)