diff --git a/CHANGELOG b/CHANGELOG index 54c61427..483be2bc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,4 +14,5 @@ Fixes an issue where Prime Video overlays/collections would not be built when th Fixed the `cast` search option for the `imdb_search` builder Fixes #2258 `imdb_list` sort was not being parsed correctly Fixes `letterboxd_list` rating filter to use a 1-10 rating vs 1-100 to reflect how letterboxd ratings work on their website +Fixes #2274 Enhance handling of smart collections in deletion Fixed the `ids_to_anidb` lookup for anime movies and shows diff --git a/VERSION b/VERSION index 10465647..e58687f3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0-build8 +2.1.0-build9 diff --git a/docs/config/operations.md b/docs/config/operations.md index ed06c5f5..e30f4214 100644 --- a/docs/config/operations.md +++ b/docs/config/operations.md @@ -83,6 +83,8 @@ You can create individual blocks of operations by using a list under `operations `configured: true`Collection must be Configured to be deleted
(collection is in the config file of the specific Kometa run) `configured: false`Collection must be Unconfigured to be deleted
(collection is not in the config file of the specific Kometa run) `less: ###`Collection must contain less than the given number of items to be deleted.
### is a Number greater than 0
Optional value which if undefined means collections will be deleted regardless of how many items they have + `ignore_empty_smart_collections: false`Do not apply less check to empty smart collections
This allows retaining things like smart collections that show movies without subtitles or the like. + **The collection does not need to be scheduled to be considered configured and only needs to be in the config file.** diff --git a/json-schema/config-schema.json b/json-schema/config-schema.json index 77d07edc..3cfd1905 100644 --- a/json-schema/config-schema.json +++ b/json-schema/config-schema.json @@ -2080,7 +2080,8 @@ "properties": { "configured": { "type": "boolean" }, "managed": { "type": "boolean" }, - "less": { "type": "integer" } + "less": { "type": "integer" }, + "ignore_empty_smart_collections": { "type": "boolean" } }, "required": [] }, diff --git a/json-schema/kitchen_sink_config.yml b/json-schema/kitchen_sink_config.yml index 4a826bbf..71941947 100644 --- a/json-schema/kitchen_sink_config.yml +++ b/json-schema/kitchen_sink_config.yml @@ -344,7 +344,8 @@ libraries: delete_collections: configured: false # False - Collection must be an Unconfigured Collection to be deleted (collection is not in the config file of the specific Kometa run). managed: false # False - Collection must be an Unmanaged Collection to be deleted (the collection does not have the Kometa label) - less: 99999 # Effectively all collections regardless of teh number of items in the collection + less: 99999 # Effectively all collections regardless of the number of items in the collection + ignore_empty_smart_collections: true # Don't do the less check on empty smart collections mass_user_rating_update: mdb_tomatoes # Update user ratings with mdb_tomatoes mass_critic_rating_update: imdb # Update critic ratings with imdb mass_audience_rating_update: tmdb # Update audience ratings with tmdb @@ -758,7 +759,8 @@ libraries: delete_collections: configured: false # False - Collection must be an Unconfigured Collection to be deleted (collection is not in the config file of the specific Kometa run). managed: false # False - Collection must be an Unmanaged Collection to be deleted (the collection does not have the Kometa label) - less: 99999 # Effectively all collections regardless of teh number of items in the collection + less: 99999 # Effectively all collections regardless of the number of items in the collection + ignore_empty_smart_collections: true # Don't do the less check on empty smart collections mass_user_rating_update: mdb_tomatoes # Update user ratings with mdb_tomatoes mass_critic_rating_update: imdb # Update critic ratings with imdb mass_audience_rating_update: tmdb # Update audience ratings with tmdb @@ -859,8 +861,8 @@ webhooks: # Can be individually specif version: (redacted) delete: (redacted) plex: # Can be individually specified per library as well; REQUIRED for the script to run - url: (redacted) - token: (redacted) + url: http://10.10.10.10:1234 + token: THIS_IS_A_FAKE_TOKEN timeout: 60 clean_bundles: true empty_trash: true @@ -873,19 +875,19 @@ tmdb: # REQUIRED for the script to region: CA # Upper case ISO 3166-1 Code cache_expiration: 60 tautulli: # Can be individually specified per library as well - url: (redacted) - apikey: (redacted) + url: http://10.10.10.10:1234 + apikey: THIS_IS_A_FAKE_TOKEN omdb: - apikey: (redacted) + apikey: THIS_IS_A_FAKE_TOKEN cache_expiration: 60 mdblist: - apikey: (redacted) + apikey: THIS_IS_A_FAKE_TOKEN cache_expiration: 60 notifiarr: - apikey: (redacted) + apikey: THIS_IS_A_FAKE_TOKEN radarr: # Can be individually specified per library as well - url: (redacted) - token: (redacted) + url: http://10.10.10.10:1234 + token: THIS_IS_A_FAKE_TOKEN root_folder_path: /data/media/movies monitor: true availability: announced @@ -900,8 +902,8 @@ radarr: # Can be individually specified ignore_cache: false monitor_existing: false sonarr: # Can be individually specified per library as well - url: (redacted) - token: (redacted) + url: http://10.10.10.10:1234 + token: THIS_IS_A_FAKE_TOKEN root_folder_path: /data/media/tv monitor: all quality_profile: Any @@ -920,7 +922,7 @@ sonarr: # Can be individually specified monitor_existing: false anidb: client: (redacted) - version: (redacted) + version: 1234 language: en cache_expiration: 60 username: (redacted) diff --git a/json-schema/prototype_config.yml b/json-schema/prototype_config.yml index 015770a4..91b297c1 100644 --- a/json-schema/prototype_config.yml +++ b/json-schema/prototype_config.yml @@ -388,6 +388,7 @@ libraries: managed: false configured: true less: 123 + ignore_empty_smart_collections: true Anime: collection_files: diff --git a/modules/config.py b/modules/config.py index 09dac1cd..9289f810 100644 --- a/modules/config.py +++ b/modules/config.py @@ -964,6 +964,7 @@ class ConfigFile: "managed": check_for_attribute(input_dict, "managed", var_type="bool", default_is_none=True, save=False), "configured": check_for_attribute(input_dict, "configured", var_type="bool", default_is_none=True, save=False), "less": check_for_attribute(input_dict, "less", var_type="int", default_is_none=True, save=False, int_min=1), + "ignore_empty_smart_collections": check_for_attribute(input_dict, "ignore_empty_smart_collections", var_type="bool", default=True, save=False), } elif op == "mass_collection_content_rating_update": section_final[op] = { diff --git a/modules/operations.py b/modules/operations.py index a35a98b5..1a681cfc 100644 --- a/modules/operations.py +++ b/modules/operations.py @@ -64,14 +64,19 @@ class Operations: logger.debug(f"Item Operation: {self.library.items_library_operation}") logger.debug("") - def should_be_deleted(col_in, labels_in, configured_in, managed_in, less_in): + def should_be_deleted(col_in, labels_in, configured_in, managed_in, less_in, ignore_smart_in): if all((x is None for x in [configured_in, managed_in, less_in])): return False - less_check = True + less_check = not ignore_smart_in if col_in.smart else True if less_in is not None: - less_check = col_in.childCount < less_in - logger.trace(f"{col_in.title} - collection size: {col_in.childCount} < less: {less_in}, DELETE: {less_check}") + if less_check: + col_count = col_in.childCount if col_in.childCount is not None else 0 + less_check = col_count < less_in + logger.trace(f"{col_in.title} - collection size: {col_count} < less: {less_in}, DELETE: {less_check}") + else: + logger.trace(f"{col_in.title} - skipping size check: smart - {col_in.smart}, ignore_smart - {ignore_smart_in}") + managed_check = True if managed_in is not None: @@ -1071,6 +1076,7 @@ class Operations: less = self.library.delete_collections["less"] if self.library.delete_collections and self.library.delete_collections["less"] is not None else None managed = self.library.delete_collections["managed"] if self.library.delete_collections else None configured = self.library.delete_collections["configured"] if self.library.delete_collections else None + ignore_smart = self.library.delete_collections["ignore_empty_smart_collections"] if self.library.delete_collections else True unmanaged_collections = [] unconfigured_collections = [] all_collections = self.library.get_all_collections() @@ -1079,7 +1085,7 @@ class Operations: col = self.library.reload(col, force=True) labels = [la.tag for la in self.library.item_labels(col)] - if should_be_deleted(col, labels, configured, managed, less): + if should_be_deleted(col, labels, configured, managed, less, ignore_smart): try: self.library.delete(col) logger.info(f"{col.title} Deleted")