diff --git a/modules/builder.py b/modules/builder.py index 5c90a7d8..dc95fa94 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -274,8 +274,9 @@ class CollectionBuilder: logger.debug("Validating Method: template") new_attributes = self.metadata.apply_template(self.name, self.data, self.data[methods["template"]]) for attr in new_attributes: - self.data[attr] = new_attributes[attr] - methods[attr.lower()] = attr + if attr.lower() not in methods: + self.data[attr] = new_attributes[attr] + methods[attr.lower()] = attr if "delete_not_scheduled" in methods: logger.debug("") diff --git a/modules/meta.py b/modules/meta.py index 8973e122..4ab5021c 100644 --- a/modules/meta.py +++ b/modules/meta.py @@ -17,6 +17,13 @@ auto = { "Artist": ["mood", "style", "country"] + all_auto, "Video": ["country"] + all_auto } +default_templates = { + "tmdb_collection": {"tmdb_collection_details": "<>"}, + "trakt_user_lists": {"trakt_list_details": "<>"}, + "trakt_liked_lists": {"trakt_list_details": "<>"}, + "tmdb_popular_people": {"tmdb_person": f"<>", "plex_search": {"all": {"actor": "tmdb"}}}, + "trakt_people_list": {"tmdb_person": f"<>", "plex_search": {"all": {"actor": "tmdb"}}} +} def get_dict(attribute, attr_data, check_list=None): if check_list is None: @@ -176,36 +183,25 @@ class DataFile: except Failed: continue else: - txt = str(_data) - + final_data = _data def scan_text(og_txt, var, var_value): - if og_txt == f"<<{var}>>": - return str(var_value) + if str(og_txt) == f"<<{var}>>": + return var_value elif f"<<{var}>>" in str(og_txt): return str(og_txt).replace(f"<<{var}>>", str(var_value)) else: return og_txt for option in optional: - if option not in variables and f"<<{option}>>" in txt: + if option not in variables and f"<<{option}>>" in str(final_data): raise Failed for variable, variable_data in variables.items(): if (variable == "collection_name" or variable == "playlist_name") and _method in ["radarr_tag", "item_radarr_tag", "sonarr_tag", "item_sonarr_tag"]: - txt = scan_text(txt, variable, variable_data.replace(",", "")) + final_data = scan_text(final_data, variable, variable_data.replace(",", "")) elif variable != "name": - txt = scan_text(txt, variable, variable_data) + final_data = scan_text(final_data, variable, variable_data) for dm, dd in default.items(): - txt = scan_text(txt, dm, dd) - if txt in ["true", "True"]: - final_data = True - elif txt in ["false", "False"]: - final_data = False - else: - try: - num_data = float(txt) - final_data = int(num_data) if num_data.is_integer() else num_data - except (ValueError, TypeError): - final_data = txt + final_data = scan_text(final_data, dm, dd) return final_data new_attributes = {} @@ -255,16 +251,14 @@ class MetadataFile(DataFile): else: auto_type = dynamic[methods["type"]].lower() exclude = util.parse("Config", "exclude", dynamic, parent=map_name, methods=methods, datatype="list") if "exclude" in methods else [] - add_ons = util.parse("Config", "add_ons", dynamic, parent=map_name, methods=methods, datatype="dictlist") if "add_ons" in methods else {} - for k, v in add_ons.items(): + addons = util.parse("Config", "addons", dynamic, parent=map_name, methods=methods, datatype="dictlist") if "addons" in methods else {} + for k, v in addons.items(): exclude.extend(v) default_title_format = "<>" + default_template = None if auto_type in ["genre", "mood", "style", "country", "network"]: auto_list = {i.title: i.title for i in library.get_tags(auto_type) if i.title not in exclude} - if library.is_music: - use_filter = f"artist_{auto_type}" - else: - use_filter = auto_type + use_filter = f"artist_{auto_type}" if library.is_music else auto_type default_template = {"smart_filter": {"limit": 50, "sort_by": "critic_rating.desc", "all": {use_filter: f"<<{auto_type}>>"}}} default_title_format = "Top <<title>> <<library_type>>s" elif auto_type == "tmdb_collection": @@ -277,22 +271,20 @@ class MetadataFile(DataFile): if tmdb_item and tmdb_item.collection and tmdb_item.collection.id not in exclude and tmdb_item.collection.name not in exclude: auto_list[tmdb_item.collection.id] = tmdb_item.collection.name util.print_end() - default_template = {"tmdb_collection_details": "<<tmdb_collection>>"} elif auto_type == "trakt_user_lists": auto_list = [] for option in util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="list"): auto_list.extend(self.config.Trakt.get_user_lists(option)) - default_template = {"trakt_list_details": "<<trakt_user_lists>>"} + elif auto_type == "trakt_liked_lists": + auto_list = self.config.Trakt.get_liked_lists() + elif auto_type == "tmdb_popular_people": + auto_list = self.config.TMDb.get_popular_people(util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="int", minimum=1)) + elif auto_type == "trakt_people_list": + auto_list = [] + for option in util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="list"): + auto_list.extend(self.config.Trakt.get_people(option)) else: - default_template = {"tmdb_person": f"<<{auto_type}>>", "plex_search": {"all": {"actor": "tmdb"}}} - if auto_type == "tmdb_popular_people": - auto_list = self.config.TMDb.get_popular_people(util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="int", minimum=1)) - elif auto_type == "trakt_people_list": - auto_list = [] - for option in util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="list"): - auto_list.extend(self.config.Trakt.get_people(option)) - else: - raise Failed(f"Config Error: {map_name} type attribute {dynamic[methods['type']]} invalid") + raise Failed(f"Config Error: {map_name} type attribute {dynamic[methods['type']]} invalid") title_format = default_title_format if "title_format" in methods: title_format = util.parse("Config", "title_format", dynamic, parent=map_name, methods=methods, default=default_title_format) @@ -312,13 +304,13 @@ class MetadataFile(DataFile): if f"<<{auto_type}>>" not in str(self.templates[template_name]): raise Failed(f"Config Error: {map_name} template: {template_name} is required to have the template variable <<{auto_type}>>") else: - self.templates[map_name] = default_template + self.templates[map_name] = default_template if default_template else default_templates[auto_type] template_name = map_name remove_prefix = util.parse("Config", "remove_prefix", dynamic, parent=map_name, methods=methods, datatype="commalist") if "remove_prefix" in methods else [] remove_suffix = util.parse("Config", "remove_suffix", dynamic, parent=map_name, methods=methods, datatype="commalist") if "remove_suffix" in methods else [] sync = {i.title: i for i in self.library.search(libtype="collection", label=str(map_name))} if sync else {} for key, value in auto_list.items(): - template_call = {"name": template_name, auto_type: [key].extend(add_ons[key]) if key in add_ons else key} + template_call = {"name": template_name, auto_type: [key] + addons[key] if key in addons else key} for k, v in dictionary_variables.items(): if key in v: template_call[k] = v[key] diff --git a/modules/trakt.py b/modules/trakt.py index 31a1c59c..40c8254c 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -18,7 +18,15 @@ sorts = [ "rank", "added", "title", "released", "runtime", "popularity", "percentage", "votes", "random", "my_rating", "watched", "collected" ] -id_translation = {"movie": "movie", "show": "show", "season": "show", "episode": "show", "person": "person"} +id_translation = {"movie": "movie", "show": "show", "season": "show", "episode": "show", "person": "person", "list": "list"} +id_types = { + "movie": ("tmdb", "TMDb ID"), + "person": ("tmdb", "TMDb ID"), + "show": ("tvdb", "TVDb ID"), + "season": ("tvdb", "TVDb ID"), + "episode": ("tvdb", "TVDb ID"), + "list": ("slug", "Trakt Slug") +} class Trakt: def __init__(self, config, params): @@ -163,19 +171,20 @@ class Trakt: current_type = item["type"] else: continue - id_type = "tmdb" if current_type in ["movie", "person"] else "tvdb" + id_type, id_display = id_types[current_type] if id_type in data["ids"] and data["ids"][id_type]: final_id = data["ids"][id_type] if current_type == "episode": final_id = f"{final_id}_{item[current_type]['season']}" if current_type in ["episode", "season"]: final_id = f"{final_id}_{item[current_type]['number']}" - if current_type == "person": + if current_type in ["person", "list"]: final_id = (final_id, data["name"]) final_type = f"{id_type}_{current_type}" if current_type in ["episode", "season", "person"] else id_type ids.append((final_id, final_type)) else: - logger.error(f"Trakt Error: No {id_type.upper().replace('B', 'b')} ID found for {data['title']} ({data['year']})") + name = data["name"] if current_type in ["person", "list"] else f"{data['title']} ({data['year']})" + logger.error(f"Trakt Error: No {id_display} found for {name}") return ids def get_user_lists(self, data): @@ -185,7 +194,16 @@ class Trakt: raise Failed(f"Trakt Error: User {data} not found") if len(items) == 0: raise Failed(f"Trakt Error: User {data} has no lists") - return {f"{base_url}/users/{data}/lists/{i['ids']['slug']}": i["name"] for i in items} + return {self.build_user_url(data, i["ids"]["slug"]): i["name"] for i in items} + + def get_liked_lists(self): + items = self._request(f"/users/likes/lists") + if len(items) == 0: + raise Failed(f"Trakt Error: No Liked lists found") + return {self.build_user_url(i['list']['user']['ids']['slug'], i['list']['ids']['slug']): i["list"]["name"] for i in items} + + def build_user_url(self, user, name): + return f"{base_url.replace('api.', '')}/users/{user}/lists/{name}" def _user_list(self, data): try: