diff --git a/VERSION b/VERSION index 0d307c09..a0e273f9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.17.3-develop157 +1.17.3-develop158 diff --git a/docs/conf.py b/docs/conf.py index a0ab2e68..7ae0641f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -160,6 +160,8 @@ html_theme_options = { ("Mass Originally Available Update", "config/operations", "#mass-originally-available-update"), ("Mass * Rating Update", "config/operations", "#mass--rating-update"), ("Mass Episode * Rating Update", "config/operations", "#mass-episode--rating-update"), + ("Mass Poster Update", "config/operations", "#mass-poster-update"), + ("Mass Background Update", "config/operations", "#mass-background-update"), ("Mass IMDb Parental Labels", "config/operations", "#mass-imdb-parental-labels"), ("Mass Collection Mode", "config/operations", "#mass-collection-mode"), ("Update Blank Track Titles", "config/operations", "#update-blank-track-titles"), diff --git a/docs/config/operations.md b/docs/config/operations.md index 66720586..40b32528 100644 --- a/docs/config/operations.md +++ b/docs/config/operations.md @@ -16,29 +16,31 @@ libraries: The available attributes for the operations attribute are as follows -| Attribute | Description | -|:----------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------| -| [Assets For All](#assets-for-all) | Search in assets for images for every item in your library. | -| [Delete Collections With Less](#delete-collections-with-less) | Deletes every collection with less than the given number of items. | -| [Delete Unmanaged Collections](#delete-unmanaged-collections) | Deletes every unmanaged collection. | -| [Mass Genre Update](#mass-genre-update) | Updates every item's genres in the library to the chosen site's genres. | -| [Mass Content Rating Update](#mass-content-rating-update) | Updates every item's content rating in the library to the chosen site's content rating. | -| [Mass Original Title Update](#mass-original-title-update) | Updates every item's original title in the library to the chosen site's original title. | -| [Mass Originally Available Update](#mass-originally-available-update) | Updates every item's originally available date in the library to the chosen site's date. | -| [Mass * Rating Update](#mass--rating-update) | Updates every item's audience/critic/user rating in the library to the chosen site's rating. | -| [Mass Episode * Rating Update](#mass-episode--rating-update) | Updates every item's episode's audience/critic/user rating in the library to the chosen site's rating | -| [Mass IMDb Parental Labels](#mass-imdb-parental-labels) | Updates every item's labels in the library to match the IMDb Parental Guide. | -| [Mass Collection Mode](#mass-collection-mode) | Updates every Collection in your library to the specified Collection Mode. | -| [Update Blank Track Titles](#update-blank-track-titles) | Search though every track in a music library and replace any blank track titles with the tracks sort title. | -| [Remove Title Parentheses](#remove-title-parentheses) | Search through every title and remove all ending parentheses in an items title if the title isn not locked. | -| [Split Duplicates](#split-duplicates) | Splits all duplicate movies/shows found in this library. | -| [Radarr Add All](#radarr-add-all) | Adds every item in the library to Radarr. | -| [Radarr Remove By Tag](#radarr-remove-by-tag) | Removes every item from Radarr with the Tags given. | -| [Sonarr Add All](#sonarr-add-all) | Adds every item in the library to Sonarr. | -| [Sonarr Remove By Tag](#sonarr-remove-by-tag) | Removes every item from Sonarr with the Tags given. | -| [Genre Mapper](#genre-mapper) | Allows genres to be changed to other genres or be removed from every item in your library. | -| [Content Rating Mapper](#content-rating-mapper) | Allows content ratings to be changed to other content ratings or be removed from every item in your library. | -| [Metadata Backup](#metadata-backup) | Creates/Maintains a PMM [Metadata File](../metadata/metadata) with a full `metadata` mapping based on the library's items locked attributes. | +| Attribute | Description | +|:----------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Assets For All](#assets-for-all) | Search in assets for images for every item in your library. | +| [Delete Collections With Less](#delete-collections-with-less) | Deletes every collection with less than the given number of items. | +| [Delete Unmanaged Collections](#delete-unmanaged-collections) | Deletes every unmanaged collection. | +| [Mass Genre Update](#mass-genre-update) | Updates every item's genres in the library to the chosen site's genres. | +| [Mass Content Rating Update](#mass-content-rating-update) | Updates every item's content rating in the library to the chosen site's content rating. | +| [Mass Original Title Update](#mass-original-title-update) | Updates every item's original title in the library to the chosen site's original title. | +| [Mass Originally Available Update](#mass-originally-available-update) | Updates every item's originally available date in the library to the chosen site's date. | +| [Mass * Rating Update](#mass--rating-update) | Updates every item's audience/critic/user rating in the library to the chosen site's rating. | +| [Mass Episode * Rating Update](#mass-episode--rating-update) | Updates every item's episode's audience/critic/user rating in the library to the chosen site's rating. | +| [Mass Poster Update](#mass-poster-update) | Updates every item's poster to the chosen sites poster. Will fallback to `plex` if the given option fails. Assets will be used over anything else. | +| [Mass Background Update](#mass-background-update) | Updates every item's background to the chosen sites background. Will fallback to `plex` if the given option fails. Assets will be used over anything else. | +| [Mass IMDb Parental Labels](#mass-imdb-parental-labels) | Updates every item's labels in the library to match the IMDb Parental Guide. | +| [Mass Collection Mode](#mass-collection-mode) | Updates every Collection in your library to the specified Collection Mode. | +| [Update Blank Track Titles](#update-blank-track-titles) | Search though every track in a music library and replace any blank track titles with the tracks sort title. | +| [Remove Title Parentheses](#remove-title-parentheses) | Search through every title and remove all ending parentheses in an items title if the title isn not locked. | +| [Split Duplicates](#split-duplicates) | Splits all duplicate movies/shows found in this library. | +| [Radarr Add All](#radarr-add-all) | Adds every item in the library to Radarr. | +| [Radarr Remove By Tag](#radarr-remove-by-tag) | Removes every item from Radarr with the Tags given. | +| [Sonarr Add All](#sonarr-add-all) | Adds every item in the library to Sonarr. | +| [Sonarr Remove By Tag](#sonarr-remove-by-tag) | Removes every item from Sonarr with the Tags given. | +| [Genre Mapper](#genre-mapper) | Allows genres to be changed to other genres or be removed from every item in your library. | +| [Content Rating Mapper](#content-rating-mapper) | Allows content ratings to be changed to other content ratings or be removed from every item in your library. | +| [Metadata Backup](#metadata-backup) | Creates/Maintains a PMM [Metadata File](../metadata/metadata) with a full `metadata` mapping based on the library's items locked attributes. | ## Assets For All @@ -179,7 +181,7 @@ Updates every item's audience/critic/user rating in the library to the chosen si | `remove` | Remove Rating and Lock Field | | `reset` | Remove Rating and Unlock Field | -## Mass Episode * Rating Update +## Mass Episode * Rating Update Updates every item's episode's audience/critic/user rating in the library to the chosen site's rating. @@ -196,6 +198,36 @@ Updates every item's episode's audience/critic/user rating in the library to the | `remove` | Remove Rating and Lock Field | | `reset` | Remove Rating and Unlock Field | +## Mass Poster Update + +Updates every item's poster to the chosen sites poster. Will fallback to `plex` if the given option fails. Assets will be used over anything else. + +**Attribute:** `mass_poster_update` + +**Values:** + +| Value | Description | +|:---------|:----------------| +| `tmdb` | Use TMDb Poster | +| `plex` | Use Plex Poster | +| `lock` | Lock Poster | +| `unlock` | Unlock Poster | + +## Mass Background Update + +Updates every item's background to the chosen sites background. Will fallback to `plex` if the given option fails. Assets will be used over anything else. + +**Attribute:** `mass_background_update` + +**Values:** + +| Value | Description | +|:---------|:--------------------| +| `tmdb` | Use TMDb Background | +| `plex` | Use Plex Background | +| `lock` | Lock Background | +| `unlock` | Unlock Background | + ## Mass IMDb Parental Labels Updates every item's labels in the library to match the IMDb Parental Guide diff --git a/modules/config.py b/modules/config.py index 1d041e99..4b830ae3 100644 --- a/modules/config.py +++ b/modules/config.py @@ -54,6 +54,9 @@ mass_available_options = { "tmdb": "Use TMDb Release", "omdb": "Use IMDb Release through OMDb", "mdb": "Use MdbList Release", "tvdb": "Use TVDb Release", "anidb": "Use AniDB Release", "mal": "Use MyAnimeList Release" } +mass_image_options = { + "plex": "Use Plex Images", "tmdb": "Use TMDb Images" +} mass_episode_rating_options = { "lock": "Unlock Rating", "unlock": "Unlock Rating", "remove": "Remove and Lock Rating", "reset": "Remove and Unlock Rating", "tmdb": "Use TMDb Rating", "imdb": "Use IMDb Rating" @@ -658,6 +661,8 @@ class ConfigFile: "mass_episode_audience_rating_update": None, "mass_episode_critic_rating_update": None, "mass_episode_user_rating_update": None, + "mass_poster_update": None, + "mass_background_update": None, } display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"] @@ -742,6 +747,10 @@ class ConfigFile: params["mass_originally_available_update"] = check_for_attribute(lib["operations"], "mass_originally_available_update", test_list=mass_available_options, default_is_none=True, save=False) if "mass_imdb_parental_labels" in lib["operations"]: params["mass_imdb_parental_labels"] = check_for_attribute(lib["operations"], "mass_imdb_parental_labels", test_list=imdb_label_options, default_is_none=True, save=False) + if "mass_poster_update" in lib["operations"]: + params["mass_poster_update"] = check_for_attribute(lib["operations"], "mass_poster_update", test_list=mass_image_options, default_is_none=True, save=False) + if "mass_background_update" in lib["operations"]: + params["mass_background_update"] = check_for_attribute(lib["operations"], "mass_background_update", test_list=mass_image_options, default_is_none=True, save=False) if "mass_trakt_rating_update" in lib["operations"]: params["mass_trakt_rating_update"] = check_for_attribute(lib["operations"], "mass_trakt_rating_update", var_type="bool", default=False, save=False, do_print=False) if "split_duplicates" in lib["operations"]: diff --git a/modules/library.py b/modules/library.py index 987684d1..527ca577 100644 --- a/modules/library.py +++ b/modules/library.py @@ -86,6 +86,8 @@ class Library(ABC): self.mass_original_title_update = params["mass_original_title_update"] self.mass_originally_available_update = params["mass_originally_available_update"] self.mass_imdb_parental_labels = params["mass_imdb_parental_labels"] + self.mass_poster_update = params["mass_poster_update"] + self.mass_background_update = params["mass_background_update"] self.radarr_add_all_existing = params["radarr_add_all_existing"] self.radarr_remove_by_tag = params["radarr_remove_by_tag"] self.sonarr_add_all_existing = params["sonarr_add_all_existing"] @@ -113,7 +115,7 @@ class Library(ABC): or self.mass_episode_audience_rating_update or self.mass_episode_critic_rating_update or self.mass_episode_user_rating_update \ or self.mass_content_rating_update or self.mass_originally_available_update or self.mass_original_title_update\ or self.mass_imdb_parental_labels or self.genre_mapper or self.content_rating_mapper \ - or self.radarr_add_all_existing or self.sonarr_add_all_existing else False + or self.radarr_add_all_existing or self.sonarr_add_all_existing or self.mass_poster_update or self.mass_background_update else False self.library_operation = True if self.items_library_operation or self.delete_unmanaged_collections or self.delete_collections_with_less \ or self.radarr_remove_by_tag or self.sonarr_remove_by_tag or self.mass_collection_mode \ or self.show_unmanaged or self.metadata_backup or self.update_blank_track_titles else False diff --git a/modules/operations.py b/modules/operations.py index dba5b4d6..c877d2b9 100644 --- a/modules/operations.py +++ b/modules/operations.py @@ -8,7 +8,8 @@ logger = util.logger meta_operations = [ "mass_audience_rating_update", "mass_user_rating_update", "mass_critic_rating_update", "mass_episode_audience_rating_update", "mass_episode_user_rating_update", "mass_episode_critic_rating_update", - "mass_genre_update", "mass_content_rating_update", "mass_originally_available_update", "mass_original_title_update" + "mass_genre_update", "mass_content_rating_update", "mass_originally_available_update", "mass_original_title_update", + "mass_poster_update", "mass_background_update" ] class Operations: @@ -36,6 +37,8 @@ class Operations: logger.debug(f"Mass Original Title Update: {self.library.mass_original_title_update}") logger.debug(f"Mass Originally Available Update: {self.library.mass_originally_available_update}") logger.debug(f"Mass IMDb Parental Labels: {self.library.mass_imdb_parental_labels}") + logger.debug(f"Mass Poster Update: {self.library.mass_poster_update}") + logger.debug(f"Mass Background Update: {self.library.mass_background_update}") logger.debug(f"Mass Collection Mode Update: {self.library.mass_collection_mode}") logger.debug(f"Split Duplicates: {self.library.split_duplicates}") logger.debug(f"Radarr Add All Existing: {self.library.radarr_add_all_existing}") @@ -444,6 +447,63 @@ class Operations: item.saveEdits() logger.info(f"Batch Edits{batch_display}") + if self.library.mass_poster_update or self.library.mass_background_update: + try: + new_poster, new_background, _, _ = self.library.find_item_assets(item) + except Failed: + new_poster = None + new_background = None + if self.library.mass_poster_update: + if self.library.mass_poster_update == "lock": + self.library.query(item.lockPoster) + logger.infd(f"Poster | Locked") + elif self.library.mass_poster_update == "unlock": + self.library.query(item.unlockPoster) + logger.infd(f"Poster | Unlocked") + else: + poster_location = "the Assets Directory" if new_poster else "" + poster_url = False if new_poster else True + new_poster = new_poster.location if new_poster else None + if not new_poster: + if self.library.mass_poster_update == "tmdb" and tmdb_item: + new_poster = tmdb_item.poster_url + poster_location = "TMDb" + if not new_poster: + poster = next((p for p in item.posters() if p.provider == "local"), None) + if poster: + new_poster = f"{self.library.url}{poster.key}&X-Plex-Token={self.library.token}" + poster_location = "Plex" + if new_poster: + self.library.upload_poster(item, new_poster, url=poster_url) + logger.infd(f"Poster | Reset from {poster_location}") + else: + logger.infd(f"Poster | No Reset Image Found") + if self.library.mass_background_update: + if self.library.mass_background_update == "lock": + self.library.query(item.lockArt) + logger.infd(f"Background | Locked") + elif self.library.mass_background_update == "unlock": + self.library.query(item.unlockArt) + logger.infd(f"Background | Unlocked") + else: + background_location = "the Assets Directory" if new_background else "" + background_url = False if new_background else True + new_background = new_background.location if new_background else None + if not new_background: + if self.library.mass_background_update == "tmdb" and tmdb_item: + new_background = tmdb_item.backdrop_url + background_location = "TMDb" + if not new_background: + background = next((p for p in item.arts() if p.provider == "local"), None) + if background: + new_background = f"{self.library.url}{background.key}&X-Plex-Token={self.library.token}" + background_location = "Plex" + if new_background: + self.library.upload_background(item, new_background, url=background_url) + logger.infd(f"Background | Reset from {background_location}") + else: + logger.infd(f"Background | No Reset Image Found") + episode_ops = [self.library.mass_episode_audience_rating_update, self.library.mass_episode_critic_rating_update, self.library.mass_episode_user_rating_update] if any([x is not None for x in episode_ops]): diff --git a/modules/plex.py b/modules/plex.py index 8e6b8f6b..af7dbe2f 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -631,6 +631,13 @@ class Plex(Library): else: item.uploadPoster(filepath=image) + @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) + def upload_background(self, item, image, url=False): + if url: + item.uploadArt(url=image) + else: + item.uploadArt(filepath=image) + @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_actor_id(self, name): results = self.Plex.hubSearch(name)