diff --git a/VERSION b/VERSION
index f72d2ad0..ff21e5fa 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.19.1-develop67
+1.19.1-develop68
diff --git a/docs/config/settings.md b/docs/config/settings.md
index 90ae65ff..c0a721fe 100644
--- a/docs/config/settings.md
+++ b/docs/config/settings.md
@@ -483,7 +483,7 @@ The available setting attributes which can be set at each level are outlined bel
delete_not_scheduled: true
```
-??? blank "`run_again_delay` - Used to control the depth of search in the asset directory."
+??? blank "`run_again_delay` - Used to control the number of minutes to delay running `run_again` collections."
Set the number of minutes to delay running `run_again` collections after daily run is
finished.
diff --git a/docs/files/dynamic.md b/docs/files/dynamic.md
index d6b7e3dd..d21e5c1f 100644
--- a/docs/files/dynamic.md
+++ b/docs/files/dynamic.md
@@ -116,7 +116,7 @@ by this dynamic collection.
remove_suffix: "Collection"
```
-## Attributes
+### Attributes
??? blank "`type` & `data` - Used to specify the type of Dynamic Collection."
@@ -584,6 +584,36 @@ by this dynamic collection.
- War
```
+## Dynamic Collection Template Variables
+
+When calling a collection file with dynamic collection all the following are automatically accepted as template
+variables which will just replace the same attribute when running the file.
+
+* `data`
+* `exclude`
+* `addons`
+* `remove_suffix`
+* `remove_prefix`
+* `title_format`
+* `key_name_override`
+* `title_override`
+* `custom_keys`
+* `test`
+* `sync`
+* `include`
+* `other_name`
+
+There are also several template variables that will be automatically append/remove from `data`, `exclude`, `include`,
+and `addons` so they can be changed by the user on the fly when needed.
+
+* `append_data`
+* `append_exclude`
+* `remove_exclude`
+* `append_include`
+* `remove_include`
+* `append_addons`
+* `remove_addons`
+
{%
include-markdown "./dynamic_examples.md"
%}
\ No newline at end of file
diff --git a/docs/files/dynamic_types.md b/docs/files/dynamic_types.md
index a4bbe33f..ceefba3f 100644
--- a/docs/files/dynamic_types.md
+++ b/docs/files/dynamic_types.md
@@ -120,7 +120,8 @@ requirements of creating the collection.
??? blank "`origin_country` - Collections based on TMDb origin countries."
- Creates collections based on the TMDb origin country associated with items in the library.
+ Creates collections based on the TMDb origin country associated with items in the
+ library.
@@ -153,6 +154,72 @@ requirements of creating the collection.
type: origin_country
```
+??? blank "`imdb_awards` - Collections based on IMDb Events by Year."
+
+ Creates collections for each of the Year's found on the IMDb event page.
+
+
+
+ **`type` Value:** `imdb_awards`
+
+ **`data` Value:** [Dictionary](../pmm/yaml.md#dictionaries) of Attributes
+
+ ??? blank "`event_id` - Determines the [IMDb Event](https://www.imdb.com/event/) used."
+
+ This determines which [IMDb Event](https://www.imdb.com/event/) is used.
+
+ **Allowed Values:** The ID found in the URLs linked on the [IMDb Events Page](https://www.imdb.com/event/).
+ (ex. `ev0000003`)
+
+ ??? blank "`starting` - Determines the starting year of the event to use."
+
+ This determines the starting year of the event to use to create collections.
+
+ **Allowed Values:** Number greater than 0, `current_year`, or relative year `current_year-#` (`#` is the number
+ of years back from the current year)
+
+ **Default:** `current-5`
+
+ ??? blank "`ending` - Determines the ending year of the event to use."
+
+ This determines the ending year of the event to use to create collections.
+
+ **Allowed Values:** Number greater than 1, `current_year`, or relative year `current_year-#` (`#` is the number
+ of years back from the current year)
+
+ **Default:** `current`
+
+ **Valid Library Types:** Movies and Shows
+
+ **Key Values:** Award Year (sometimes this will look like `2003-2` if there are more then one award show that year)
+
+ **Key Name Value:** Award Year (sometimes this will look like `2003-2` if there are more then one award show that
+ year)
+
+ **Default `title_format`:** `<>`
+
+ ??? tip "Default Template (click to expand)"
+
+ ```yaml
+ default_template:
+ imdb_award:
+ event_id: <>
+ event_year: <>
+ winning: true
+ ```
+
+ ???+ example "Example"
+
+ ```yaml
+ dynamic_collections:
+ Oscar Awards Lists: # This name is the mapping name
+ type: imdb_awards
+ data:
+ event_id: ev0000003
+ starting: current-15
+ ending: current
+ ```
+
??? blank "`trakt_user_lists` - Collections based on Trakt Lists by users."
Creates collections for each of the Trakt lists for the specified users. Use `me` to
@@ -1341,7 +1408,8 @@ requirements of creating the collection.
This determines the starting number of collections to create.
- **Allowed Values:** Number greater than 0, `current_year`, or relative year `current_year-#` (`#` is the number of years back from the current year)
+ **Allowed Values:** Number greater than 0, `current_year`, or relative year `current_year-#` (`#` is the number
+ of years back from the current year)
**Default:** `0`
@@ -1349,7 +1417,8 @@ requirements of creating the collection.
This determines the ending number of collections to create.
- **Allowed Values:** Number greater than 1, `current_year`, or relative year `current_year-#` (`#` is the number of years back from the current year)
+ **Allowed Values:** Number greater than 1, `current_year`, or relative year `current_year-#` (`#` is the number
+ of years back from the current year)
**Default:** `1`
diff --git a/modules/builder.py b/modules/builder.py
index b81dd1d8..2e2b8cf0 100644
--- a/modules/builder.py
+++ b/modules/builder.py
@@ -1531,9 +1531,8 @@ class CollectionBuilder:
raise Failed(f"{self.Type} Error: imdb_award event_year attribute invalid: {og_year}")
else:
event_year = util.parse(self.Type, "event_year", og_year, parent=method_name, datatype="strlist", options=year_options)
-
if len(event_year) > 1 and not git_event:
- raise Failed(f"{self.Type} Error: Only specific events work when using multiple years. Event Options: {self.config.IMDb.events_validation.keys()}")
+ raise Failed(f"{self.Type} Error: Only specific events work when using multiple years. Event Options: [{', '.join([k for k in self.config.IMDb.events_validation])}]")
award_filters = []
if "award_filter" in dict_methods:
if not dict_data[dict_methods["award_filter"]]:
diff --git a/modules/meta.py b/modules/meta.py
index 1061941f..bfaf1a11 100644
--- a/modules/meta.py
+++ b/modules/meta.py
@@ -9,7 +9,7 @@ logger = util.logger
all_auto = ["genre", "number", "custom"]
ms_auto = [
"actor", "year", "content_rating", "original_language", "tmdb_popular_people", "trakt_user_lists", "studio",
- "trakt_liked_lists", "trakt_people_list", "subtitle_language", "audio_language", "resolution", "decade", "imdb_award"
+ "trakt_liked_lists", "trakt_people_list", "subtitle_language", "audio_language", "resolution", "decade", "imdb_awards"
]
auto = {
"Movie": ["tmdb_collection", "edition", "country", "director", "producer", "writer"] + all_auto + ms_auto,
@@ -833,6 +833,7 @@ class MetadataFile(DataFile):
default_template = None
auto_list = {}
all_keys = {}
+ extra_template_vars = {}
dynamic_data = None
def _check_dict(check_dict):
for ck, cv in check_dict.items():
@@ -986,23 +987,59 @@ class MetadataFile(DataFile):
all_keys[role["name"]] = role["name"]
person_count += 1
default_template = {"plex_search": {"any": {auto_type: "<>"}}}
- elif auto_type == "imdb_award":
+ elif auto_type == "imdb_awards":
if "data" not in methods:
raise Failed(f"Config Error: {map_name} data attribute not found")
elif "data" in self.temp_vars:
dynamic_data = util.parse("Config", "data", self.temp_vars["data"], datatype="dict")
else:
dynamic_data = util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="dict")
- lower_methods = {am.lower(): am for am in dynamic_data}
- person_depth = util.parse("Config", "event_id", dynamic_data, parent=f"{map_name} data",
- methods=lower_methods, datatype="int", default=3, minimum=1)
- person_minimum = util.parse("Config", "minimum", dynamic_data, parent=f"{map_name} data",
- methods=lower_methods, datatype="int", default=3,
- minimum=1) if "minimum" in lower_methods else None
- person_limit = util.parse("Config", "limit", dynamic_data, parent=f"{map_name} data",
- methods=lower_methods, datatype="int", default=25,
- minimum=1) if "limit" in lower_methods else None
+ award_methods = {am.lower(): am for am in dynamic_data}
+ event_id = util.parse("Config", "event_id", dynamic_data, parent=f"{map_name} data", methods=award_methods, regex=(r"(ev\d+)", "ev0000003"))
+ extra_template_vars["event_id"] = event_id
+ if event_id not in self.config.IMDb.events_validation:
+ raise Failed(f"Config Error: {map_name} data only specific Event IDs work with imdb_awards. Event Options: [{', '.join([k for k in self.config.IMDb.events_validation])}]")
+ current_year = datetime.now().year
+ _, year_options = self.config.IMDb.get_event_years(event_id)
+ min_year = None
+ max_year = None
+ for option in year_options:
+ year = int(option.split("-")[0] if "-" in option else option)
+ if min_year is None or year < min_year:
+ min_year = year
+ if max_year is None or year > max_year:
+ max_year = year
+ if "starting" in award_methods and str(dynamic_data[award_methods["starting"]]).startswith("current_year"):
+ year_values = str(dynamic_data[award_methods["starting"]]).split("-")
+ try:
+ starting = current_year - (0 if len(year_values) == 1 else int(year_values[1].strip()))
+ except ValueError:
+ raise Failed(f"Config Error: {map_name} data starting attribute modifier invalid '{year_values[1]}'")
+ else:
+ try:
+ starting = util.parse("Config", "starting", dynamic_data, parent=f"{map_name} data", methods=award_methods, datatype="int", minimum=min_year)
+ except Failed
+ if not starting:
+ starting = current_year
+ if "ending" in award_methods and str(dynamic_data[award_methods["ending"]]).startswith("current_year"):
+ year_values = str(dynamic_data[award_methods["ending"]]).split("-")
+ try:
+ ending = current_year - (0 if len(year_values) == 1 else int(year_values[1].strip()))
+ except ValueError:
+ raise Failed(f"Config Error: {map_name} data ending attribute modifier invalid '{year_values[1]}'")
+ else:
+ ending = util.parse("Config", "ending", dynamic_data, parent=f"{map_name} data", methods=award_methods, datatype="int", default=0, minimum=1)
+ if not ending:
+ ending = current_year - 5
+ if starting > ending:
+ raise Failed(f"Config Error: {map_name} data ending must be greater than starting")
+ _, year_options = self.config.IMDb.get_event_years(event_id)
+ for option in year_options:
+ all_keys[option] = option
+ if option not in exclude and starting <= int(option.split("-")[0] if "-" in option else option) <= ending:
+ auto_list[option] = option
+ default_template = {"imdb_award": {"event_id": ">", "event_year": "<>", "winning": True}}
elif auto_type == "number":
if "data" not in methods:
raise Failed(f"Config Error: {map_name} data attribute not found")
@@ -1016,7 +1053,7 @@ class MetadataFile(DataFile):
try:
starting = datetime.now().year - (0 if len(year_values) == 1 else int(year_values[1].strip()))
except ValueError:
- raise Failed(f"Config Error: starting attribute modifier invalid '{year_values[1]}'")
+ raise Failed(f"Config Error: {map_name} data starting attribute modifier invalid '{year_values[1]}'")
else:
starting = util.parse("Config", "starting", dynamic_data, parent=f"{map_name} data", methods=number_methods, datatype="int", default=0, minimum=0)
if "ending" in number_methods and str(dynamic_data[number_methods["ending"]]).startswith("current_year"):
@@ -1024,7 +1061,7 @@ class MetadataFile(DataFile):
try:
ending = datetime.now().year - (0 if len(year_values) == 1 else int(year_values[1].strip()))
except ValueError:
- raise Failed(f"Config Error: ending attribute modifier invalid '{year_values[1]}'")
+ raise Failed(f"Config Error: {map_name} data ending attribute modifier invalid '{year_values[1]}'")
else:
ending = util.parse("Config", "ending", dynamic_data, parent=f"{map_name} data", methods=number_methods, datatype="int", default=0, minimum=1)
increment = util.parse("Config", "increment", dynamic_data, parent=f"{map_name} data", methods=number_methods, datatype="int", default=1, minimum=1) if "increment" in number_methods else 1
@@ -1109,7 +1146,11 @@ class MetadataFile(DataFile):
key_name_override.pop(k)
else:
test_override.append(v)
- test = util.parse("Config", "test", dynamic, parent=map_name, methods=methods, default=False, datatype="bool") if "test" in methods else False
+ test = False
+ if "test" in self.temp_vars:
+ test = util.parse("Config", "test", self.temp_vars["test"], parent="template_variables", datatype="bool")
+ elif "test" in methods:
+ test = util.parse("Config", "test", dynamic, parent=map_name, methods=methods, default=False, datatype="bool")
sync = False
if "sync" in self.temp_vars:
sync = util.parse("Config", "sync", self.temp_vars["sync"], parent="template_variables", datatype="bool")
@@ -1216,6 +1257,9 @@ class MetadataFile(DataFile):
og_call[k] = v[key]
elif "default" in v:
og_call[k] = v["default"]
+ for k, v in extra_template_vars.items():
+ if k not in og_call:
+ og_call[k] = v
template_call = []
for template_name in template_names:
new_call = og_call.copy()
diff --git a/requirements.txt b/requirements.txt
index 98f50091..670d532a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
arrapi==1.4.3
GitPython==3.1.40
-lxml==4.9.4
+lxml==5.0.0
num2words==0.5.13
pathvalidate==3.2.0
pillow==10.1.0