diff --git a/VERSION b/VERSION index 7b5ee1a9..b4547d80 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.17.1-develop35 +1.17.1-develop36 diff --git a/docs/metadata/dynamic.md b/docs/metadata/dynamic.md index 0738b027..52755966 100644 --- a/docs/metadata/dynamic.md +++ b/docs/metadata/dynamic.md @@ -129,7 +129,8 @@ Depending on the `type` of dynamic collection, `data` is used to specify the opt | [`studio`](#studio) | Create a collection for each studio found in the library | ❌ | ✅ | ✅ | ❌ | ❌ | | [`network`](#network) | Create a collection for each network found in the library | ❌ | ❌ | ✅ | ❌ | ❌ | | [`mood`](#mood) | Create a collection for each mood found in the library | ❌ | ❌ | ❌ | ✅ | ❌ | -| [`style`](#style) | Create a collection for each style found in the library | ❌ | ❌ | ❌ | ✅ | ❌ | +| [`style`](#style) | Create a collection for each artist style found in the library | ❌ | ❌ | ❌ | ✅ | ❌ | +| [`album_style`](#album-style) | Create a collection for each album style found in the library | ❌ | ❌ | ❌ | ✅ | ❌ | | [`number`](#number) | Creates a collection for each number defined | ✅ | ✅ | ✅ | ✅ | ✅ | | [`custom`](#custom) | Creates a collection for each custom `key: key_name` pair defined. | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -1503,7 +1504,7 @@ dynamic_collections: ### Style -Create a collection for each style found in the library. +Create a collection for each artist style found in the library. @@ -1545,21 +1546,83 @@ default_template: #### Example: +* Create a collection for the top 10 artists for each style found in the Music library +* Name the collection "Top [Style] Artists" + +```yaml +templates: + style collection: + smart_filter: + limit: 10 + sort_by: plays.desc + all: + artist_style: <> +dynamic_collections: + Styles: # mapping name does not matter just needs to be unique + type: style + title_format: Top <> <> + template: style collection +``` + +### Album Style + +Create a collection for each album style found in the library. + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
type Optionalbum_style
data ValueNot Used
KeysStyle
Key NamesStyle
Default title_formatMost Played <<key_name>> Albums
Default Template + +```yaml +default_template: + smart_filter: + limit: 50 + sort_by: plays.desc + any: + album_style: <> +``` + +
+ +#### Example: + * Create a collection for the top 10 albums for each style found in the Music library * Name the collection "Top [Style] Albums" ```yaml templates: style collection: + collection_level: albums smart_filter: limit: 10 sort_by: plays.desc - type: albums all: album_style: <> dynamic_collections: Styles: # mapping name does not matter just needs to be unique - type: style + type: album_style title_format: Top <> Albums template: style collection ``` diff --git a/docs/metadata/templates.md b/docs/metadata/templates.md index 66e9f796..6c25441c 100644 --- a/docs/metadata/templates.md +++ b/docs/metadata/templates.md @@ -112,8 +112,10 @@ There are some attributes unique to `templates`; `default`, `optional`, `conditi Every template call is given these template variables. * Either `<>`, `<>`, or `<>` which is the name of the definition. +* `<>` is the original mapping name for the definition in the YAML file. * Either `<>` or `<>` which is the name of the definition after `move_prefix` is applied. * `<>` which is the library type +* `<>` which is the name of the library * All Template Variables can append `_encoded` to the variable name to use a URL encode version of the variable. ex. `<>` ### Conditionals diff --git a/modules/builder.py b/modules/builder.py index cac97486..409f305e 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -200,7 +200,7 @@ class CollectionBuilder: logger.info(extra) logger.info("") - logger.separator(f"Validating {self.mapping_name} Attributes", space=False, border=False) + logger.separator(f"Building Definition From Templates", space=False, border=False) if f"{self.type}_name" in methods: logger.warning(f"Config Warning: Running {self.type}_name as name") @@ -217,6 +217,8 @@ class CollectionBuilder: self.data[attr] = new_attributes[attr] methods[attr.lower()] = attr + logger.separator(f"Validating {self.mapping_name} Attributes", space=False, border=False) + if "name" in methods: logger.debug("") logger.debug("Validating Method: name") diff --git a/modules/meta.py b/modules/meta.py index 8e9ce15f..0d9a43f2 100644 --- a/modules/meta.py +++ b/modules/meta.py @@ -21,7 +21,10 @@ dynamic_attributes = [ "type", "data", "exclude", "addons", "template", "template_variables", "other_template", "remove_suffix", "remove_prefix", "title_format", "key_name_override", "title_override", "test", "sync", "include", "other_name" ] -auto_type_translation = {"content_rating": "contentRating", "subtitle_language": "subtitleLanguage", "audio_language": "audioLanguage"} +auto_type_translation = { + "content_rating": "contentRating", "subtitle_language": "subtitleLanguage", "audio_language": "audioLanguage", + "album_style": "album.style" +} default_templates = { "original_language": {"plex_all": True, "filters": {"original_language": "<>"}}, "origin_country": {"plex_all": True, "filters": {"origin_country": "<>"}}, @@ -211,10 +214,14 @@ class DataFile: if not isinstance(template["conditionals"], dict): raise Failed(f"{self.data_type} Error: template sub-attribute conditionals is not a dictionary") for con_key, con_value in template["conditionals"].items(): + logger.debug("") + logger.debug(f"Conditional: {con_key}") if not isinstance(con_value, dict): raise Failed(f"{self.data_type} Error: template sub-attribute conditionals is not a dictionary") - con_key = small_var_check(con_key) - if con_key in variables: + final_key = small_var_check(con_key) + if final_key != con_key: + logger.debug(f"Conditional Variable: {final_key}") + if final_key in variables: continue if "conditions" not in con_value: raise Failed(f"{self.data_type} Error: conditions sub-attribute required for conditionals") @@ -238,25 +245,34 @@ class DataFile: if var_key in variables: if (isinstance(var_value, list) and variables[var_key] not in var_value) or \ (not isinstance(var_value, list) and variables[var_key] != var_value): + logger.debug(f"Condition Failed: {variables[var_key]} {'not in' if isinstance(var_value, list) else '!='} {var_value}") condition_passed = False break elif var_key in default: if (isinstance(var_value, list) and default[var_key] not in var_value) or \ (not isinstance(var_value, list) and default[var_key] != var_value): + logger.debug(f"Condition Failed: {default[var_key]} {'not in' if isinstance(var_value, list) else '!='} {var_value}") condition_passed = False break + else: + logger.debug(f"Condition Failed: {var_key} is not a variable provided or a default") + condition_passed = False + break if condition_passed: + logger.debug(f"Conditional Variable: {final_key} = {condition['value']}") condition_found = True - variables[con_key] = condition["value"] - variables[f"{con_key}_encoded"] = requests.utils.quote(str(condition["value"])) + variables[final_key] = condition["value"] + variables[f"{final_key}_encoded"] = requests.utils.quote(str(condition["value"])) break if not condition_found: if "default" in con_value: - variables[con_key] = con_value["default"] - variables[f"{con_key}_encoded"] = requests.utils.quote(str(con_value["default"])) + logger.debug(f"Conditional Variable: {final_key} = {con_value['default']}") + variables[final_key] = con_value["default"] + variables[f"{final_key}_encoded"] = requests.utils.quote(str(con_value["default"])) else: - optional.append(str(con_key)) - optional.append(f"{con_key}_encoded") + logger.debug(f"Conditional Variable: {final_key} added as optional variable") + optional.append(str(final_key)) + optional.append(f"{final_key}_encoded") sort_name = None if "move_prefix" in template or "move_collection_prefix" in template: @@ -275,6 +291,13 @@ class DataFile: raise Failed(f"{self.data_type} Error: template sub-attribute move_prefix is blank") variables[f"{self.data_type.lower()}_sort"] = sort_name if sort_name else variables[name_var] + logger.debug("") + logger.debug(f"Variables: {variables}") + logger.debug("") + logger.debug(f"Defaults: {default}") + logger.debug("") + logger.debug(f"Optional: {optional}") + def check_for_var(_method, _data): def scan_text(og_txt, var, actual_value): if og_txt is None: @@ -434,7 +457,7 @@ class MetadataFile(DataFile): auto_list = {str(k): f"{k}s" for k in addons if str(k) not in exclude and f"{k}s" not in exclude} default_template = {"smart_filter": {"limit": 50, "sort_by": "critic_rating.desc", "any": {"year": f"<>"}}} default_title_format = "Best <>s of <>" - elif auto_type in ["genre", "mood", "style", "country", "studio", "network", "year", "decade", "content_rating", "subtitle_language", "audio_language", "resolution"]: + elif auto_type in ["genre", "mood", "style", "album_style", "country", "studio", "network", "year", "decade", "content_rating", "subtitle_language", "audio_language", "resolution"]: search_tag = auto_type_translation[auto_type] if auto_type in auto_type_translation else auto_type if library.is_show and auto_type in ["resolution", "subtitle_language", "audio_language"]: tags = library.get_tags(f"episode.{search_tag}") @@ -455,8 +478,9 @@ class MetadataFile(DataFile): all_keys = [str(i.title) for i in tags] auto_list = {str(i.title): i.title for i in tags if str(i.title) not in exclude} if library.is_music: - default_template = {"smart_filter": {"limit": 50, "sort_by": "plays.desc", "any": {f"artist_{auto_type}": f"<>"}}} - default_title_format = "Most Played <> <>s" + final_var = auto_type if auto_type.startwith("album") else f"artist_{auto_type}" + default_template = {"smart_filter": {"limit": 50, "sort_by": "plays.desc", "any": {final_var: f"<>"}}} + default_title_format = f"Most Played <> {'Albums' if auto_type.startwith('album') else '<>'}s" elif auto_type == "resolution": default_template = {"smart_filter": {"sort_by": "title.asc", "any": {auto_type: f"<>"}}} default_title_format = "<> <>s" diff --git a/requirements.txt b/requirements.txt index 5f069830..ead3b20b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ PlexAPI==4.11.2 -tmdbapis==1.0.5 +tmdbapis==1.0.6 arrapi==1.3.1 lxml==4.9.1 requests==2.28.1