Hide keyboard shortcuts

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> 

3 

4This file is part of toktokkie. 

5 

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. 

10 

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. 

15 

16You should have received a copy of the GNU General Public License 

17along with toktokkie. If not, see <http://www.gnu.org/licenses/>. 

18LICENSE""" 

19 

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 

35 

36 

37class IdFetcher: 

38 """ 

39 Class that handles fetching ID types using the internet 

40 """ 

41 

42 logger = logging.getLogger(__name__) 

43 """ 

44 Logger for this class 

45 """ 

46 

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 

55 

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 

86 

87 if results is None or len(results) == 0: 

88 return None 

89 else: 

90 return results 

91 

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() 

100 

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() 

112 

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 

130 

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 

148 

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() 

157 

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 

170 

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())] 

181 

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 = [] 

189 

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 

199 

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 

210 

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 

220 

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] 

233 

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 

262 

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] 

275 

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"] 

295 

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