diff --git a/modules/builder.py b/modules/builder.py index fe28d6b1..0e20cdf6 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -131,13 +131,25 @@ all_details = [ "url_background", "tmdb_background", "tvdb_background", "file_background", "name_mapping", "label", "show_filtered", "show_missing", "save_missing" ] -collectionless_details = [ - "sort_title", "content_rating", - "summary", "tmdb_summary", "tmdb_description", "tmdb_biography", - "collection_order", "plex_collectionless", - "url_poster", "tmdb_poster", "tmdb_profile", "file_poster", - "url_background", "file_background", - "name_mapping", "label", "label_sync_mode", "test" +summary_details = [ + "summary", "tmdb_summary", "tmdb_description", "tmdb_biography", "tvdb_summary", + "tvdb_description", "trakt_description", "letterboxd_description" +] +poster_details = [ + "url_poster", "tmdb_poster", "tmdb_profile", "tvdb_poster", "file_poster" +] +background_details = [ + "url_background", "tmdb_background", "tvdb_background", "file_background" +] +boolean_details = [ + "show_filtered", + "show_missing", + "save_missing" +] +string_details = [ + "sort_title", + "content_rating", + "name_mapping" ] ignored_details = [ "smart_filter", @@ -148,13 +160,13 @@ ignored_details = [ "sync_mode", "template", "test", - "tmdb_person" -] -boolean_details = [ - "show_filtered", - "show_missing", - "save_missing" + "tmdb_person", + "build_collection" ] +collectionless_details = [ + "collection_order", "plex_collectionless", + "label", "label_sync_mode", "test" +] + poster_details + background_details + summary_details + string_details all_filters = [ "actor", "actor.not", "audio_language", "audio_language.not", @@ -439,7 +451,7 @@ class CollectionBuilder: if "build_collection" in methods: logger.info("") logger.info("Validating Method: build_collection") - if not self.data[methods["build_collection"]]: + if self.data[methods["build_collection"]] is None: logger.warning(f"Collection Warning: build_collection attribute is blank defaulting to true") else: logger.debug(f"Value: {self.data[methods['build_collection']]}") @@ -627,8 +639,8 @@ class CollectionBuilder: self.item_details[method_name] = str(method_data).lower() elif method_name in boolean_details: self.details[method_name] = util.get_bool(method_name, method_data) - elif method_name in all_details: - self.details[method_name] = method_data + elif method_name in string_details: + self.details[method_name] = str(method_data) elif method_name == "radarr_add": self.add_to_radarr = util.get_bool(method_name, method_data) elif method_name == "radarr_folder": @@ -1651,6 +1663,8 @@ class CollectionBuilder: logger.error(e) for item in items: + poster, background = self.library.update_item_from_assets(item) + self.library.upload_images(item, poster=poster, background=background) self.library.edit_tags("label", item, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags) advance_edits = {} for method_name, method_data in self.item_details.items(): @@ -1745,7 +1759,7 @@ class CollectionBuilder: if "name_mapping" in self.details: if self.details["name_mapping"]: name_mapping = self.details["name_mapping"] else: logger.error("Collection Error: name_mapping attribute is blank") - poster_image, background_image = self.library.update_item_from_assets(self.obj, collection_mode=True, upload=False, name=name_mapping) + poster_image, background_image = self.library.find_collection_assets(self.obj, name=name_mapping) if poster_image: self.posters["asset_directory"] = poster_image if background_image: @@ -1767,7 +1781,7 @@ class CollectionBuilder: 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 "asset_directory" in self.posters: poster = self.posters["asset_directory"] 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"]) @@ -1786,7 +1800,7 @@ class CollectionBuilder: 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 "asset_directory" in self.backgrounds: background = self.backgrounds["asset_directory"] 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) diff --git a/modules/config.py b/modules/config.py index fca04696..77519af0 100644 --- a/modules/config.py +++ b/modules/config.py @@ -23,14 +23,12 @@ from ruamel import yaml logger = logging.getLogger("Plex Meta Manager") sync_modes = {"append": "Only Add Items to the Collection", "sync": "Add & Remove Items from the Collection"} -radarr_versions = {"v2": "For Radarr 0.2", "v3": "For Radarr 3.0"} radarr_availabilities = { "announced": "For Announced", "cinemas": "For In Cinemas", "released": "For Released", "db": "For PreDB" } -sonarr_versions = {"v2": "For Sonarr 0.2", "v3": "For Sonarr 3.0"} sonarr_monitors = { "all": "Monitor all episodes except specials", "future": "Monitor episodes that have not aired yet", @@ -58,6 +56,7 @@ class Config: else: raise Failed(f"Config Error: config not found at {os.path.abspath(default_dir)}") logger.info(f"Using {self.config_path} as config") + self.default_dir = default_dir self.test_mode = is_test self.run_start_time = time_scheduled self.run_hour = datetime.strptime(time_scheduled, "%H:%M").hour diff --git a/modules/plex.py b/modules/plex.py index 98a8eb14..c81a3028 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -10,6 +10,7 @@ from plexapi.server import PlexServer from retrying import retry from ruamel import yaml from urllib import parse +from xml.etree.ElementTree import ParseError logger = logging.getLogger("Plex Meta Manager") @@ -260,7 +261,7 @@ class Plex: raise Failed("Plex Error: Plex token is invalid") except ValueError as e: raise Failed(f"Plex Error: {e}") - except requests.exceptions.ConnectionError: + except (requests.exceptions.ConnectionError, ParseError): util.print_stacktrace() raise Failed("Plex Error: Plex url is invalid") self.Plex = next((s for s in self.PlexServer.library.sections() if s.title == params["name"]), None) @@ -697,16 +698,19 @@ class Plex: updated = False key = builder.filter_translation[attr] if attr in builder.filter_translation else attr if add_tags or remove_tags or sync_tags: + _add_tags = [f"{t[:1].upper()}{t[1:]}" for t in add_tags] if add_tags else None + _remove_tags = [f"{t[:1].upper()}{t[1:]}" for t in remove_tags] if remove_tags else None + _sync_tags = [t.lower() for t in sync_tags] if sync_tags else None item_tags = [item_tag.tag for item_tag in getattr(obj, key)] input_tags = [] - if add_tags: - input_tags.extend(add_tags) - if sync_tags: - input_tags.extend(sync_tags) - if sync_tags or remove_tags: + if _add_tags: + input_tags.extend(_add_tags) + if _sync_tags: + input_tags.extend(_sync_tags) + if _sync_tags or _remove_tags: remove_method = getattr(obj, f"remove{attr.capitalize()}") for tag in item_tags: - if (sync_tags and tag not in sync_tags) or (remove_tags and tag in remove_tags): + if (_sync_tags and tag.lower() not in _sync_tags) or (_remove_tags and tag in _remove_tags): updated = True self.query_data(remove_method, tag) logger.info(f"Detail: {attr.capitalize()} {tag} removed") @@ -719,60 +723,75 @@ class Plex: logger.info(f"Detail: {attr.capitalize()} {tag} added") return updated - def update_item_from_assets(self, item, collection_mode=False, upload=True, dirs=None, name=None): - if dirs is None: - dirs = self.asset_directory - if not name and collection_mode: - name = item.title - 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: + def update_item_from_assets(self, item): + name = os.path.basename(os.path.dirname(item.locations[0]) if self.is_movie else item.locations[0]) + for ad in self.asset_directory: poster = None background = None - poster_image = None - background_image = None + item_dir = None if self.asset_folders: - if not os.path.isdir(os.path.join(ad, name)): + if os.path.isdir(os.path.join(ad, name)): + item_dir = os.path.join(ad, name) + else: + matches = glob.glob(os.path.join(ad, "*", name)) + if len(matches) > 0: + item_dir = os.path.abspath(matches[0]) + if item_dir is None: continue - poster_filter = os.path.join(ad, name, "poster.*") - background_filter = os.path.join(ad, name, "background.*") + poster_filter = os.path.join(item_dir, "poster.*") + background_filter = os.path.join(item_dir, "background.*") else: poster_filter = os.path.join(ad, f"{name}.*") background_filter = os.path.join(ad, f"{name}_background.*") matches = glob.glob(poster_filter) if len(matches) > 0: - poster_image = os.path.abspath(matches[0]) - if upload: - poster = Image("asset_directory", poster_image, prefix=f"{item.title}'s ", is_url=False) + poster = Image("asset_directory", os.path.abspath(matches[0]), 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: - 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)]) - if not upload: - return poster_image, background_image - if self.is_show and not collection_mode: + background = Image("asset_directory", os.path.abspath(matches[0]), prefix=f"{item.title}'s ", is_poster=False, is_url=False) + if self.is_show: for season in self.query(item.seasons): - if self.asset_folders: - season_filter = os.path.join(ad, name, f"Season{'0' if season.seasonNumber < 10 else ''}{season.seasonNumber}.*") + if item_dir: + season_filter = os.path.join(item_dir, f"Season{'0' if season.seasonNumber < 10 else ''}{season.seasonNumber}.*") else: season_filter = os.path.join(ad, f"{name}_Season{'0' if season.seasonNumber < 10 else ''}{season.seasonNumber}.*") matches = glob.glob(season_filter) if len(matches) > 0: - season_path = os.path.abspath(matches[0]) - self.upload_images(season, poster=Image("asset_directory", season_path, prefix=f"{item.title} Season {season.seasonNumber}'s ", is_url=False)) + season_poster = Image("asset_directory", os.path.abspath(matches[0]), prefix=f"{item.title} Season {season.seasonNumber}'s ", is_url=False) + self.upload_images(season, poster=season_poster) for episode in self.query(season.episodes): - if self.asset_folders: - episode_filter = os.path.join(ad, name, f"{episode.seasonEpisode.upper()}.*") + if item_dir: + episode_filter = os.path.join(item_dir, f"{episode.seasonEpisode.upper()}.*") else: episode_filter = os.path.join(ad, f"{name}_{episode.seasonEpisode.upper()}.*") matches = glob.glob(episode_filter) if len(matches) > 0: - episode_path = os.path.abspath(matches[0]) - self.upload_images(episode, poster=Image("asset_directory", episode_path, prefix=f"{item.title} {episode.seasonEpisode.upper()}'s ", is_url=False)) + episode_poster = Image("asset_directory", os.path.abspath(matches[0]), prefix=f"{item.title} {episode.seasonEpisode.upper()}'s ", is_url=False) + self.upload_images(episode, poster=episode_poster) + if poster or background: + return poster, background + return None, None + + def find_collection_assets(self, item, name=None): + if name is None: + name = item.title + for ad in self.asset_directory: + poster = None + background = None + if self.asset_folders: + if not os.path.isdir(os.path.join(ad, name)): + continue + poster_filter = os.path.join(ad, name, "poster.*") + background_filter = os.path.join(ad, name, "background.*") + else: + poster_filter = os.path.join(ad, f"{name}.*") + background_filter = os.path.join(ad, f"{name}_background.*") + matches = glob.glob(poster_filter) + if len(matches) > 0: + poster = Image("asset_directory", os.path.abspath(matches[0]), prefix=f"{item.title}'s ", is_url=False) + matches = glob.glob(background_filter) + if len(matches) > 0: + background = Image("asset_directory", os.path.abspath(matches[0]), prefix=f"{item.title}'s ", is_poster=False, is_url=False) + if poster or background: + return poster, background return None, None diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 730aa3be..f49a685a 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -189,9 +189,11 @@ def update_libraries(config): util.separator(f"All {'Movies' if library.is_movie else 'Shows'} Assets Check for {library.name} Library", space=False, border=False) logger.info("") for col in unmanaged_collections: - library.update_item_from_assets(col, collection_mode=True) + poster, background = library.find_collection_assets(col) + library.upload_images(col, poster=poster, background=background) for item in library.get_all(): - library.update_item_from_assets(item) + poster, background = library.update_item_from_assets(item) + library.upload_images(item, poster=poster, background=background) logger.removeHandler(library_handler) @@ -298,6 +300,9 @@ def mass_metadata(config, library): omdb_item = config.OMDb.get_omdb(imdb_id) except Failed as e: logger.info(util.adjust_space(str(e))) + except Exception: + logger.error(f"IMDb ID: {imdb_id}") + raise else: logger.info(util.adjust_space(f"{item.title[:25]:<25} | No IMDb ID for Guid: {item.guid}")) @@ -453,11 +458,10 @@ def run_collection(config, library, metadata, requested_collections): logger.info("") builder.update_details() - if len(builder.item_details) > 0: - logger.info("") - util.separator(f"Updating Details of the Items in {mapping_name} Collection", space=False, border=False) - logger.info("") - builder.update_item_details() + logger.info("") + util.separator(f"Updating Details of the Items in {mapping_name} Collection", space=False, border=False) + logger.info("") + builder.update_item_details() if builder.run_again and (len(builder.run_again_movies) > 0 or len(builder.run_again_shows) > 0): library.run_again.append(builder)