Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2015 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of toktokkie.
6toktokkie is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11toktokkie is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20import json
21import logging
22import tvdb_api
23import requests
24import musicbrainzngs
25# noinspection PyPackageRequirements
26from imdb import IMDb
27from typing import List, Dict, Optional, Tuple
28from puffotter.graphql import GraphQlClient
29from toktokkie import version
30from toktokkie.enums import IdType, MediaType
31from toktokkie.metadata.base.IdHelper import IdHelper
32from anime_list_apis.api.AnilistApi import AnilistApi
33from anime_list_apis.models.attributes.MediaType import MediaType as \
34 AnimeListMediaType
37class IdFetcher:
38 """
39 Class that handles fetching ID types using the internet
40 """
42 logger = logging.getLogger(__name__)
43 """
44 Logger for this class
45 """
47 def __init__(self, name: str, media_type: MediaType):
48 """
49 Initializes the ID fetcher
50 :param name: The name of the media
51 :param media_type: The media type of the media
52 """
53 self.name = name
54 self.media_type = media_type
56 def fetch_ids(
57 self,
58 id_type: IdType,
59 other_ids: Dict[IdType, List[str]]
60 ) -> Optional[List[str]]:
61 """
62 Retrieves any supported IDs based on the media name and/or existing
63 IDs.
64 :param id_type: The ID Type to fetch
65 :param other_ids: Any other known IDs
66 :return: The IDs or None if no ID could be determined
67 """
68 minimized_ids = IdHelper.minimize_ids(other_ids)
69 results: Optional[List[str]]
70 try:
71 if id_type == IdType.TVDB:
72 results = self.__load_tvdb_ids(minimized_ids)
73 elif id_type == IdType.IMDB:
74 results = self.__load_imdb_ids(minimized_ids)
75 elif id_type == IdType.ANILIST:
76 results = self.__load_anilist_ids(minimized_ids)
77 elif id_type == IdType.MYANIMELIST:
78 results = self.__load_myanimelist_ids(minimized_ids)
79 elif id_type == IdType.MUSICBRAINZ_ARTIST:
80 results = self.__load_musicbrainz_ids(minimized_ids)
81 else:
82 results = None
83 except Exception as e:
84 self.logger.warning(str(e))
85 results = None
87 if results is None or len(results) == 0:
88 return None
89 else:
90 return results
92 def __load_tvdb_ids(self, _: Dict[IdType, List[str]]) \
93 -> Optional[List[str]]:
94 """
95 Loads TVDB IDs
96 :param _: Any additional IDs
97 :return: The tvdb IDs, or None if none could be found
98 """
99 return self.__load_tvdb_ids_from_name()
101 def __load_imdb_ids(self, other_ids: Dict[IdType, List[str]]) \
102 -> Optional[List[str]]:
103 """
104 Loads IMDB IDs
105 :param other_ids: Any additional IDs
106 :return: The imdb IDs, or None if none could be found
107 """
108 if IdType.TVDB in other_ids:
109 return self.__load_imdb_ids_from_tvdb(other_ids[IdType.TVDB])
110 else:
111 return self.__load_imdb_ids_from_name()
113 def __load_anilist_ids(self, other_ids: Dict[IdType, List[str]]) \
114 -> Optional[List[str]]:
115 """
116 Loads Anilist IDs
117 :param other_ids: Any additional IDs
118 :return: The anilist IDs, or None if none could be found
119 """
120 if IdType.MYANIMELIST in other_ids:
121 return self.__load_anilist_ids_from_mal(
122 other_ids[IdType.MYANIMELIST]
123 )
124 elif IdType.MANGADEX in other_ids:
125 return self.__load_anilist_ids_from_mangadex(
126 other_ids[IdType.MANGADEX]
127 )
128 else:
129 return None
131 def __load_myanimelist_ids(self, other_ids: Dict[IdType, List[str]]) \
132 -> Optional[List[str]]:
133 """
134 Loads myanimelist IDs
135 :param other_ids: Any additional IDs
136 :return: The myanimelist IDs, or None if none could be found
137 """
138 if IdType.ANILIST in other_ids:
139 return self.__load_mal_ids_from_anilist(
140 other_ids[IdType.ANILIST]
141 )
142 elif IdType.MANGADEX in other_ids:
143 return self.__load_mal_ids_from_mangadex(
144 other_ids[IdType.MANGADEX]
145 )
146 else:
147 return None
149 def __load_musicbrainz_ids(self, _: Dict[IdType, List[str]]) \
150 -> Optional[List[str]]:
151 """
152 Loads musicbrainz IDs
153 :param _: Any additional IDs
154 :return: The musicbrainz IDs, or None if none could be found
155 """
156 return self.__load_musicbrainz_artist_ids_from_name()
158 def __load_tvdb_ids_from_name(self) -> Optional[List[str]]:
159 """
160 Retrieves TVDB IDs based on the media name
161 :return: THe TVDB IDs
162 """
163 try:
164 return [str(tvdb_api.Tvdb()[self.name].data["id"])]
165 except (tvdb_api.tvdb_shownotfound, TypeError):
166 return None
167 except ValueError:
168 self.logger.warning("TVDB API now required API keys")
169 return None
171 def __load_imdb_ids_from_name(self) -> Optional[List[str]]:
172 """
173 Retrieves IMDB IDs based on the media name
174 :return: The IMDB IDs
175 """
176 results = IMDb().search_movie(self.name)
177 if len(results) == 0:
178 return None
179 else:
180 return ["tt" + str(results[0].getID())]
182 def __load_imdb_ids_from_tvdb(self, tvdb_ids: List[str]) -> List[str]:
183 """
184 Loads IMDB IDs based on tvdb IDs
185 :param tvdb_ids: THe TVDB IDs
186 :return: THe IMDB IDs
187 """
188 imdb_ids = []
190 for tvdb_id in tvdb_ids:
191 try:
192 imdb_id = tvdb_api.Tvdb()[int(tvdb_id)].data["imdbId"]
193 imdb_ids.append(imdb_id)
194 except (tvdb_api.tvdb_shownotfound, TypeError):
195 self.logger.warning("Show not found")
196 except ValueError:
197 self.logger.warning("TVDB API now required API keys")
198 return imdb_ids
200 def __load_anilist_ids_from_mal(self, mal_ids: List[str]) -> List[str]:
201 """
202 Loads anilist IDs based on myanimelist IDs
203 :param mal_ids: THe myanimelist IDs
204 :return: The anilist IDs
205 """
206 self.logger.info("Loading anilist ID from mal IDs {}".format(mal_ids))
207 list_type = AnimeListMediaType.ANIME
208 if self.media_type in IdHelper.literature_media_types():
209 list_type = AnimeListMediaType.MANGA
211 ids = []
212 api = AnilistApi()
213 for mal_id in mal_ids:
214 ids.append(str(
215 api.get_anilist_id_from_mal_id(
216 list_type, int(mal_id)
217 )
218 ))
219 return ids
221 def __load_anilist_ids_from_mangadex(self, mangadex_ids: List[str]) \
222 -> List[str]:
223 """
224 Loads anilist IDs from mangadex IDs
225 :param mangadex_ids: The mangadex IDs
226 :return: The anilist IDs
227 """
228 anilist_ids = []
229 for mangadex_id in mangadex_ids:
230 _id = self.__load_mangadex_related_ids(mangadex_id)[0]
231 anilist_ids.append(_id)
232 return [x for x in anilist_ids if x is not None]
234 def __load_mal_ids_from_anilist(self, anilist_ids: List[str]) \
235 -> List[str]:
236 """
237 Loads myanimelist IDs from anilist IDs
238 :param anilist_ids: The anilist IDs
239 :return: The myanimelist IDs
240 """
241 mal_ids = []
242 graphql = GraphQlClient("https://graphql.anilist.co")
243 query = """
244 query ($id: Int, $media_type: MediaType) {
245 Media(id: $id, type: $media_type) {
246 idMal
247 }
248 }
249 """
250 media_type = "ANIME"
251 if self.media_type in IdHelper.literature_media_types():
252 media_type = "MANGA"
253 for anilist_id in anilist_ids:
254 data = graphql.query(
255 query,
256 {"id": int(anilist_id), "media_type": media_type}
257 )
258 mal_id = data["data"]["Media"]["idMal"]
259 if mal_id is not None:
260 mal_ids.append(str(mal_id))
261 return mal_ids
263 def __load_mal_ids_from_mangadex(self, mangadex_ids: List[str]) \
264 -> List[str]:
265 """
266 Loads myanimelist IDs from mangadex IDs
267 :param mangadex_ids: The mangadex IDs
268 :return: The myanimelist IDs
269 """
270 mal_ids = []
271 for mangadex_id in mangadex_ids:
272 _id = self.__load_mangadex_related_ids(mangadex_id)[1]
273 mal_ids.append(_id)
274 return [x for x in mal_ids if x is not None]
276 def __load_musicbrainz_artist_ids_from_name(self) -> List[str]:
277 """
278 Retrieves a musicbrainz ID based on the artist name
279 :return: The musicbrainz IDs
280 """
281 musicbrainzngs.set_useragent(
282 "toktokkie media manager",
283 version,
284 "https://gitlab.namibsun.net/namibsun/python/toktokie"
285 )
286 artist_guess = musicbrainzngs.search_artists(self.name)
287 if artist_guess["artist-count"] > 0:
288 name = artist_guess["artist-list"][0]["name"]
289 if name == self.name:
290 return [artist_guess["artist-list"][0]["id"]]
291 else:
292 return ["0"]
293 else:
294 return ["0"]
296 @staticmethod
297 def __load_mangadex_related_ids(mangadex_id: str) \
298 -> Tuple[Optional[str], Optional[str]]:
299 """
300 Loads IDs related to a mangadex ID
301 :param mangadex_id: The mangadex ID
302 :return: The anilist ID, the myanimelist ID
303 """
304 url = f"https://mangadex.org/api/v2/manga/{mangadex_id}"
305 response = requests.get(url)
306 data = json.loads(response.text)
307 anilist_id = data["data"]["links"].get("al")
308 myanimelist_id = data["data"]["links"].get("mal")
309 return anilist_id, myanimelist_id