diff --git a/VERSION b/VERSION index aeef2a88..092afa15 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.5-develop118 +1.17.0 diff --git a/config/config.yml.template b/config/config.yml.template index 1ab69782..c3b031a6 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -5,24 +5,33 @@ libraries: # This is called out once within metadata_path: - file: config/Movies.yml # This is a local file on the system - folder: config/Movies/ # This is a local directory on the system - - git: meisnate12/MovieCharts # This is a file within the GitHub Repository + - git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository + - git: PMM/chart/imdb # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository overlay_path: - remove_overlays: false # Set this to true to remove all overlays - file: config/Overlays.yml # This is a local file on the system + - git: PMM/overlays/imdb_top_250 # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository TV Shows: metadata_path: - file: config/TVShows.yml - folder: config/TV Shows/ - - git: meisnate12/ShowCharts # This points to the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository + - git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository + - git: PMM/chart/imdb # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository + overlay_path: + - remove_overlays: false # Set this to true to remove all overlays + - file: config/Overlays.yml # This is a local file on the system + - git: PMM/overlays/imdb_top_250 # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository Anime: metadata_path: - file: config/Anime.yml + - git: PMM/chart/basic # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository + - git: PMM/chart/anilist # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository Music: metadata_path: - file: config/Music.yml playlist_files: - file: config/playlists.yml - - git: meisnate12/Playlists + - git: PMM/playlist # This is a file within the https://github.com/meisnate12/Plex-Meta-Manager-Configs Repository settings: cache: true cache_expiration: 60 diff --git a/docs/conf.py b/docs/conf.py index eb7ce5b6..bca6f40a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -130,7 +130,7 @@ html_theme_options = { ("Overlay Files", "metadata/overlay"), ("Playlist Files", "metadata/playlist"), ("_divider", ), - ("Default Configs and Overlays", "home/guides/defaults"), + ("Default Metadata & Overlays Files", "home/guides/defaults"), ("Scheduling Guide", "home/guides/scheduling"), ("Image Asset Directory Guide", "home/guides/assets"), ("Formula 1 Metadata Guide", "home/guides/formula"), diff --git a/docs/home/guides/defaults.md b/docs/home/guides/defaults.md index 9acd5420..bf6b32f5 100644 --- a/docs/home/guides/defaults.md +++ b/docs/home/guides/defaults.md @@ -1,4 +1,4 @@ -# Default Collections & Overlays +# Default Metadata & Overlays Files There is a default set of Metadata and Overlay Files located in the [PMM Folder](https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/PMM) in the [Plex Meta Manager Configs](https://github.com/meisnate12/Plex-Meta-Manager-Configs) Repository. @@ -143,7 +143,14 @@ libraries: - git: PMM/award/other - git: PMM/award/spirit - git: PMM/award/sundance - - git: PMM/chart/old_movie_chart + - git: PMM/chart/anilist + - git: PMM/chart/basic + - git: PMM/chart/imdb + - git: PMM/chart/myanimelist + - git: PMM/chart/other + - git: PMM/chart/tautulli + - git: PMM/chart/tmdb + - git: PMM/chart/trakt - git: PMM/actor - git: PMM/audio_language - git: PMM/movie/content_rating_us # Choose content_rating_uk or content_rating_us @@ -156,7 +163,7 @@ libraries: - git: PMM/movie/decade - git: PMM/movie/director - git: PMM/movie/franchise - - git: PMM/movie/multi-franchise + - git: PMM/movie/universe - git: PMM/movie/producer - git: PMM/movie/seasonal - git: PMM/movie/streaming @@ -177,7 +184,14 @@ libraries: - git: PMM/award/choice - git: PMM/award/golden - git: PMM/award/emmy - - git: PMM/chart/old_show_chart + - git: PMM/chart/anilist + - git: PMM/chart/basic + - git: PMM/chart/imdb + - git: PMM/chart/myanimelist + - git: PMM/chart/other + - git: PMM/chart/tautulli + - git: PMM/chart/tmdb + - git: PMM/chart/trakt - git: PMM/actor - git: PMM/audio_language - git: PMM/show/content_rating_us # Choose content_rating_uk or content_rating_us @@ -192,7 +206,7 @@ libraries: - git: PMM/show/streaming overlay_path: - remove_overlays: false - - git: PMM/overlays/audio_codec + - git: PMM/overlays/audio_codec - git: PMM/overlays/audio_codec template_variables: overlay_level: episode diff --git a/docs/metadata/details/metadata.md b/docs/metadata/details/metadata.md index 4ad20b8a..c246e84e 100644 --- a/docs/metadata/details/metadata.md +++ b/docs/metadata/details/metadata.md @@ -45,6 +45,7 @@ None of these details work with Playlists. | `item_lock_poster` | **Description:** Locks/Unlocks the poster of every movie/show in the collection
**Default:** `None`
**Values:**
`true`Lock
`false`Unlock
| | `item_lock_background` | **Description:** Locks/Unlocks the background of every movie/show in the collection
**Default:** `None`
**Values:**
`true`Lock
`false`Unlock
| | `item_lock_title` | **Description:** Locks/Unlocks the title of every movie/show in the collection
**Default:** `None`
**Values:**
`true`Lock
`false`Unlock
| +| `item_assets` | **Description:** Checks your assets folders for assets of every movie/show in the collection
**Default:** `false`
**Values:** `true` or `false` | | `item_refresh` | **Description:** Refreshes the metadata of every movie/show in the collection
**Default:** `false`
**Values:** `true` or `false` | | `item_refresh_delay` | **Description:** Amount of time to wait between each `item_refresh` of every movie/show in the collection
**Default:** `0`
**Values:** Number greater than `0` | | `item_tmdb_season_titles` | **Description:** Changes the season titles of every show in the collection to match TMDb
**Default:** `false`
**Values:** `true` or `false` | diff --git a/docs/metadata/overlay.md b/docs/metadata/overlay.md index ac0e06b8..61ae9542 100644 --- a/docs/metadata/overlay.md +++ b/docs/metadata/overlay.md @@ -145,9 +145,11 @@ You can control the backdrop of the text using the various `back_*` attributes. The `horizontal_offset` and `vertical_offset` overlay attributes are required when using Text Overlays. -You can add an items rating number (`8.7`) to the image by using `text(audience_rating)`, `text(critic_rating)`, or `text(user_rating)` +You can add an items rating number (`8.7`, `9.0`) to the image by using `text(audience_rating)`, `text(critic_rating)`, or `text(user_rating)` -You can add an items rating percentage (`87%`) to the image by using `text(audience_rating%)`, `text(critic_rating%)`, or `text(user_rating%)` +You can add an items rating number removing `.0` as needed (`8.7`, `9`) to the image by using `text(audience_rating#)`, `text(critic_rating#)`, or `text(user_rating#)` + +You can add an items rating percentage (`87%`, `90%`) to the image by using `text(audience_rating%)`, `text(critic_rating%)`, or `text(user_rating%)` You can use the `mass_audience_rating_update` or `mass_critic_rating_update` [Library Operation](../config/operations) to update your plex ratings to various services like `tmdb`, `imdb`, `mdb`, `metacritic`, `letterboxd` and many more. diff --git a/modules/builder.py b/modules/builder.py index 4ee192bd..e7025aee 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -49,8 +49,8 @@ details = [ collectionless_details = ["collection_order", "plex_collectionless", "label", "label_sync_mode", "test"] + \ poster_details + background_details + summary_details + string_details item_false_details = ["item_lock_background", "item_lock_poster", "item_lock_title"] -item_bool_details = ["item_tmdb_season_titles", "revert_overlay", "item_refresh"] + item_false_details -item_details = ["non_item_remove_label", "item_label", "item_radarr_tag", "item_sonarr_tag", "item_overlay", "item_refresh_delay"] + item_bool_details + list(plex.item_advance_keys.keys()) +item_bool_details = ["item_tmdb_season_titles", "revert_overlay", "item_assets", "item_refresh"] + item_false_details +item_details = ["non_item_remove_label", "item_label", "item_radarr_tag", "item_sonarr_tag", "item_refresh_delay"] + item_bool_details + list(plex.item_advance_keys.keys()) none_details = ["label.sync", "item_label.sync", "radarr_taglist", "sonarr_taglist"] radarr_details = [ "radarr_add_missing", "radarr_add_existing", "radarr_upgrade_existing", "radarr_folder", "radarr_monitor", @@ -160,7 +160,7 @@ playlist_attributes = [ ] + custom_sort_builders + summary_details + poster_details + radarr_details + sonarr_details music_attributes = [ "non_item_remove_label", "item_label", "collection_filtering", "item_lock_background", "item_lock_poster", "item_lock_title", - "item_refresh", "item_refresh_delay", "plex_search", "plex_all", "filters" + "item_assets", "item_refresh", "item_refresh_delay", "plex_search", "plex_all", "filters" ] + details + summary_details + poster_details + background_details class CollectionBuilder: @@ -906,38 +906,6 @@ class CollectionBuilder: raise Failed(f"{self.Type} Error: Cannot use {method_name} and {method_name}.remove together") self.item_details[method_name] = util.get_list(method_data, lower=True) self.item_details["apply_tags"] = method_mod[1:] if method_mod else "" - elif method_name == "item_overlay": - if isinstance(method_data, dict): - if "name" not in method_data or not method_data["name"]: - raise Failed(f"{self.Type} Error: item_overlay must have the name attribute") - if "git" in method_data and method_data["git"]: - url = f"https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/{method_data['git']}.png" - elif "url" in method_data and method_data["url"]: - url = method_data["url"] - else: - raise Failed(f"{self.Type} Error: item_overlay must have either the git or url attribute") - name = method_data["name"] - response = self.config.get(url) - if response.status_code >= 400: - raise Failed(f"{self.Type} Error: Overlay Image not found at: {url}") - overlay_dir = os.path.join(self.config.default_dir, "overlays", name) - if not os.path.exists(overlay_dir) or not os.path.isdir(overlay_dir): - os.makedirs(overlay_dir, exist_ok=False) - logger.info(f"Creating Overlay Folder found at: {overlay_dir}") - overlay = os.path.join(overlay_dir, "overlay.png") - with open(overlay, "wb") as handler: - handler.write(response.content) - while util.is_locked(overlay): - time.sleep(1) - else: - overlay = os.path.join(self.config.default_dir, "overlays", method_data, "overlay.png") - name = method_data - if not os.path.exists(overlay): - raise Failed(f"{self.Type} Error: {name} overlay image not found at {overlay}") - if name in self.library.overlays_old: - raise Failed("Each Overlay can only be used once per Library") - self.library.overlays_old.append(name) - self.item_details[method_name] = name elif method_name == "item_refresh_delay": self.item_details[method_name] = util.parse(self.Type, method_name, method_data, datatype="int", default=0, minimum=0) elif method_name in item_bool_details: @@ -2327,6 +2295,8 @@ class CollectionBuilder: tmdb_paths = [] tvdb_paths = [] for item in self.items: + if "item_assets" in self.item_details and self.library.asset_directory and "Overlay" not in [la.tag for la in item.labels]: + self.library.find_and_upload_assets(item) self.library.edit_tags("label", item, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags) path = os.path.dirname(str(item.locations[0])) if self.library.is_movie else str(item.locations[0]) if self.library.Radarr and item.ratingKey in self.library.movie_rating_key_map: diff --git a/modules/operations.py b/modules/operations.py index 2df6c109..1ce11e68 100644 --- a/modules/operations.py +++ b/modules/operations.py @@ -82,52 +82,7 @@ class Operations: current_labels = [la.tag for la in item.labels] if self.library.assets_for_all or self.library.mass_imdb_parental_labels else [] if self.library.assets_for_all and self.library.asset_directory and "Overlay" not in current_labels: - try: - poster, background, item_dir, name = self.library.find_item_assets(item) - if poster or background: - self.library.upload_images(item, poster=poster, background=background) - elif self.library.show_missing_assets: - logger.warning(f"Asset Warning: No poster or background found in the assets folder '{item_dir}'") - - if isinstance(item, Show): - missing_seasons = "" - missing_episodes = "" - found_season = False - found_episode = False - for season in self.library.query(item.seasons): - season_poster, season_background, _, _ = self.library.find_item_assets(season, item_asset_directory=item_dir) - if season_poster: - found_season = True - elif self.library.show_missing_season_assets and season.seasonNumber > 0: - missing_seasons += f"\nMissing Season {season.seasonNumber} Poster" - if season_poster or season_background: - self.library.upload_images(season, poster=season_poster, background=season_background) - for episode in self.library.query(season.episodes): - if episode.seasonEpisode: - episode_poster, episode_background, _, _ = self.library.find_item_assets(episode, item_asset_directory=item_dir) - if episode_poster or episode_background: - found_episode = True - self.library.upload_images(episode, poster=episode_poster, background=episode_background) - elif self.library.show_missing_episode_assets: - missing_episodes += f"\nMissing {episode.seasonEpisode.upper()} Title Card" - if (found_season and missing_seasons) or (found_episode and missing_episodes): - logger.info(f"Missing Posters for {item.title}{missing_seasons}{missing_episodes}") - if isinstance(item, Artist): - missing_assets = "" - found_album = False - for album in self.library.query(item.albums): - album_poster, album_background, _, _ = self.library.find_item_assets(album, item_asset_directory=item_dir) - if album_poster or album_background: - found_album = True - elif self.library.show_missing_season_assets: - missing_assets += f"\nMissing Album {album.title} Poster" - if album_poster or album_background: - self.library.upload_images(album, poster=album_poster, background=album_background) - if self.library.show_missing_season_assets and found_album and missing_assets: - logger.info(f"Missing Album Posters for {item.title}{missing_assets}") - except Failed as e: - if self.library.show_missing_assets: - logger.warning(e) + self.library.find_and_upload_assets(item) tmdb_id, tvdb_id, imdb_id = self.library.get_ids(item) diff --git a/modules/overlays.py b/modules/overlays.py index d921c6cb..35c1ac28 100644 --- a/modules/overlays.py +++ b/modules/overlays.py @@ -179,9 +179,10 @@ class Overlays: for over_name in text_names: overlay = properties[over_name] text = over_name[5:-1] - if text in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%"]]: + if text in [f"{a}{s}" for a in ["audience_rating", "critic_rating", "user_rating"] for s in ["", "%", "#"]]: per = text.endswith("%") - rating_type = text[:-1] if per else text + flat = text.endswith("#") + rating_type = text[:-1] if per or flat else text actual = plex.attribute_translation[rating_type] if not hasattr(item, actual) or getattr(item, actual) is None: logger.warning(f"Overlay Warning: No {rating_type} found") @@ -191,6 +192,8 @@ class Overlays: self.config.Cache.update_overlay_ratings(item.ratingKey, rating_type, text) if per: text = f"{int(text * 10)}%" + if flat and str(text).endswith(".0"): + text = str(text)[:-2] overlay_image = overlay.get_text_overlay(text, image_width, image_height) else: overlay_image = overlay.landscape if isinstance(item, Episode) else overlay.image diff --git a/modules/plex.py b/modules/plex.py index 11c03291..12d17593 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -913,6 +913,54 @@ class Plex(Library): self.upload_images(item, poster=poster, background=background) return asset_location + def find_and_upload_assets(self, item): + try: + poster, background, item_dir, name = self.find_item_assets(item) + if poster or background: + self.upload_images(item, poster=poster, background=background) + elif self.show_missing_assets: + logger.warning(f"Asset Warning: No poster or background found in the assets folder '{item_dir}'") + + if isinstance(item, Show): + missing_seasons = "" + missing_episodes = "" + found_season = False + found_episode = False + for season in self.query(item.seasons): + season_poster, season_background, _, _ = self.find_item_assets(season, item_asset_directory=item_dir) + if season_poster: + found_season = True + elif self.show_missing_season_assets and season.seasonNumber > 0: + missing_seasons += f"\nMissing Season {season.seasonNumber} Poster" + if season_poster or season_background: + self.upload_images(season, poster=season_poster, background=season_background) + for episode in self.query(season.episodes): + if episode.seasonEpisode: + episode_poster, episode_background, _, _ = self.find_item_assets(episode, item_asset_directory=item_dir) + if episode_poster or episode_background: + found_episode = True + self.upload_images(episode, poster=episode_poster, background=episode_background) + elif self.show_missing_episode_assets: + missing_episodes += f"\nMissing {episode.seasonEpisode.upper()} Title Card" + if (found_season and missing_seasons) or (found_episode and missing_episodes): + logger.info(f"Missing Posters for {item.title}{missing_seasons}{missing_episodes}") + if isinstance(item, Artist): + missing_assets = "" + found_album = False + for album in self.query(item.albums): + album_poster, album_background, _, _ = self.find_item_assets(album, item_asset_directory=item_dir) + if album_poster or album_background: + found_album = True + elif self.show_missing_season_assets: + missing_assets += f"\nMissing Album {album.title} Poster" + if album_poster or album_background: + self.upload_images(album, poster=album_poster, background=album_background) + if self.show_missing_season_assets and found_album and missing_assets: + logger.info(f"Missing Album Posters for {item.title}{missing_assets}") + except Failed as e: + if self.show_missing_assets: + logger.warning(e) + def find_item_assets(self, item, item_asset_directory=None, asset_directory=None): poster = None background = None diff --git a/modules/trakt.py b/modules/trakt.py index e21085b3..bb871155 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -218,6 +218,7 @@ class Trakt: raise Failed(f"({response.status_code}) {response.reason}") json_data = response.json() if self.config.trace_mode: + logger.debug(f"Headers: {response.headers}") logger.debug(f"Response: {json_data}") if isinstance(json_data, dict): return json_data