diff --git a/modules/builder.py b/modules/builder.py index 38ccfd2f..75d4d153 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -111,8 +111,8 @@ sonarr_details = [ "sonarr_quality", "sonarr_season", "sonarr_search", "sonarr_cutoff_search", "sonarr_tag" ] album_details = ["non_item_remove_label", "item_label", "item_album_sorting"] -discover_types = {0: "returning", 1: "planned", 2: "production", 3: "ended", 4: "cancelled", 5: "pilot"} -discover_status = {0: "documentary", 1: "news", 2: "miniseries", 3: "reality", 4: "scripted", 5: "talk_show", 6: "video"} +discover_types = {0: "documentary", 1: "news", 2: "miniseries", 3: "reality", 4: "scripted", 5: "talk_show", 6: "video"} +discover_status = {0: "returning", 1: "planned", 2: "production", 3: "ended", 4: "cancelled", 5: "pilot"} filters_by_type = { "movie_show_season_episode_artist_album_track": ["title", "summary", "collection", "has_collection", "added", "last_played", "user_rating", "plays"], "movie_show_season_episode_album_track": ["year"], @@ -1589,9 +1589,9 @@ class CollectionBuilder: return data.lower() raise Failed(f"{self.Type} Error: history attribute invalid: {data} must be a number between 1-30, day, or month") elif attribute == "tmdb_type": - return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_types.items()]).lower() + return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_types.items()]) elif attribute == "tmdb_status": - return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_status.items()]).lower() + return util.parse(self.Type, final, data, datatype="commalist", options=[v for k, v in discover_status.items()]) elif attribute in plex.tag_attributes and modifier in ["", ".not"]: if attribute in plex.tmdb_attributes: final_values = [] diff --git a/modules/meta.py b/modules/meta.py index 3f1a9b91..30d14fb5 100644 --- a/modules/meta.py +++ b/modules/meta.py @@ -10,10 +10,10 @@ logger = logging.getLogger("Plex Meta Manager") github_base = "https://raw.githubusercontent.com/meisnate12/Plex-Meta-Manager-Configs/master/" all_auto = ["genre"] - +ms_auto = ["trakt_user_lists", "trakt_people_list"] auto = { - "Movie": ["tmdb_collection", "country"] + all_auto, - "Show": ["network"] + all_auto, + "Movie": ["tmdb_collection", "country"] + all_auto + ms_auto, + "Show": ["network"] + all_auto + ms_auto, "Artist": ["mood", "style", "country"] + all_auto, "Video": ["country"] + all_auto } @@ -239,20 +239,23 @@ class MetadataFile(DataFile): self.templates = get_dict("templates", data) self.collections = get_dict("collections", data, library.collections) col_names = library.collections + [c for c in self.collections] - for auto_name, auto_data in get_dict("auto_collections", data).items(): + for map_name, dynamic in get_dict("dynamic_collections", data).items(): try: - auto_methods = {dm.lower(): dm for dm in auto_data} - if "auto_list" not in auto_methods: - raise Failed(f"Config Error: {auto_name}'s auto_list attribute not found") - elif not auto_data[auto_methods["auto_list"]]: - raise Failed(f"Config Error: {auto_name}'s auto_list attribute is blank") - elif auto_data[auto_methods["auto_list"]].lower() not in auto[library.type]: - raise Failed(f"Config Error: auto_list attribute {auto_data[auto_methods['auto_list']].lower()} invalid Options: {auto[library.type]}") - elif auto_data[auto_methods["auto_list"]].lower() == "network" and library.agent not in plex.new_plex_agents: - raise Failed(f"Config Error: network auto_list only works with the New Plex TV Agent") + methods = {dm.lower(): dm for dm in dynamic} + if "type" not in methods: + raise Failed(f"Config Error: {map_name}'s type attribute not found") + elif not dynamic[methods["type"]]: + raise Failed(f"Config Error: {map_name}'s type attribute is blank") + elif dynamic[methods["type"]].lower() not in auto[library.type]: + raise Failed(f"Config Error: type attribute {dynamic[methods['type']].lower()} invalid Options: {auto[library.type]}") + elif dynamic[methods["type"]].lower() == "network" and library.agent not in plex.new_plex_agents: + raise Failed(f"Config Error: network type only works with the New Plex TV Agent") + elif dynamic[methods["type"]].lower().startswith("trakt") and not self.config.Trakt: + raise Failed(f"Config Error: trakt must be configured to use {dynamic[methods['type']]}") else: - auto_type = auto_data[auto_methods["auto_list"]].lower() - exclude = util.parse("Config", "exclude", auto_data, methods=auto_methods, datatype="list") if "exclude" in auto_methods else [] + auto_type = dynamic[methods["type"]].lower() + exclude = util.parse("Config", "exclude", dynamic, methods=methods, datatype="list") if "exclude" in methods else [] + default_title_format = "<>" 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: @@ -272,29 +275,47 @@ class MetadataFile(DataFile): auto_list[tmdb_item.collection.id] = tmdb_item.collection.name util.print_end() default_template = {"tmdb_collection_details": "<<tmdb_collection>>"} - default_title_format = "<<title>>" else: - raise Failed(f"Config Error: {auto_name}'s auto_list attribute {auto_data[auto_methods['auto_list']]} invalid") + if "data" not in methods: + raise Failed(f"Config Error: {map_name}'s data attribute not found") + elif not dynamic[methods["data"]]: + raise Failed(f"Config Error: {map_name}'s data attribute is blank") + else: + options = dynamic[methods["data"]] + if not isinstance(options, list): + options = [options] + if auto_type == "trakt_people_list": + auto_list = [] + for option in options: + auto_list.extend(self.config.Trakt.get_people(option)) + default_template = {"tmdb_person": "<<trakt_people_list>>", "plex_search": {"all": {"actor": "tmdb"}}} + elif auto_type == "trakt_user_lists": + auto_list = [] + for option in options: + auto_list.extend(self.config.Trakt.get_user_lists(option)) + default_template = {"trakt_list_details": "<<trakt_user_lists>>"} + else: + raise Failed(f"Config Error: {map_name}'s type attribute {dynamic[methods['type']]} invalid") title_format = default_title_format - if "title_format" in auto_methods: - title_format = util.parse("Config", "title_format", auto_data, methods=auto_methods, default=default_title_format) + if "title_format" in methods: + title_format = util.parse("Config", "title_format", dynamic, methods=methods, default=default_title_format) if "<<title>>" not in title_format: logger.error(f"Config Error: <<title>> not in title_format: {title_format} using default: {default_title_format}") title_format = default_title_format if "<<library_type>>" in title_format: title_format = title_format.replace("<<library_type>>", library.type) - dictionary_variables = util.parse("Config", "dictionary_variables", auto_data, methods=auto_methods, datatype="dictdict") if "dictionary_variables" in auto_methods else {} - template_name = util.parse("Config", "template", auto_data, methods=auto_methods) + dictionary_variables = util.parse("Config", "dictionary_variables", dynamic, methods=methods, datatype="dictdict") if "dictionary_variables" in methods else {} + template_name = util.parse("Config", "template", dynamic, methods=methods) if template_name: if template_name not in self.templates: raise Failed(f"Config Error: template: {template_name} not found") if f"<<{auto_type}>>" not in str(self.templates[template_name]): raise Failed(f"Config Error: template: {template_name} is required to have the template variable <<{auto_type}>>") else: - self.templates[auto_name] = default_template - template_name = auto_name - remove_prefix = util.parse("Config", "remove_prefix", auto_data, methods=auto_methods, datatype="commalist") if "remove_prefix" in auto_methods else [] - remove_suffix = util.parse("Config", "remove_suffix", auto_data, methods=auto_methods, datatype="commalist") if "remove_suffix" in auto_methods else [] + self.templates[map_name] = default_template + template_name = map_name + remove_prefix = util.parse("Config", "remove_prefix", dynamic, methods=methods, datatype="commalist") if "remove_prefix" in methods else [] + remove_suffix = util.parse("Config", "remove_suffix", dynamic, methods=methods, datatype="commalist") if "remove_suffix" in methods else [] for key, value in auto_list.items(): template_call = {"name": template_name, auto_type: key} for k, v in dictionary_variables.items(): diff --git a/modules/trakt.py b/modules/trakt.py index c3e0cf0c..31a1c59c 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -18,7 +18,7 @@ sorts = [ "rank", "added", "title", "released", "runtime", "popularity", "percentage", "votes", "random", "my_rating", "watched", "collected" ] -id_translation = {"movie": "tmdb", "show": "tvdb", "season": "TVDb Season", "episode": "TVDb Episode"} +id_translation = {"movie": "movie", "show": "show", "season": "show", "episode": "show", "person": "person"} class Trakt: def __init__(self, config, params): @@ -159,23 +159,34 @@ class Trakt: data = item[item_type] current_type = item_type elif "type" in item and item["type"] in id_translation: - data = item["movie" if item["type"] == "movie" else "show"] + data = item[id_translation[item["type"]]] current_type = item["type"] else: continue - id_type = "tmdb" if current_type == "movie" else "tvdb" + id_type = "tmdb" if current_type in ["movie", "person"] else "tvdb" 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']}" - final_type = f"{id_type}_{current_type}" if current_type in ["episode", "season"] else id_type + if current_type == "person": + 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']})") return ids + def get_user_lists(self, data): + try: + items = self._request(f"/users/{data}/lists") + except Failed: + 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} + def _user_list(self, data): try: items = self._request(f"{requests.utils.urlparse(data).path}/items") @@ -198,6 +209,9 @@ class Trakt: items = self._request(f"/{'movies' if is_movie else 'shows'}/{pagenation}?limit={amount}") return self._parse(items, typeless=pagenation == "popular", item_type="movie" if is_movie else "show") + def get_people(self, data): + return {i[0][0]: i[0][1] for i in self._user_list(data) if i[1] == "tmdb_person"} + def validate_trakt(self, trakt_lists, is_movie, trakt_type="list"): values = util.get_list(trakt_lists, split=False) trakt_values = []