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 Option |
+ album_style |
+
+
+ data Value |
+ Not Used |
+
+
+ Keys |
+ Style |
+
+
+ Key Names |
+ Style |
+
+
+ Default title_format |
+ Most 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