From 188063cb198bd73df57e8c6221347ccc77c1ac54 Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Thu, 9 May 2024 09:04:04 -0400 Subject: [PATCH] [3] Added the `mass_added_at_update` operation --- CHANGELOG | 1 + VERSION | 2 +- docs/config/operations.md | 39 +++++++++ json-schema/config-schema.json | 4 + modules/config.py | 6 +- modules/library.py | 3 +- modules/operations.py | 154 +++++++++++++++++++-------------- 7 files changed, 138 insertions(+), 71 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 042ff9d4..81fa9c4b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ # New Features Checks requirement versions to print a message if one needs to be updated +Added the `mass_added_at_update` operation to mass set the Added At field of Movies and Shows. # Updates Changed the `overlay_artwork_filetype` Setting to accept `webp_lossy` and `webp_lossless` while the old attribute `webp` will be treated as `webp_lossy`. diff --git a/VERSION b/VERSION index ee2103b2..9ac22ce5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.1-develop2 +2.0.1-develop3 diff --git a/docs/config/operations.md b/docs/config/operations.md index 52a8470d..c5917022 100644 --- a/docs/config/operations.md +++ b/docs/config/operations.md @@ -302,6 +302,45 @@ You can create individual blocks of operations by using a list under `operations - 1900-01-01 ``` +###### Mass Added At Update + +??? blank "`mass_added_at_update` - Updates the added at date of every item in the library." + +
Updates every item's added at date in the library to the chosen site's date. + +
+ + **Attribute:** `mass_added_at_update` + + **Accepted Values:** Source or List of sources to use in that order + + + + + + + + + + + + + + +
`tmdb`Use TMDb Release Date
`tvdb`Use TVDb Release Date
`omdb`Use IMDb Release Date through OMDb
`mdb`Use MDBList Release Date
`mdb_digital`Use MDBList Digital Release Date
`anidb`Use AniDB Release Date
`mal`Use MyAnimeList Release Date
`lock`Lock Added At Field
`unlock`Unlock Added At Field
`remove`Remove Added At and Lock Field
`reset`Remove Added At and Unlock Field
Any String in the Format: YYYY-MM-DD for Added At (2022-05-28)
+ + ???+ example "Example" + + ```yaml + libraries: + TV Shows: + operations: + mass_added_at_update: + - mdb_digital + - mdb + - 1900-01-01 + ``` + ###### Mass Rating Update ??? blank "`mass_***_rating_update` - Updates the audience/critic/user rating of every item in the library." diff --git a/json-schema/config-schema.json b/json-schema/config-schema.json index af900945..a86b9b7e 100644 --- a/json-schema/config-schema.json +++ b/json-schema/config-schema.json @@ -1429,6 +1429,10 @@ "type": "string", "enum": ["tmdb","tvdb","omdb","mdb","anidb","mal","lock","unlock","remove","reset"] }, + "mass_added_at_update": { + "type": "string", + "enum": ["tmdb","tvdb","omdb","mdb","anidb","mal","lock","unlock","remove","reset"] + }, "mass_audience_rating_update": { "anyOf": [ { diff --git a/modules/config.py b/modules/config.py index 7d464110..17122730 100644 --- a/modules/config.py +++ b/modules/config.py @@ -135,8 +135,8 @@ library_operations = { "mass_audience_rating_update": mass_rating_options, "mass_episode_audience_rating_update": mass_episode_rating_options, "mass_critic_rating_update": mass_rating_options, "mass_episode_critic_rating_update": mass_episode_rating_options, "mass_user_rating_update": mass_rating_options, "mass_episode_user_rating_update": mass_episode_rating_options, - "mass_original_title_update": mass_original_title_options, "mass_originally_available_update": mass_available_options, - "mass_imdb_parental_labels": imdb_label_options, + "mass_original_title_update": mass_original_title_options, "mass_imdb_parental_labels": imdb_label_options, + "mass_originally_available_update": mass_available_options, "mass_added_at_update": mass_available_options, "mass_collection_mode": "mass_collection_mode", "mass_poster_update": "dict", "mass_background_update": "dict", "metadata_backup": "dict", "delete_collections": "dict", "genre_mapper": "dict", "content_rating_mapper": "dict", } @@ -917,7 +917,7 @@ class ConfigFile: final_list.append(str(list_attr)) elif op == "mass_genre_update": final_list.append(list_attr if isinstance(list_attr, list) else [list_attr]) - elif op == "mass_originally_available_update": + elif op in ["mass_originally_available_update", "mass_added_at_update"]: final_list.append(util.validate_date(list_attr)) elif op.endswith("rating_update"): final_list.append(util.check_int(list_attr, datatype="float", minimum=0, maximum=10, throw=True)) diff --git a/modules/library.py b/modules/library.py index 1030849d..a11818fc 100644 --- a/modules/library.py +++ b/modules/library.py @@ -97,6 +97,7 @@ class Library(ABC): self.mass_content_rating_update = params["mass_content_rating_update"] self.mass_original_title_update = params["mass_original_title_update"] self.mass_originally_available_update = params["mass_originally_available_update"] + self.mass_added_at_update = params["mass_added_at_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"] @@ -124,7 +125,7 @@ class Library(ABC): self.items_library_operation = True if self.assets_for_all or self.mass_genre_update or self.remove_title_parentheses \ or self.mass_audience_rating_update or self.mass_critic_rating_update or self.mass_user_rating_update \ 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_content_rating_update or self.mass_originally_available_update or self.mass_added_at_update or self.mass_original_title_update\ or self.mass_imdb_parental_labels or self.genre_mapper or self.content_rating_mapper or self.mass_studio_update\ 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_collections or self.mass_collection_mode \ diff --git a/modules/operations.py b/modules/operations.py index 978bfada..62ff3956 100644 --- a/modules/operations.py +++ b/modules/operations.py @@ -1,5 +1,5 @@ import os, re -from datetime import datetime +from datetime import datetime, timedelta, timezone from modules import plex, util, anidb from modules.util import Failed, LimitReached, YAML from plexapi.exceptions import NotFound @@ -10,14 +10,15 @@ 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_poster_update", "mass_background_update", "mass_studio_update" + "mass_genre_update", "mass_content_rating_update", "mass_originally_available_update", "mass_added_at_update", + "mass_original_title_update", "mass_poster_update", "mass_background_update", "mass_studio_update" ] name_display = { "audienceRating": "Audience Rating", "rating": "Critic Rating", "userRating": "User Rating", "originallyAvailableAt": "Originally Available Date", + "addedAt": "Added At Date", "contentRating": "Content Rating" } @@ -45,6 +46,7 @@ class Operations: logger.debug(f"Mass Content Rating Update: {self.library.mass_content_rating_update}") 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 Added At Update: {self.library.mass_added_at_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}") @@ -88,7 +90,7 @@ class Operations: genre_edits = {"add": {}, "remove": {}} content_edits = {} studio_edits = {} - available_edits = {} + date_edits = {"originallyAvailableAt": {}, "addedAt": {}} remove_edits = {} reset_edits = {} lock_edits = {} @@ -664,65 +666,69 @@ class Operations: except Failed: continue - if self.library.mass_originally_available_update: - current_available = item.originallyAvailableAt - if current_available: - current_available = current_available.strftime("%Y-%m-%d") - for option in self.library.mass_originally_available_update: - if option in ["lock", "remove"]: - if option == "remove" and current_available: - if "originallyAvailableAt" not in remove_edits: - remove_edits["originallyAvailableAt"] = [] - remove_edits["originallyAvailableAt"].append(item.ratingKey) - item_edits += "\nRemove Originally Available Date (Batched)" - elif "originallyAvailableAt" not in locked_fields: - if "originallyAvailableAt" not in lock_edits: - lock_edits["originallyAvailableAt"] = [] - lock_edits["originallyAvailableAt"].append(item.ratingKey) - item_edits += "\nLock Originally Available Date (Batched)" - break - elif option in ["unlock", "reset"]: - if option == "reset" and current_available: - if "originallyAvailableAt" not in reset_edits: - reset_edits["originallyAvailableAt"] = [] - reset_edits["originallyAvailableAt"].append(item.ratingKey) - item_edits += "\nReset Originally Available Date (Batched)" - elif "originallyAvailableAt" in locked_fields: - if "originallyAvailableAt" not in unlock_edits: - unlock_edits["originallyAvailableAt"] = [] - unlock_edits["originallyAvailableAt"].append(item.ratingKey) - item_edits += "\nUnlock Originally Available Date (Batched)" - break - else: - try: - if option == "tmdb": - new_available = tmdb_obj().release_date if self.library.is_movie else tmdb_obj().first_air_date # noqa - elif option == "omdb": - new_available = omdb_obj().released # noqa - elif option == "tvdb": - new_available = tvdb_obj().release_date # noqa - elif option == "mdb": - new_available = mdb_obj().released # noqa - elif option == "mdb_digital": - new_available = mdb_obj().released_digital # noqa - elif option == "anidb": - new_available = anidb_obj().released # noqa - elif option == "mal": - new_available = mal_obj().aired # noqa - else: - new_available = option - if not new_available: - logger.info(f"No {option} Originally Available Date Found") - raise Failed - new_available = new_available.strftime("%Y-%m-%d") - if current_available != new_available: - if new_available not in available_edits: - available_edits[new_available] = [] - available_edits[new_available].append(item.ratingKey) - item_edits += f"\nUpdate Originally Available Date (Batched) | {new_available}" + for attribute, item_attr in [ + (self.library.mass_originally_available_update, "originallyAvailableAt"), + (self.library.mass_added_at_update, "addedAt") + ]: + if attribute: + current = getattr(item, item_attr) + if current: + current = current.strftime("%Y-%m-%d") + for option in attribute: + if option in ["lock", "remove"]: + if option == "remove" and current: + if item_attr not in remove_edits: + remove_edits[item_attr] = [] + remove_edits[item_attr].append(item.ratingKey) + item_edits += f"\nRemove {name_display[item_attr]} (Batched)" + elif item_attr not in locked_fields: + if item_attr not in lock_edits: + lock_edits[item_attr] = [] + lock_edits[item_attr].append(item.ratingKey) + item_edits += f"\nLock {name_display[item_attr]} (Batched)" break - except Failed: - continue + elif option in ["unlock", "reset"]: + if option == "reset" and current: + if item_attr not in reset_edits: + reset_edits[item_attr] = [] + reset_edits[item_attr].append(item.ratingKey) + item_edits += f"\nReset {name_display[item_attr]} (Batched)" + elif item_attr in locked_fields: + if item_attr not in unlock_edits: + unlock_edits[item_attr] = [] + unlock_edits[item_attr].append(item.ratingKey) + item_edits += f"\nUnlock {name_display[item_attr]} (Batched)" + break + else: + try: + if option == "tmdb": + new_date = tmdb_obj().release_date if self.library.is_movie else tmdb_obj().first_air_date # noqa + elif option == "omdb": + new_date = omdb_obj().released # noqa + elif option == "tvdb": + new_date = tvdb_obj().release_date # noqa + elif option == "mdb": + new_date = mdb_obj().released # noqa + elif option == "mdb_digital": + new_date = mdb_obj().released_digital # noqa + elif option == "anidb": + new_date = anidb_obj().released # noqa + elif option == "mal": + new_date = mal_obj().aired # noqa + else: + new_date = option + if not new_date: + logger.info(f"No {option} {name_display[item_attr]} Found") + raise Failed + new_date = new_date.strftime("%Y-%m-%d") + if current != new_date: + if new_date not in date_edits[item_attr]: + date_edits[item_attr][new_date] = [] + date_edits[item_attr][new_date].append(item.ratingKey) + item_edits += f"\nUpdate {name_display[item_attr]} (Batched) | {new_date}" + break + except Failed: + continue if len(item_edits) > 0: logger.info(f"Item Edits{item_edits}") @@ -920,11 +926,27 @@ class Operations: self.library.Plex.editStudio(new_studio) self.library.Plex.saveMultiEdits() - _size = len(available_edits.items()) - for i, (new_available, rating_keys) in enumerate(sorted(available_edits.items()), 1): - logger.info(get_batch_info(i, _size, "originallyAvailableAt", len(rating_keys), display_value=new_available)) + _size = len(date_edits["originallyAvailableAt"].items()) + for i, (new_date, rating_keys) in enumerate(sorted(date_edits["originallyAvailableAt"].items()), 1): + logger.info(get_batch_info(i, _size, "originallyAvailableAt", len(rating_keys), display_value=new_date)) self.library.Plex.batchMultiEdits(self.library.load_list_from_cache(rating_keys)) - self.library.Plex.editOriginallyAvailable(new_available) + self.library.Plex.editOriginallyAvailable(new_date) + self.library.Plex.saveMultiEdits() + + epoch = datetime(1970, 1, 1) + _size = len(date_edits["addedAt"].items()) + for i, (new_date, rating_keys) in enumerate(sorted(date_edits["addedAt"].items()), 1): + logger.info(get_batch_info(i, _size, "addedAt", len(rating_keys), display_value=new_date)) + self.library.Plex.batchMultiEdits(self.library.load_list_from_cache(rating_keys)) + new_date = datetime.strptime(new_date, "%Y-%m-%d") + logger.trace(new_date) + try: + ts = int(round(new_date.timestamp())) + except (TypeError, OSError): + offset = int(datetime(2000, 1, 1, tzinfo=timezone.utc).timestamp() - datetime(2000, 1, 1).timestamp()) + ts = int((new_date - epoch).total_seconds()) - offset + logger.trace(epoch + timedelta(seconds=ts)) + self.library.Plex.editAddedAt(ts) self.library.Plex.saveMultiEdits() _size = len(remove_edits.items())