diff --git a/modules/anilist.py b/modules/anilist.py index 53e71826..2ae98118 100644 --- a/modules/anilist.py +++ b/modules/anilist.py @@ -9,6 +9,13 @@ class AniListAPI: def __init__(self, config): self.config = config self.url = "https://graphql.anilist.co" + self.tags = {} + self.genres = {} + + for tag in self.send_request("query{MediaTagCollection {name}}", {})["data"]["MediaTagCollection"]: + self.tags[tag["name"].lower()] = tag["name"] + for genre in self.send_request("query{GenreCollection}", {})["data"]["GenreCollection"]: + self.genres[genre.lower()] = genre @retry(stop_max_attempt_number=6, wait_fixed=10000) def post(self, query, variables): @@ -56,7 +63,6 @@ class AniListAPI: break if 0 < limit == count: break - return mal_ids def top_rated(self, limit): @@ -93,6 +99,30 @@ class AniListAPI: variables = {"season": season.upper(), "year": year, "sort": "SCORE_DESC" if sort == "score" else "POPULARITY_DESC"} return self.get_pagenation(query, limit=limit, variables=variables) + def genre(self, genre, sort, limit): + query = """ + query ($page: Int, $genre: String, $sort: [MediaSort]) { + Page(page: $page){ + pageInfo {hasNextPage} + media(genre: $genre, sort: $sort){idMal} + } + } + """ + variables = {"genre": genre, "sort": "SCORE_DESC" if sort == "score" else "POPULARITY_DESC"} + return self.get_pagenation(query, limit=limit, variables=variables) + + def tag(self, tag, sort, limit): + query = """ + query ($page: Int, $tag: String, $sort: [MediaSort]) { + Page(page: $page){ + pageInfo {hasNextPage} + media(tag: $tag, sort: $sort){idMal} + } + } + """ + variables = {"tag": tag, "sort": "SCORE_DESC" if sort == "score" else "POPULARITY_DESC"} + return self.get_pagenation(query, limit=limit, variables=variables) + def studio(self, studio_id): query = """ query ($page: Int, $id: Int) { @@ -154,6 +184,16 @@ class AniListAPI: return mal_ids, ignore_ids, name + def validate_genre(self, genre): + if genre.lower() in self.genres: + return self.genres[genre.lower()] + raise Failed(f"AniList Error: Genre: {genre} does not exist") + + def validate_tag(self, tag): + if tag.lower() in self.tags: + return self.tags[tag.lower()] + raise Failed(f"AniList Error: Tag: {tag} does not exist") + def validate_anilist_ids(self, anilist_ids, studio=False): anilist_values = [] for anilist_id in anilist_ids: @@ -183,7 +223,15 @@ class AniListAPI: elif method == "anilist_season": mal_ids = self.season(data["season"], data["year"], data["sort_by"], data["limit"]) if status_message: - logger.info(f"Processing {pretty}: {data['limit']} Anime from {util.pretty_seasons[data['season']]} {data['year']} sorted by {util.anilist_pretty[data['sort_by']]}") + logger.info(f"Processing {pretty}: {data['limit'] if data['limit'] > 0 else 'All'} Anime from {util.pretty_seasons[data['season']]} {data['year']} sorted by {util.anilist_pretty[data['sort_by']]}") + elif method == "anilist_genre": + mal_ids = self.genre(data["genre"], data["sort_by"], data["limit"]) + if status_message: + logger.info(f"Processing {pretty}: {data['limit'] if data['limit'] > 0 else 'All'} Anime from the Genre: {data['genre']} sorted by {util.anilist_pretty[data['sort_by']]}") + elif method == "anilist_tag": + mal_ids = self.tag(data["tag"], data["sort_by"], data["limit"]) + if status_message: + logger.info(f"Processing {pretty}: {data['limit'] if data['limit'] > 0 else 'All'} Anime from the Tag: {data['tag']} sorted by {util.anilist_pretty[data['sort_by']]}") elif method in ["anilist_studio", "anilist_relations"]: if method == "anilist_studio": mal_ids, name = self.studio(data) else: mal_ids, _, name = self.relations(data) diff --git a/modules/builder.py b/modules/builder.py index 73bdd0e2..94fe943f 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -541,25 +541,36 @@ class CollectionBuilder: new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, maximum=1000) self.methods.append((method_name, [new_dictionary])) - elif method_name == "anilist_season": + elif "anilist" in method_name: new_dictionary = {"sort_by": "score"} - if "sort_by" not in data[m]: logger.warning("Collection Warning: anilist_season sort_by attribute not found using score as default") - elif not data[m]["sort_by"]: logger.warning("Collection Warning: anilist_season sort_by attribute is blank using score as default") - elif data[m]["sort_by"] not in ["score", "popular"]: logger.warning(f"Collection Warning: anilist_season sort_by attribute {data[m]['sort_by']} invalid must be either 'score' or 'popular' using score as default") - else: new_dictionary["sort_by"] = data[m]["sort_by"] + if method_name == "anilist_season": + if current_time.month in [12, 1, 2]: new_dictionary["season"] = "winter" + elif current_time.month in [3, 4, 5]: new_dictionary["season"] = "spring" + elif current_time.month in [6, 7, 8]: new_dictionary["season"] = "summer" + elif current_time.month in [9, 10, 11]: new_dictionary["season"] = "fall" - if current_time.month in [12, 1, 2]: new_dictionary["season"] = "winter" - elif current_time.month in [3, 4, 5]: new_dictionary["season"] = "spring" - elif current_time.month in [6, 7, 8]: new_dictionary["season"] = "summer" - elif current_time.month in [9, 10, 11]: new_dictionary["season"] = "fall" + if "season" not in data[m]: logger.warning(f"Collection Warning: anilist_season season attribute not found using the current season: {new_dictionary['season']} as default") + elif not data[m]["season"]: logger.warning(f"Collection Warning: anilist_season season attribute is blank using the current season: {new_dictionary['season']} as default") + elif data[m]["season"] not in util.pretty_seasons: logger.warning(f"Collection Warning: anilist_season season attribute {data[m]['season']} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {new_dictionary['season']} as default") + else: new_dictionary["season"] = data[m]["season"] - if "season" not in data[m]: logger.warning(f"Collection Warning: anilist_season season attribute not found using the current season: {new_dictionary['season']} as default") - elif not data[m]["season"]: logger.warning(f"Collection Warning: anilist_season season attribute is blank using the current season: {new_dictionary['season']} as default") - elif data[m]["season"] not in util.pretty_seasons: logger.warning(f"Collection Warning: anilist_season season attribute {data[m]['season']} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {new_dictionary['season']} as default") - else: new_dictionary["season"] = data[m]["season"] + new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, minimum=1917, maximum=current_time.year + 1) + elif method_name == "anilist_genre": + if "genre" not in data[m]: raise Failed(f"Collection Warning: anilist_genre genre attribute not found") + elif not data[m]["genre"]: raise Failed(f"Collection Warning: anilist_genre genre attribute is blank") + else: new_dictionary["genre"] = self.config.AniList.validate_genre(data[m]["genre"]) + elif method_name == "anilist_tag": + if "tag" not in data[m]: raise Failed(f"Collection Warning: anilist_tag tag attribute not found") + elif not data[m]["tag"]: raise Failed(f"Collection Warning: anilist_tag tag attribute is blank") + else: new_dictionary["tag"] = self.config.AniList.validate_tag(data[m]["tag"]) + + if "sort_by" not in data[m]: logger.warning(f"Collection Warning: {method_name} sort_by attribute not found using score as default") + elif not data[m]["sort_by"]: logger.warning(f"Collection Warning: {method_name} sort_by attribute is blank using score as default") + elif data[m]["sort_by"] not in ["score", "popular"]: logger.warning(f"Collection Warning: {method_name} sort_by attribute {data[m]['sort_by']} invalid must be either 'score' or 'popular' using score as default") + else: new_dictionary["sort_by"] = data[m]["sort_by"] - new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, minimum=1917, maximum=current_time.year + 1) new_dictionary["limit"] = get_int(method_name, "limit", data[m], 0, maximum=500) + self.methods.append((method_name, [new_dictionary])) else: raise Failed(f"Collection Error: {m} attribute is not a dictionary: {data[m]}") diff --git a/modules/util.py b/modules/util.py index f935b2b8..d7730fb4 100644 --- a/modules/util.py +++ b/modules/util.py @@ -87,11 +87,13 @@ pretty_names = { "anidb_id": "AniDB ID", "anidb_relation": "AniDB Relation", "anidb_popular": "AniDB Popular", + "anilist_genre": "AniList Genre", "anilist_id": "AniList ID", "anilist_popular": "AniList Popular", "anilist_relations": "AniList Relations", "anilist_season": "AniList Season", "anilist_studio": "AniList Studio", + "anilist_tag": "AniList Tag", "anilist_top_rated": "AniList Top Rated", "imdb_list": "IMDb List", "imdb_id": "IMDb ID", @@ -224,11 +226,13 @@ all_lists = [ "anidb_id", "anidb_relation", "anidb_popular", + "anilist_genre", "anilist_id", "anilist_popular", "anilist_relations", "anilist_season", "anilist_studio", + "anilist_tag", "anilist_top_rated", "imdb_list", "imdb_id", @@ -311,7 +315,9 @@ other_attributes = [ ] dictionary_lists = [ "filters", + "anilist_genre", "anilist_season", + "anilist_tag", "mal_season", "mal_userlist", "plex_collectionless",