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