diff --git a/modules/arms.py b/modules/arms.py index 9ad6f840..20b2db3f 100644 --- a/modules/arms.py +++ b/modules/arms.py @@ -1,4 +1,4 @@ -import logging, requests +import logging, re, requests from lxml import html from modules import util from modules.util import Failed @@ -140,10 +140,10 @@ class ArmsAPI: try: tvdb_id = self.config.TMDb.convert_imdb_to_tvdb(imdb_id) except Failed: pass if not tmdb_id and not tvdb_id and self.config.Trakt: - try: tmdb_id = self.config.Trakt.convert_imdb_to_tmdb(imdb_id) + try: tmdb_id = self.convert_imdb_to_tmdb(imdb_id) except Failed: pass if not tmdb_id and not tvdb_id and self.config.Trakt: - try: tvdb_id = self.config.Trakt.convert_imdb_to_tvdb(imdb_id) + try: tvdb_id = self.convert_imdb_to_tvdb(imdb_id) except Failed: pass if tmdb_id and not from_cache: try: self.config.TMDb.get_movie(tmdb_id) @@ -159,3 +159,205 @@ class ArmsAPI: if tvdb_id and update_tvdb is not False: self.config.Cache.update_imdb("show", update_tvdb, imdb_id, tvdb_id) return tmdb_id, tvdb_id + + def convert_tmdb_to_imdb(self, tmdb_id, is_movie=True, fail=False): + try: + return self.config.TMDb.convert_from(tmdb_id, "imdb_id", is_movie) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(tmdb_id, "tmdb", "imdb", "movie" if is_movie else "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No IMDb ID Found for TMDb ID: {tmdb_id}") + return None + + def convert_imdb_to_tmdb(self, imdb_id, is_movie=True, fail=False): + try: + return self.config.TMDb.convert_to(imdb_id, "imdb_id", is_movie) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(imdb_id, "imdb", "tmdb", "movie" if is_movie else "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No TMDb ID Found for IMDb ID: {imdb_id}") + return None + + def convert_tmdb_to_tvdb(self, tmdb_id, fail=False): + try: + return self.config.TMDb.convert_from(tmdb_id, "tvdb_id", False) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(tmdb_id, "tmdb", "tvdb", "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No TVDb ID Found for TMDb ID: {tmdb_id}") + return None + + def convert_tvdb_to_tmdb(self, tvdb_id, fail=False): + try: + return self.config.TMDb.convert_to(tvdb_id, "tvdb_id", False) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(tvdb_id, "tvdb", "tmdb", "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No TMDb ID Found for TVDb ID: {tvdb_id}") + return None + + def convert_tvdb_to_imdb(self, tvdb_id, fail=False): + try: + return self.convert_tmdb_to_imdb(self.convert_tvdb_to_tmdb(tvdb_id), False) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(tvdb_id, "tvdb", "imdb", "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No IMDb ID Found for TVDb ID: {tvdb_id}") + return None + + def convert_imdb_to_tvdb(self, imdb_id, fail=False): + try: + return self.convert_tmdb_to_tvdb(self.convert_imdb_to_tmdb(imdb_id, False)) + except Failed: + if self.config.Trakt: + try: + return self.config.Trakt.convert(imdb_id, "imdb", "tvdb", "show") + except Failed: + pass + if fail: + raise Failed(f"Arms Error: No TVDb ID Found for IMDb ID: {imdb_id}") + return None + + def get_id(self, item, library, length): + expired = None + tmdb_id = None + imdb_id = None + tvdb_id = None + anidb_id = None + mal_id = None + error_message = None + if self.config.Cache: + if library.is_movie: tmdb_id, expired = self.config.Cache.get_tmdb_id("movie", plex_guid=item.guid) + else: tvdb_id, expired = self.config.Cache.get_tvdb_id("show", plex_guid=item.guid) + if not tvdb_id and library.is_show: + tmdb_id, expired = self.config.Cache.get_tmdb_id("show", plex_guid=item.guid) + anidb_id, expired = self.config.Cache.get_anidb_id("show", plex_guid=item.guid) + if expired or (not tmdb_id and library.is_movie) or (not tvdb_id and not tmdb_id and library.is_show): + guid = requests.utils.urlparse(item.guid) + item_type = guid.scheme.split(".")[-1] + check_id = guid.netloc + + if item_type == "plex": + tmdb_id = [] + imdb_id = [] + tvdb_id = [] + if check_id == "movie": + try: + for guid_tag in library.get_guids(item): + url_parsed = requests.utils.urlparse(guid_tag.id) + if url_parsed.scheme == "tmdb": tmdb_id.append(int(url_parsed.netloc)) + elif url_parsed.scheme == "imdb": imdb_id.append(url_parsed.netloc) + except requests.exceptions.ConnectionError: + util.print_stacktrace() + logger.error(f"{'Cache | ! |' if self.config.Cache else 'Mapping Error:'} {item.guid:<46} | No External GUIDs found for {item.title}") + return None, None + elif check_id == "show": + try: + for guid_tag in library.get_guids(item): + url_parsed = requests.utils.urlparse(guid_tag.id) + if url_parsed.scheme == "tvdb": tvdb_id.append(int(url_parsed.netloc)) + elif url_parsed.scheme == "imdb": imdb_id.append(url_parsed.netloc) + elif url_parsed.scheme == "tmdb": tmdb_id.append(int(url_parsed.netloc)) + except requests.exceptions.ConnectionError: + util.print_stacktrace() + logger.error(f"{'Cache | ! |' if self.config.Cache else 'Mapping Error:'} {item.guid:<46} | No External GUIDs found for {item.title}") + return None, None + elif item_type == "imdb": imdb_id = check_id + elif item_type == "thetvdb": tvdb_id = int(check_id) + elif item_type == "themoviedb": tmdb_id = int(check_id) + elif item_type == "hama": + if check_id.startswith("tvdb"): tvdb_id = int(re.search("-(.*)", check_id).group(1)) + elif check_id.startswith("anidb"): anidb_id = re.search("-(.*)", check_id).group(1) + else: error_message = f"Hama Agent ID: {check_id} not supported" + elif item_type == "myanimelist": mal_id = check_id + elif item_type == "local": error_message = "No match in Plex" + else: error_message = f"Agent {item_type} not supported" + + if not error_message: + if mal_id and not anidb_id: + try: anidb_id = self.mal_to_anidb(mal_id) + except Failed: pass + if anidb_id and not tvdb_id: + try: tvdb_id = self.anidb_to_tvdb(anidb_id) + except Failed: pass + if anidb_id and not imdb_id: + try: imdb_id = self.anidb_to_imdb(anidb_id) + except Failed: pass + if not tmdb_id and imdb_id: + if isinstance(imdb_id, list): + tmdb_id = [] + new_imdb_id = [] + for imdb in imdb_id: + try: + tmdb_id.append(self.convert_imdb_to_tmdb(imdb_id, fail=True)) + new_imdb_id.append(imdb) + except Failed: + continue + imdb_id = new_imdb_id + else: + tmdb_id = self.convert_imdb_to_tmdb(imdb_id) + if not tmdb_id and tvdb_id and library.is_show: + tmdb_id = self.convert_tvdb_to_tmdb(tvdb_id) + if not imdb_id and tmdb_id and library.is_movie: + imdb_id = self.convert_tmdb_to_imdb(tmdb_id) + if not imdb_id and tvdb_id and library.is_show: + imdb_id = self.convert_tvdb_to_imdb(tvdb_id) + if not tvdb_id and tmdb_id and library.is_show: + tvdb_id = self.convert_tmdb_to_tvdb(tmdb_id) + if not tvdb_id and imdb_id and library.is_show: + tvdb_id = self.convert_imdb_to_tvdb(imdb_id) + + if (not tmdb_id and library.is_movie) or (not tvdb_id and not (anidb_id and tmdb_id) and library.is_show): + service_name = "TMDb ID" if library.is_movie else "TVDb ID" + + if self.config.Trakt: api_name = "TMDb or Trakt" + else: api_name = "TMDb" + + if tmdb_id and imdb_id: id_name = f"TMDb ID: {tmdb_id} or IMDb ID: {imdb_id}" + elif imdb_id and tvdb_id: id_name = f"IMDb ID: {imdb_id} or TVDb ID: {tvdb_id}" + elif tmdb_id: id_name = f"TMDb ID: {tmdb_id}" + elif imdb_id: id_name = f"IMDb ID: {imdb_id}" + elif tvdb_id: id_name = f"TVDb ID: {tvdb_id}" + else: id_name = None + + if anidb_id and not tmdb_id and not tvdb_id: error_message = f"Unable to convert AniDB ID: {anidb_id} to TMDb ID or TVDb ID" + elif id_name: error_message = f"Unable to convert {id_name} to {service_name} using {api_name}" + else: error_message = f"No ID to convert to {service_name}" + if self.config.Cache and ((tmdb_id and library.is_movie) or ((tvdb_id or (anidb_id and tmdb_id)) and library.is_show)): + if not isinstance(tmdb_id, list): tmdb_id = [tmdb_id] + if not isinstance(imdb_id, list): imdb_id = [imdb_id] + if not isinstance(tvdb_id, list): tvdb_id = [tvdb_id] + try: tvdb_value = tvdb_id[0] + except IndexError: tvdb_value = None + for i in range(len(tmdb_id)): + try: imdb_value = imdb_id[i] + except IndexError: imdb_value = None + util.print_end(length, f"Cache | {'^' if expired is True else '+'} | {item.guid:<46} | {tmdb_id[i] if tmdb_id[i] else 'None':<6} | {imdb_value if imdb_value else 'None':<10} | {tvdb_value if tvdb_value else 'None':<6} | {anidb_id if anidb_id else 'None':<5} | {item.title}") + self.config.Cache.update_guid("movie" if library.is_movie else "show", item.guid, tmdb_id[i], imdb_value, tvdb_value, anidb_id, expired) + + if tmdb_id and library.is_movie: return "movie", tmdb_id + elif tvdb_id and library.is_show: return "show", tvdb_id + elif anidb_id and tmdb_id: return "movie", tmdb_id + else: + util.print_end(length, f"{'Cache | ! |' if self.config.Cache else 'Mapping Error:'} {item.guid:<46} | {error_message} for {item.title}") + return None, None diff --git a/modules/config.py b/modules/config.py index 38ec76b5..f9f67514 100644 --- a/modules/config.py +++ b/modules/config.py @@ -206,7 +206,7 @@ class Config: try: self.tmdb["apikey"] = check_for_attribute(self.data, "apikey", parent="tmdb", throw=True) except Failed as e: raise Failed(e) self.tmdb["language"] = check_for_attribute(self.data, "language", parent="tmdb", default="en") - self.TMDb = TMDbAPI(self.tmdb) + self.TMDb = TMDbAPI(self, self.tmdb) logger.info(f"TMDb Connection {'Failed' if self.TMDb is None else 'Successful'}") else: raise Failed("Config Error: tmdb attribute not found") @@ -755,194 +755,21 @@ class Config: for i, item in enumerate(items, 1): length = util.print_return(length, f"Processing: {i}/{len(items)} {item.title}") try: - id_type, main_id = self.get_id(item, library, length) + id_type, main_id = self.Arms.get_id(item, library, length) except BadRequest: util.print_stacktrace() util.print_end(length, f"{'Cache | ! |' if self.Cache else 'Mapping Error:'} | {item.guid} for {item.title} not found") continue - if isinstance(main_id, list): - if id_type == "movie": - for m in main_id: - if m in movie_map: - movie_map[m].append(item.ratingKey) - else: - movie_map[m] = [item.ratingKey] - elif id_type == "show": - for m in main_id: - if m in show_map: - show_map[m].append(item.ratingKey) - else: - show_map[m] = [item.ratingKey] - else: - if id_type == "movie": - if main_id in movie_map: - movie_map[main_id].append(item.ratingKey) - else: - movie_map[main_id] = [item.ratingKey] - elif id_type == "show": - if main_id in show_map: - show_map[main_id].append(item.ratingKey) - else: - show_map[main_id] = [item.ratingKey] + if not isinstance(main_id, list): + main_id = [main_id] + if id_type == "movie": + for m in main_id: + if m in movie_map: movie_map[m].append(item.ratingKey) + else: movie_map[m] = [item.ratingKey] + elif id_type == "show": + for m in main_id: + if m in show_map: show_map[m].append(item.ratingKey) + else: show_map[m] = [item.ratingKey] util.print_end(length, f"Processed {len(items)} {'Movies' if library.is_movie else 'Shows'}") return movie_map, show_map - @retry(stop_max_attempt_number=6, wait_fixed=10000) - def get_guids(self, item): - return item.guids - - def get_id(self, item, library, length): - expired = None - tmdb_id = None - imdb_id = None - tvdb_id = None - anidb_id = None - mal_id = None - error_message = None - if self.Cache: - if library.is_movie: tmdb_id, expired = self.Cache.get_tmdb_id("movie", plex_guid=item.guid) - else: tvdb_id, expired = self.Cache.get_tvdb_id("show", plex_guid=item.guid) - if not tvdb_id and library.is_show: - tmdb_id, expired = self.Cache.get_tmdb_id("show", plex_guid=item.guid) - anidb_id, expired = self.Cache.get_anidb_id("show", plex_guid=item.guid) - if expired or (not tmdb_id and library.is_movie) or (not tvdb_id and not tmdb_id and library.is_show): - guid = requests.utils.urlparse(item.guid) - item_type = guid.scheme.split(".")[-1] - check_id = guid.netloc - - if item_type == "plex": - tmdb_id = [] - imdb_id = [] - tvdb_id = [] - if check_id == "movie": - try: - for guid_tag in self.get_guids(item): - url_parsed = requests.utils.urlparse(guid_tag.id) - if url_parsed.scheme == "tmdb": tmdb_id.append(int(url_parsed.netloc)) - elif url_parsed.scheme == "imdb": imdb_id.append(url_parsed.netloc) - except requests.exceptions.ConnectionError: - util.print_stacktrace() - logger.error(f"{'Cache | ! |' if self.Cache else 'Mapping Error:'} {item.guid:<46} | No External GUIDs found for {item.title}") - return None, None - elif check_id == "show": - try: - for guid_tag in self.get_guids(item): - url_parsed = requests.utils.urlparse(guid_tag.id) - if url_parsed.scheme == "tvdb": tvdb_id.append(int(url_parsed.netloc)) - elif url_parsed.scheme == "imdb": imdb_id.append(url_parsed.netloc) - elif url_parsed.scheme == "tmdb": tmdb_id.append(int(url_parsed.netloc)) - except requests.exceptions.ConnectionError: - util.print_stacktrace() - logger.error(f"{'Cache | ! |' if self.Cache else 'Mapping Error:'} {item.guid:<46} | No External GUIDs found for {item.title}") - return None, None - elif item_type == "imdb": imdb_id = check_id - elif item_type == "thetvdb": tvdb_id = int(check_id) - elif item_type == "themoviedb": tmdb_id = int(check_id) - elif item_type == "hama": - if check_id.startswith("tvdb"): tvdb_id = int(re.search("-(.*)", check_id).group(1)) - elif check_id.startswith("anidb"): anidb_id = re.search("-(.*)", check_id).group(1) - else: error_message = f"Hama Agent ID: {check_id} not supported" - elif item_type == "myanimelist": mal_id = check_id - elif item_type == "local": error_message = "No match in Plex" - else: error_message = f"Agent {item_type} not supported" - - if not error_message: - if mal_id and not anidb_id: - try: anidb_id = self.Arms.mal_to_anidb(mal_id) - except Failed: pass - if anidb_id and not tvdb_id: - try: tvdb_id = self.Arms.anidb_to_tvdb(anidb_id) - except Failed: pass - if anidb_id and not imdb_id: - try: imdb_id = self.Arms.anidb_to_imdb(anidb_id) - except Failed: pass - if not tmdb_id and imdb_id: - if isinstance(imdb_id, list): - tmdb_id = [] - new_imdb_id = [] - for imdb in imdb_id: - try: - tmdb_id.append(self.TMDb.convert_imdb_to_tmdb(imdb)) - new_imdb_id.append(imdb) - except Failed: - if self.Trakt: - try: - tmdb_id.append(self.Trakt.convert_imdb_to_tmdb(imdb)) - new_imdb_id.append(imdb) - except Failed: - continue - else: - continue - imdb_id = new_imdb_id - else: - try: tmdb_id = self.TMDb.convert_imdb_to_tmdb(imdb_id) - except Failed: pass - if not tmdb_id and self.Trakt: - try: tmdb_id = self.Trakt.convert_imdb_to_tmdb(imdb_id) - except Failed: pass - if not tmdb_id and tvdb_id and library.is_show: - try: tmdb_id = self.TMDb.convert_tvdb_to_tmdb(tvdb_id) - except Failed: pass - if not tmdb_id and self.Trakt: - try: tmdb_id = self.Trakt.convert_tvdb_to_tmdb(tvdb_id) - except Failed: pass - if not imdb_id and tmdb_id and library.is_movie: - try: imdb_id = self.TMDb.convert_tmdb_to_imdb(tmdb_id) - except Failed: pass - if not imdb_id and self.Trakt: - try: imdb_id = self.Trakt.convert_tmdb_to_imdb(tmdb_id) - except Failed: pass - if not imdb_id and tvdb_id and library.is_show: - try: imdb_id = self.TMDb.convert_tvdb_to_imdb(tvdb_id) - except Failed: pass - if not imdb_id and self.Trakt: - try: imdb_id = self.Trakt.convert_tvdb_to_imdb(tvdb_id) - except Failed: pass - if not tvdb_id and library.is_show: - if tmdb_id: - try: tvdb_id = self.TMDb.convert_tmdb_to_tvdb(tmdb_id) - except Failed: pass - if not tvdb_id and self.Trakt: - try: tvdb_id = self.Trakt.convert_tmdb_to_tvdb(tmdb_id) - except Failed: pass - if not tvdb_id and imdb_id: - try: tvdb_id = self.TMDb.convert_imdb_to_tvdb(imdb_id) - except Failed: pass - if not tvdb_id and self.Trakt: - try: tvdb_id = self.Trakt.convert_imdb_to_tvdb(tmdb_id) - except Failed: pass - - if (not tmdb_id and library.is_movie) or (not tvdb_id and not (anidb_id and tmdb_id) and library.is_show): - service_name = "TMDb ID" if library.is_movie else "TVDb ID" - - if self.Trakt: api_name = "TMDb or Trakt" - else: api_name = "TMDb" - - if tmdb_id and imdb_id: id_name = f"TMDb ID: {tmdb_id} or IMDb ID: {imdb_id}" - elif imdb_id and tvdb_id: id_name = f"IMDb ID: {imdb_id} or TVDb ID: {tvdb_id}" - elif tmdb_id: id_name = f"TMDb ID: {tmdb_id}" - elif imdb_id: id_name = f"IMDb ID: {imdb_id}" - elif tvdb_id: id_name = f"TVDb ID: {tvdb_id}" - else: id_name = None - - if anidb_id and not tmdb_id and not tvdb_id: error_message = f"Unable to convert AniDB ID: {anidb_id} to TMDb ID or TVDb ID" - elif id_name: error_message = f"Unable to convert {id_name} to {service_name} using {api_name}" - else: error_message = f"No ID to convert to {service_name}" - if self.Cache and ((tmdb_id and library.is_movie) or ((tvdb_id or (anidb_id and tmdb_id)) and library.is_show)): - if not isinstance(tmdb_id, list): tmdb_id = [tmdb_id] - if not isinstance(imdb_id, list): imdb_id = [imdb_id] - if not isinstance(tvdb_id, list): tvdb_id = [tvdb_id] - try: tvdb_value = tvdb_id[0] - except IndexError: tvdb_value = None - for i in range(len(tmdb_id)): - try: imdb_value = imdb_id[i] - except IndexError: imdb_value = None - util.print_end(length, f"Cache | {'^' if expired is True else '+'} | {item.guid:<46} | {tmdb_id[i] if tmdb_id[i] else 'None':<6} | {imdb_value if imdb_value else 'None':<10} | {tvdb_value if tvdb_value else 'None':<6} | {anidb_id if anidb_id else 'None':<5} | {item.title}") - self.Cache.update_guid("movie" if library.is_movie else "show", item.guid, tmdb_id[i], imdb_value, tvdb_value, anidb_id, expired) - - if tmdb_id and library.is_movie: return "movie", tmdb_id - elif tvdb_id and library.is_show: return "show", tvdb_id - elif anidb_id and tmdb_id: return "movie", tmdb_id - else: - util.print_end(length, f"{'Cache | ! |' if self.Cache else 'Mapping Error:'} {item.guid:<46} | {error_message} for {item.title}") - return None, None diff --git a/modules/plex.py b/modules/plex.py index 7c8e6dfc..a11d7e65 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -1,4 +1,4 @@ -import glob, logging, os, re, requests +import glob, logging, os, requests from datetime import datetime, timedelta from modules import util from modules.meta import Metadata @@ -113,7 +113,7 @@ searches = [ "duration.gt", "duration.gte", "duration.lt", "duration.lte", "user_rating.gt", "user_rating.gte", "user_rating.lt", "user_rating.lte", "critic_rating.gt", "critic_rating.gte", "critic_rating.lt", "critic_rating.lte", - "audience_rating.gt", "audience_rating.gte", "audience_rating.lt", "audience_rating.lte"\ + "audience_rating.gt", "audience_rating.gte", "audience_rating.lt", "audience_rating.lte", "year", "year.not", "year.gt", "year.gte", "year.lt", "year.lte" ] movie_only_searches = [ @@ -398,6 +398,10 @@ class PlexAPI: def collection_order_query(self, collection, data): collection.sortUpdate(sort=data) + @retry(stop_max_attempt_number=6, wait_fixed=10000) + def get_guids(self, item): + return item.guids + @retry(stop_max_attempt_number=6, wait_fixed=10000) def edit_query(self, item, edits, advanced=False): if advanced: @@ -790,311 +794,6 @@ class PlexAPI: util.print_stacktrace() logger.error(f"{item_type}: {name}{' Advanced' if advanced else ''} Details Update Failed") - def update_metadata(self, TMDb, test): - logger.info("") - util.separator(f"{self.name} Library Metadata") - logger.info("") - if not self.metadata: - raise Failed("No metadata to edit") - for mapping_name, meta in self.metadata.items(): - methods = {mm.lower(): mm for mm in meta} - if test and ("test" not in methods or meta[methods["test"]] is not True): - continue - - updated = False - edits = {} - advance_edits = {} - def add_edit(name, current, group, alias, key=None, value=None, var_type="str"): - if value or name in alias: - if value or group[alias[name]]: - if key is None: key = name - if value is None: value = group[alias[name]] - try: - if var_type == "date": - final_value = util.check_date(value, name, return_string=True, plex_date=True) - elif var_type == "float": - final_value = util.check_number(value, name, number_type="float", minimum=0, maximum=10) - else: - final_value = value - if str(current) != str(final_value): - edits[f"{key}.value"] = final_value - edits[f"{key}.locked"] = 1 - logger.info(f"Detail: {name} updated to {final_value}") - except Failed as ee: - logger.error(ee) - else: - logger.error(f"Metadata Error: {name} attribute is blank") - - def add_advanced_edit(attr, obj, group, alias, show_library=False, new_agent=False): - key, options = advance_keys[attr] - if attr in alias: - if new_agent and self.agent not in new_plex_agents: - logger.error(f"Metadata Error: {attr} attribute only works for with the New Plex Movie Agent and New Plex TV Agent") - elif show_library and not self.is_show: - logger.error(f"Metadata Error: {attr} attribute only works for show libraries") - elif group[alias[attr]]: - method_data = str(group[alias[attr]]).lower() - if method_data not in options: - logger.error(f"Metadata Error: {group[alias[attr]]} {attr} attribute invalid") - elif getattr(obj, key) != options[method_data]: - advance_edits[key] = options[method_data] - logger.info(f"Detail: {attr} updated to {method_data}") - else: - logger.error(f"Metadata Error: {attr} attribute is blank") - - def edit_tags(attr, obj, group, alias, key=None, extra=None, movie_library=False): - if key is None: - key = f"{attr}s" - if attr in alias and f"{attr}.sync" in alias: - logger.error(f"Metadata Error: Cannot use {attr} and {attr}.sync together") - elif attr in alias or f"{attr}.sync" in alias: - attr_key = attr if attr in alias else f"{attr}.sync" - if movie_library and not self.is_movie: - logger.error(f"Metadata Error: {attr_key} attribute only works for movie libraries") - elif group[alias[attr_key]] or extra: - item_tags = [item_tag.tag for item_tag in getattr(obj, key)] - input_tags = [] - if group[alias[attr_key]]: - input_tags.extend(util.get_list(group[alias[attr_key]])) - if extra: - input_tags.extend(extra) - if f"{attr}.sync" in alias: - remove_method = getattr(obj, f"remove{attr.capitalize()}") - for tag in (t for t in item_tags if t not in input_tags): - updated = True - remove_method(tag) - logger.info(f"Detail: {attr.capitalize()} {tag} removed") - add_method = getattr(obj, f"add{attr.capitalize()}") - for tag in (t for t in input_tags if t not in item_tags): - updated = True - add_method(tag) - logger.info(f"Detail: {attr.capitalize()} {tag} added") - else: - logger.error(f"Metadata Error: {attr} attribute is blank") - - def set_image(attr, obj, group, alias, poster=True, url=True): - if group[alias[attr]]: - message = f"{'poster' if poster else 'background'} to [{'URL' if url else 'File'}] {group[alias[attr]]}" - self.upload_image(obj, group[alias[attr]], poster=poster, url=url) - logger.info(f"Detail: {attr} updated {message}") - else: - logger.error(f"Metadata Error: {attr} attribute is blank") - - def set_images(obj, group, alias): - if "url_poster" in alias: - set_image("url_poster", obj, group, alias) - elif "file_poster" in alias: - set_image("file_poster", obj, group, alias, url=False) - if "url_background" in alias: - set_image("url_background", obj, group, alias, poster=False) - elif "file_background" in alias: - set_image("file_background", obj, group, alias, poster=False, url=False) - - logger.info("") - util.separator() - logger.info("") - year = None - if "year" in methods: - year = util.check_number(meta[methods["year"]], "year", minimum=1800, maximum=datetime.now().year + 1) - - title = mapping_name - if "title" in methods: - if meta[methods["title"]] is None: logger.error("Metadata Error: title attribute is blank") - else: title = meta[methods["title"]] - - item = self.search_item(title, year=year) - - if item is None: - item = self.search_item(f"{title} (SUB)", year=year) - - if item is None and "alt_title" in methods: - if meta[methods["alt_title"]] is None: - logger.error("Metadata Error: alt_title attribute is blank") - else: - alt_title = meta["alt_title"] - item = self.search_item(alt_title, year=year) - - if item is None: - logger.error(f"Plex Error: Item {mapping_name} not found") - logger.error(f"Skipping {mapping_name}") - continue - - item_type = "Movie" if self.is_movie else "Show" - logger.info(f"Updating {item_type}: {title}...") - - tmdb_item = None - tmdb_is_movie = None - if ("tmdb_show" in methods or "tmdb_id" in methods) and "tmdb_movie" in methods: - logger.error("Metadata Error: Cannot use tmdb_movie and tmdb_show when editing the same metadata item") - - if "tmdb_show" in methods or "tmdb_id" in methods or "tmdb_movie" in methods: - try: - if "tmdb_show" in methods or "tmdb_id" in methods: - data = meta[methods["tmdb_show" if "tmdb_show" in methods else "tmdb_id"]] - if data is None: - logger.error("Metadata Error: tmdb_show attribute is blank") - else: - tmdb_is_movie = False - tmdb_item = TMDb.get_show(util.regex_first_int(data, "Show")) - elif "tmdb_movie" in methods: - if meta[methods["tmdb_movie"]] is None: - logger.error("Metadata Error: tmdb_movie attribute is blank") - else: - tmdb_is_movie = True - tmdb_item = TMDb.get_movie(util.regex_first_int(meta[methods["tmdb_movie"]], "Movie")) - except Failed as e: - logger.error(e) - - originally_available = None - original_title = None - rating = None - studio = None - tagline = None - summary = None - genres = [] - if tmdb_item: - originally_available = tmdb_item.release_date if tmdb_is_movie else tmdb_item.first_air_date - if tmdb_item and tmdb_is_movie is True and tmdb_item.original_title != tmdb_item.title: - original_title = tmdb_item.original_title - elif tmdb_item and tmdb_is_movie is False and tmdb_item.original_name != tmdb_item.name: - original_title = tmdb_item.original_name - rating = tmdb_item.vote_average - if tmdb_is_movie is True and tmdb_item.production_companies: - studio = tmdb_item.production_companies[0].name - elif tmdb_is_movie is False and tmdb_item.networks: - studio = tmdb_item.networks[0].name - tagline = tmdb_item.tagline if len(tmdb_item.tagline) > 0 else None - summary = tmdb_item.overview - genres = [genre.name for genre in tmdb_item.genres] - - edits = {} - add_edit("title", item.title, meta, methods, value=title) - add_edit("sort_title", item.titleSort, meta, methods, key="titleSort") - add_edit("originally_available", str(item.originallyAvailableAt)[:-9], meta, methods, key="originallyAvailableAt", value=originally_available, var_type="date") - add_edit("critic_rating", item.rating, meta, methods, value=rating, key="rating", var_type="float") - add_edit("audience_rating", item.audienceRating, meta, methods, key="audienceRating", var_type="float") - add_edit("content_rating", item.contentRating, meta, methods, key="contentRating") - add_edit("original_title", item.originalTitle, meta, methods, key="originalTitle", value=original_title) - add_edit("studio", item.studio, meta, methods, value=studio) - add_edit("tagline", item.tagline, meta, methods, value=tagline) - add_edit("summary", item.summary, meta, methods, value=summary) - self.edit_item(item, mapping_name, item_type, edits) - - advance_edits = {} - add_advanced_edit("episode_sorting", item, meta, methods, show_library=True) - add_advanced_edit("keep_episodes", item, meta, methods, show_library=True) - add_advanced_edit("delete_episodes", item, meta, methods, show_library=True) - add_advanced_edit("season_display", item, meta, methods, show_library=True) - add_advanced_edit("episode_ordering", item, meta, methods, show_library=True) - add_advanced_edit("metadata_language", item, meta, methods, new_agent=True) - add_advanced_edit("use_original_title", item, meta, methods, new_agent=True) - self.edit_item(item, mapping_name, item_type, advance_edits, advanced=True) - - edit_tags("genre", item, meta, methods, extra=genres) - edit_tags("label", item, meta, methods) - edit_tags("collection", item, meta, methods) - edit_tags("country", item, meta, methods, key="countries", movie_library=True) - edit_tags("director", item, meta, methods, movie_library=True) - edit_tags("producer", item, meta, methods, movie_library=True) - edit_tags("writer", item, meta, methods, movie_library=True) - - logger.info(f"{item_type}: {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") - - set_images(item, meta, methods) - - if "seasons" in methods and self.is_show: - if meta[methods["seasons"]]: - for season_id in meta[methods["seasons"]]: - updated = False - logger.info("") - logger.info(f"Updating season {season_id} of {mapping_name}...") - if isinstance(season_id, int): - season = None - for s in item.seasons(): - if s.index == season_id: - season = s - break - if season is None: - logger.error(f"Metadata Error: Season: {season_id} not found") - else: - season_dict = meta[methods["seasons"]][season_id] - season_methods = {sm.lower(): sm for sm in season_dict} - - if "title" in season_methods and season_dict[season_methods["title"]]: - title = season_dict[season_methods["title"]] - else: - title = season.title - if "sub" in season_methods: - if season_dict[season_methods["sub"]] is None: - logger.error("Metadata Error: sub attribute is blank") - elif season_dict[season_methods["sub"]] is True and "(SUB)" not in title: - title = f"{title} (SUB)" - elif season_dict[season_methods["sub"]] is False and title.endswith(" (SUB)"): - title = title[:-6] - else: - logger.error("Metadata Error: sub attribute must be True or False") - - edits = {} - add_edit("title", season.title, season_dict, season_methods, value=title) - add_edit("summary", season.summary, season_dict, season_methods) - self.edit_item(season, season_id, "Season", edits) - set_images(season, season_dict, season_methods) - else: - logger.error(f"Metadata Error: Season: {season_id} invalid, it must be an integer") - logger.info(f"Season {season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") - else: - logger.error("Metadata Error: seasons attribute is blank") - elif "seasons" in methods: - logger.error("Metadata Error: seasons attribute only works for show libraries") - - if "episodes" in methods and self.is_show: - if meta[methods["episodes"]]: - for episode_str in meta[methods["episodes"]]: - updated = False - logger.info("") - match = re.search("[Ss]\\d+[Ee]\\d+", episode_str) - if match: - output = match.group(0)[1:].split("E" if "E" in match.group(0) else "e") - season_id = int(output[0]) - episode_id = int(output[1]) - logger.info(f"Updating episode S{season_id}E{episode_id} of {mapping_name}...") - try: episode = item.episode(season=season_id, episode=episode_id) - except NotFound: logger.error(f"Metadata Error: episode {episode_id} of season {season_id} not found") - else: - episode_dict = meta[methods["episodes"]][episode_str] - episode_methods = {em.lower(): em for em in episode_dict} - - if "title" in episode_methods and episode_dict[episode_methods["title"]]: - title = episode_dict[episode_methods["title"]] - else: - title = episode.title - if "sub" in episode_dict: - if episode_dict[episode_methods["sub"]] is None: - logger.error("Metadata Error: sub attribute is blank") - elif episode_dict[episode_methods["sub"]] is True and "(SUB)" not in title: - title = f"{title} (SUB)" - elif episode_dict[episode_methods["sub"]] is False and title.endswith(" (SUB)"): - title = title[:-6] - else: - logger.error("Metadata Error: sub attribute must be True or False") - edits = {} - add_edit("title", episode.title, episode_dict, episode_methods, value=title) - add_edit("sort_title", episode.titleSort, episode_dict, episode_methods, key="titleSort") - add_edit("rating", episode.rating, episode_dict, episode_methods) - add_edit("originally_available", str(episode.originallyAvailableAt)[:-9], episode_dict, episode_methods, key="originallyAvailableAt") - add_edit("summary", episode.summary, episode_dict, episode_methods) - self.edit_item(episode, f"{season_id} Episode: {episode_id}", "Season", edits) - edit_tags("director", episode, episode_dict, episode_methods) - edit_tags("writer", episode, episode_dict, episode_methods) - set_images(episode, episode_dict, episode_methods) - logger.info(f"Episode S{episode_id}E{season_id} of {mapping_name} Details Update {'Complete' if updated else 'Not Needed'}") - else: - logger.error(f"Metadata Error: episode {episode_str} invalid must have S##E## format") - else: - logger.error("Metadata Error: episodes attribute is blank") - elif "episodes" in methods: - logger.error("Metadata Error: episodes attribute only works for show libraries") - def update_item_from_assets(self, item, dirs=None): if dirs is None: dirs = self.asset_directory diff --git a/modules/tmdb.py b/modules/tmdb.py index cd93840a..bb8f5fa2 100644 --- a/modules/tmdb.py +++ b/modules/tmdb.py @@ -109,7 +109,8 @@ discover_tv_sort = [ ] class TMDbAPI: - def __init__(self, params): + def __init__(self, config, params): + self.config = config self.TMDb = tmdbv3api.TMDb() self.TMDb.api_key = params["apikey"] self.TMDb.language = params["language"] @@ -131,7 +132,7 @@ class TMDbAPI: self.image_url = "https://image.tmdb.org/t/p/original" @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) - def _from_tmdb(self, tmdb_id, convert_to, is_movie): + def convert_from(self, tmdb_id, convert_to, is_movie): try: id_to_return = self.Movie.external_ids(tmdb_id)[convert_to] if is_movie else self.TV.external_ids(tmdb_id)[convert_to] if not id_to_return or (convert_to == "tvdb_id" and id_to_return == 0): @@ -141,19 +142,12 @@ class TMDbAPI: raise Failed(f"TMDb Error: {'Movie' if is_movie else 'Show'} TMDb ID: {tmdb_id} not found") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) - def _to_tmdb(self, external_id, external_source, is_movie): + def convert_to(self, external_id, external_source, is_movie): search_results = self.Movie.external(external_id=external_id, external_source=external_source) search = search_results["movie_results" if is_movie else "tv_results"] if len(search) == 1: return int(search[0]["id"]) else: raise Failed(f"TMDb Error: No TMDb ID found for {external_source.upper().replace('B_', 'b ')} {external_id}") - def convert_tmdb_to_imdb(self, tmdb_id, is_movie=True): return self._from_tmdb(tmdb_id, "imdb_id", is_movie) - def convert_imdb_to_tmdb(self, imdb_id, is_movie=True): return self._to_tmdb(imdb_id, "imdb_id", is_movie) - def convert_tmdb_to_tvdb(self, tmdb_id): return self._from_tmdb(tmdb_id, "tvdb_id", False) - def convert_tvdb_to_tmdb(self, tvdb_id): return self._to_tmdb(tvdb_id, "tvdb_id", False) - def convert_tvdb_to_imdb(self, tvdb_id): return self.convert_tmdb_to_imdb(self.convert_tvdb_to_tmdb(tvdb_id), False) - def convert_imdb_to_tvdb(self, imdb_id): return self.convert_tmdb_to_tvdb(self.convert_imdb_to_tmdb(imdb_id, False)) - def get_movie_show_or_collection(self, tmdb_id, is_movie): if is_movie: try: return self.get_collection(tmdb_id) @@ -217,7 +211,7 @@ class TMDbAPI: movie_ids.append(credit.id) elif credit.media_type == "tv": try: - show_ids.append(self.convert_tmdb_to_tvdb(credit.id)) + show_ids.append(self.config.Arms.convert_tmdb_to_tvdb(credit.id)) except Failed as e: logger.warning(e) for credit in actor_credits.crew: @@ -229,7 +223,7 @@ class TMDbAPI: movie_ids.append(credit.id) elif credit.media_type == "tv": try: - show_ids.append(self.convert_tmdb_to_tvdb(credit.id)) + show_ids.append(self.config.Arms.convert_tmdb_to_tvdb(credit.id)) except Failed as e: logger.warning(e) return movie_ids, show_ids @@ -246,7 +240,7 @@ class TMDbAPI: else: raise Failed(f"TMDb Error: {method} method not supported") for tmdb_item in tmdb_items: try: - ids.append(tmdb_item.id if is_movie else self.convert_tmdb_to_tvdb(tmdb_item.id)) + ids.append(tmdb_item.id if is_movie else self.config.Arms.convert_tmdb_to_tvdb(tmdb_item.id)) count += 1 except Failed: pass @@ -269,7 +263,7 @@ class TMDbAPI: tmdb_items = self.Discover.discover_movies(attrs) if is_movie else self.Discover.discover_tv_shows(attrs) for tmdb_item in tmdb_items: try: - ids.append(tmdb_item.id if is_movie else self.convert_tmdb_to_tvdb(tmdb_item.id)) + ids.append(tmdb_item.id if is_movie else self.config.Arms.convert_tmdb_to_tvdb(tmdb_item.id)) count += 1 except Failed as e: logger.error(e) @@ -345,7 +339,7 @@ class TMDbAPI: if tmdb_item.media_type == "movie": movie_ids.append(tmdb_item.id) elif tmdb_item.media_type == "tv": - try: show_ids.append(self.convert_tmdb_to_tvdb(tmdb_item.id)) + try: show_ids.append(self.config.Arms.convert_tmdb_to_tvdb(tmdb_item.id)) except Failed: pass elif method == "tmdb_movie": tmdb_name = str(self.get_movie(tmdb_id).title) @@ -357,7 +351,7 @@ class TMDbAPI: movie_ids.append(tmdb_item["id"]) elif method == "tmdb_show": tmdb_name = str(self.get_show(tmdb_id).name) - show_ids.append(self.convert_tmdb_to_tvdb(tmdb_id)) + show_ids.append(self.config.Arms.convert_tmdb_to_tvdb(tmdb_id)) else: tmdb_name = str(self.get_person(tmdb_id).name) if method == "tmdb_actor": movie_ids, show_ids = self._credits(tmdb_id, actor=True) diff --git a/modules/trakttv.py b/modules/trakttv.py index e2273a0b..7dda9ee7 100644 --- a/modules/trakttv.py +++ b/modules/trakttv.py @@ -90,15 +90,8 @@ class TraktAPI: return True return False - def convert_tmdb_to_imdb(self, tmdb_id, is_movie=True): return self._convert(tmdb_id, "tmdb", "imdb", "movie" if is_movie else "show") - def convert_imdb_to_tmdb(self, imdb_id, is_movie=True): return self._convert(imdb_id, "imdb", "tmdb", "movie" if is_movie else "show") - def convert_tmdb_to_tvdb(self, tmdb_id): return self._convert(tmdb_id, "tmdb", "tvdb", "show") - def convert_tvdb_to_tmdb(self, tvdb_id): return self._convert(tvdb_id, "tvdb", "tmdb", "show") - def convert_tvdb_to_imdb(self, tvdb_id): return self._convert(tvdb_id, "tvdb", "imdb", "show") - def convert_imdb_to_tvdb(self, imdb_id): return self._convert(imdb_id, "imdb", "tvdb", "show") - @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) - def _convert(self, external_id, from_source, to_source, media_type): + def convert(self, external_id, from_source, to_source, media_type): lookup = Trakt["search"].lookup(external_id, from_source, media_type) if lookup: lookup = lookup[0] if isinstance(lookup, list) else lookup