diff --git a/.gitignore b/.gitignore index e8f099c1..0395c554 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ *.so # Distribution / packaging +.idea .Python /modules/test.py logs/ diff --git a/modules/anidb.py b/modules/anidb.py index 72fe6c3a..292c753a 100644 --- a/modules/anidb.py +++ b/modules/anidb.py @@ -25,7 +25,7 @@ class AniDBAPI: def convert_anidb(self, input_id, from_id, to_id): ids = self.id_list.xpath("//anime[contains(@{}, '{}')]/@{}".format(from_id, input_id, to_id)) if len(ids) > 0: - if from_id == "tvdbid": return [int(id) for id in ids] + if from_id == "tvdbid": return [int(i) for i in ids] if len(ids[0]) > 0: try: return ids[0].split(",") if to_id == "imdbid" else int(ids[0]) except ValueError: raise Failed("AniDB Error: No {} ID found for {} ID: {}".format(util.pretty_ids[to_id], util.pretty_ids[from_id], input_id)) @@ -80,7 +80,7 @@ class AniDBAPI: movie_ids = [] for anidb_id in anime_ids: try: - tmdb_id = self.convert_from_imdb(self.convert_anidb_to_imdb(anidb_id), language) + tmdb_id = self.convert_from_imdb(self.convert_anidb_to_imdb(anidb_id)) if tmdb_id: movie_ids.append(tmdb_id) else: raise Failed except Failed: @@ -92,15 +92,15 @@ class AniDBAPI: logger.debug("TVDb IDs Found: {}".format(show_ids)) return movie_ids, show_ids - def convert_from_imdb(self, imdb_id, language): + def convert_from_imdb(self, imdb_id): output_tmdb_ids = [] if not isinstance(imdb_id, list): imdb_id = [imdb_id] for imdb in imdb_id: + expired = False if self.Cache: tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb) - expired = False if not tmdb_id: tmdb_id, expired = self.Cache.get_tmdb_from_imdb(imdb) if expired: diff --git a/modules/builder.py b/modules/builder.py index 4f953a6a..b3e594fe 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -61,17 +61,14 @@ class CollectionBuilder: else: raise Failed("Collection Error: template sub-attribute default is blank") - - for m in template: if m not in self.data and m != "default": if template[m]: - attr = None def replace_txt(txt): txt = str(txt) - for tm in data_template: - if tm != "name" and "<<{}>>".format(tm) in txt: - txt = txt.replace("<<{}>>".format(tm), str(data_template[tm])) + for template_method in data_template: + if template_method != "name" and "<<{}>>".format(template_method) in txt: + txt = txt.replace("<<{}>>".format(template_method), str(data_template[template_method])) if "<>" in txt: txt = txt.replace("<>", str(self.name)) for dm in default: @@ -134,7 +131,7 @@ class CollectionBuilder: if weekday == current_time.weekday(): skip_collection = False else: - logger.error("Collection Error: weekly schedule attribute {} invalid must be a day of the weeek i.e. weekly(Monday)".format(schedule)) + logger.error("Collection Error: weekly schedule attribute {} invalid must be a day of the week i.e. weekly(Monday)".format(schedule)) elif run_time.startswith("month"): try: if 1 <= int(param) <= 31: @@ -162,7 +159,7 @@ class CollectionBuilder: if self.schedule is None: skip_collection = False if skip_collection: - raise Failed("Skipping Collection {}".format(c)) + raise Failed("Skipping Collection {}".format(self.name)) logger.info("Scanning {} Collection".format(self.name)) @@ -293,7 +290,6 @@ class CollectionBuilder: elif method_name == "imdb_list": new_list = [] for imdb_list in util.get_list(data[m], split=False): - new_dictionary = {} if isinstance(imdb_list, dict): if "url" in imdb_list and imdb_list["url"]: imdb_url = imdb_list["url"] else: raise Failed("Collection Error: imdb_list attribute url is required") @@ -305,25 +301,25 @@ class CollectionBuilder: self.methods.append((method_name, new_list)) elif method_name in util.dictionary_lists: if isinstance(data[m], dict): - def get_int(parent, method, data, default, min=1, max=None): - if method not in data: logger.warning("Collection Warning: {} {} attribute not found using {} as default".format(parent, method, default)) - elif not data[method]: logger.warning("Collection Warning: {} {} attribute is blank using {} as default".format(parent, method, default)) - elif isinstance(data[method], int) and data[method] >= min: - if max is None or data[method] <= max: return data[method] - else: logger.warning("Collection Warning: {} {} attribute {} invalid must an integer <= {} using {} as default".format(parent, method, data[method], max, default)) - else: logger.warning("Collection Warning: {} {} attribute {} invalid must an integer >= {} using {} as default".format(parent, method, data[method], min, default)) - return default + def get_int(parent, method, data_in, default_in, minimum=1, maximum=None): + if method not in data_in: logger.warning("Collection Warning: {} {} attribute not found using {} as default".format(parent, method, default)) + elif not data_in[method]: logger.warning("Collection Warning: {} {} attribute is blank using {} as default".format(parent, method, default)) + elif isinstance(data_in[method], int) and data_in[method] >= minimum: + if maximum is None or data_in[method] <= maximum: return data_in[method] + else: logger.warning("Collection Warning: {} {} attribute {} invalid must an integer <= {} using {} as default".format(parent, method, data_in[method], maximum, default)) + else: logger.warning("Collection Warning: {} {} attribute {} invalid must an integer >= {} using {} as default".format(parent, method, data_in[method], minimum, default)) + return default_in if method_name == "filters": for f in data[m]: if f in util.method_alias or (f.endswith(".not") and f[:-4] in util.method_alias): - filter = (util.method_alias[f[:-4]] + f[-4:]) if f.endswith(".not") else util.method_alias[f] - logger.warning("Collection Warning: {} filter will run as {}".format(f, filter)) + filter_method = (util.method_alias[f[:-4]] + f[-4:]) if f.endswith(".not") else util.method_alias[f] + logger.warning("Collection Warning: {} filter will run as {}".format(f, filter_method)) else: - filter = f - if filter in util.movie_only_filters and self.library.is_show: raise Failed("Collection Error: {} filter only works for movie libraries".format(filter)) - elif data[m][f] is None: raise Failed("Collection Error: {} filter is blank".format(filter)) - elif filter in util.all_filters: self.filters.append((filter, data[m][f])) - else: raise Failed("Collection Error: {} filter not supported".format(filter)) + filter_method = f + if filter_method in util.movie_only_filters and self.library.is_show: raise Failed("Collection Error: {} filter only works for movie libraries".format(filter_method)) + elif data[m][f] is None: raise Failed("Collection Error: {} filter is blank".format(filter_method)) + elif filter_method in util.all_filters: self.filters.append((filter_method, data[m][f])) + else: raise Failed("Collection Error: {} filter not supported".format(filter_method)) elif method_name == "plex_collectionless": new_dictionary = {} prefix_list = [] @@ -375,12 +371,12 @@ class CollectionBuilder: if re.compile("([a-z]{2})-([A-Z]{2})").match(str(attr_data)): new_dictionary[attr] = str(attr_data) else: - raise Failed("Collection Error: {} attribute {}: {} must match pattern ([a-z]{2})-([A-Z]{2}) e.g. en-US".format(m, attr, attr_data)) + raise Failed("Collection Error: {} attribute {}: {} must match pattern ([a-z]{{2}})-([A-Z]{{2}}) e.g. en-US".format(m, attr, attr_data)) elif attr == "region": if re.compile("^[A-Z]{2}$").match(str(attr_data)): new_dictionary[attr] = str(attr_data) else: - raise Failed("Collection Error: {} attribute {}: {} must match pattern ^[A-Z]{2}$ e.g. US".format(m, attr, attr_data)) + raise Failed("Collection Error: {} attribute {}: {} must match pattern ^[A-Z]{{2}}$ e.g. US".format(m, attr, attr_data)) elif attr == "sort_by": if (self.library.is_movie and attr_data in util.discover_movie_sort) or (self.library.is_show and attr_data in util.discover_tv_sort): new_dictionary[attr] = attr_data @@ -409,7 +405,7 @@ class CollectionBuilder: else: raise Failed("Collection Error: {} attribute {}: {} must match pattern MM/DD/YYYY e.g. 12/25/2020".format(m, attr, attr_data)) elif attr in ["primary_release_year", "year", "first_air_date_year"]: - if isinstance(attr_data, int) and 1800 < attr_data and attr_data < 2200: + if isinstance(attr_data, int) and 1800 < attr_data < 2200: new_dictionary[attr] = attr_data else: raise Failed("Collection Error: {} attribute {}: must be a valid year e.g. 1990".format(m, attr)) @@ -463,8 +459,8 @@ class CollectionBuilder: elif data[m]["season"] not in util.pretty_seasons: logger.warning("Collection Warning: mal_season season attribute {} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {} as default".format(data[m]["season"], new_dictionary["season"])) else: new_dictionary["season"] = data[m]["season"] - new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, min=1917, max=current_time.year + 1) - new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, max=500) + 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], 100, maximum=500) self.methods.append((method_name, [new_dictionary])) elif method_name == "mal_userlist": new_dictionary = {"status": "all", "sort_by": "list_score"} @@ -482,7 +478,7 @@ class CollectionBuilder: elif data[m]["sort_by"] not in util.mal_userlist_sort: logger.warning("Collection Warning: mal_season sort_by attribute {} invalid must be either 'score', 'last_updated', 'title' or 'start_date' using score as default".format(data[m]["sort_by"])) else: new_dictionary["sort_by"] = util.mal_userlist_sort[data[m]["sort_by"]] - new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, max=1000) + new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, maximum=1000) self.methods.append((method_name, [new_dictionary])) else: raise Failed("Collection Error: {} attribute is not a dictionary: {}".format(m, data[m])) @@ -523,7 +519,7 @@ class CollectionBuilder: if self.library.Sonarr: self.do_arr = self.details["add_to_arr"] if "add_to_arr" in self.details else self.library.Sonarr.add - def run_methods(self, collection_obj, collection_name, map, movie_map, show_map): + def run_methods(self, collection_obj, collection_name, rating_key_map, movie_map, show_map): items_found = 0 for method, values in self.methods: logger.debug("") @@ -559,7 +555,6 @@ class CollectionBuilder: items_found += len(items) elif method == "plex_search": search_terms = {} - output = "" for i, attr_pair in enumerate(value): search_list = attr_pair[1] final_method = attr_pair[0][:-4] + "!" if attr_pair[0][-4:] == ".not" else attr_pair[0] @@ -611,7 +606,7 @@ class CollectionBuilder: elif "trakt" in method: items_found += check_map(self.config.Trakt.get_items(method, value, self.library.is_movie)) else: logger.error("Collection Error: {} method not supported".format(method)) - if len(items) > 0: map = self.library.add_to_collection(collection_obj if collection_obj else collection_name, items, self.filters, self.details["show_filtered"], map, movie_map, show_map) + if len(items) > 0: rating_key_map = self.library.add_to_collection(collection_obj if collection_obj else collection_name, items, self.filters, self.details["show_filtered"], rating_key_map, movie_map, show_map) else: logger.error("No items found to add to this collection ") if len(missing_movies) > 0 or len(missing_shows) > 0: @@ -662,7 +657,7 @@ class CollectionBuilder: if self.sync and items_found > 0: logger.info("") count_removed = 0 - for ratingKey, item in map.items(): + for ratingKey, item in rating_key_map.items(): if item is not None: logger.info("{} Collection | - | {}".format(collection_name, item.title)) item.removeCollection(collection_name) @@ -695,10 +690,10 @@ class CollectionBuilder: item_labels = [label.tag for label in collection.labels] labels = util.get_list(self.details["label"]) if "label_sync_mode" in self.details and self.details["label_sync_mode"] == "sync": - for label in (l for l in item_labels if l not in labels): + for label in (la for la in item_labels if la not in labels): collection.removeLabel(label) logger.info("Detail: Label {} removed".format(label)) - for label in (l for l in labels if l not in item_labels): + for label in (la for la in labels if la not in item_labels): collection.addLabel(label) logger.info("Detail: Label {} added".format(label)) diff --git a/modules/config.py b/modules/config.py index 47945ac8..66e8de47 100644 --- a/modules/config.py +++ b/modules/config.py @@ -1,4 +1,4 @@ -import glob, logging, os, re, requests +import logging, os, re, requests from modules import util from modules.anidb import AniDBAPI from modules.builder import CollectionBuilder @@ -11,7 +11,7 @@ from modules.radarr import RadarrAPI from modules.sonarr import SonarrAPI from modules.tautulli import TautulliAPI from modules.tmdb import TMDbAPI -from modules.trakt import TraktAPI +from modules.trakttv import TraktAPI from modules.tvdb import TVDbAPI from modules.util import Failed from plexapi.exceptions import BadRequest @@ -77,7 +77,6 @@ class Config: raise Failed("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) def check_for_attribute(data, attribute, parent=None, test_list=None, options="", default=None, do_print=True, default_is_none=False, req_default=False, var_type="str", throw=False, save=True): - message = "" endline = "" if parent is not None: if parent in data: @@ -90,14 +89,13 @@ class Config: if data is None or attribute not in data: message = "{} not found".format(text) if parent and save is True: - new_config, ind, bsi = yaml.util.load_yaml_guess_indent(open(self.config_path)) + loaded_config, ind_in, bsi_in = yaml.util.load_yaml_guess_indent(open(self.config_path)) endline = "\n{} sub-attribute {} added to config".format(parent, attribute) - if parent not in new_config: new_config = {parent: {attribute: default}} - elif not new_config[parent]: new_config[parent] = {attribute: default} - elif attribute not in new_config[parent]: new_config[parent][attribute] = default - else: endLine = "" - yaml.round_trip_dump(new_config, open(self.config_path, "w"), indent=ind, block_seq_indent=bsi) - elif not data[attribute] and data[attribute] != False: + if parent not in loaded_config or not loaded_config[parent]: loaded_config[parent] = {attribute: default} + elif attribute not in loaded_config[parent]: loaded_config[parent][attribute] = default + else: endline = "" + yaml.round_trip_dump(loaded_config, open(self.config_path, "w"), indent=ind_in, block_seq_indent=bsi_in) + elif not data[attribute] and data[attribute] is not False: if default_is_none is True: return None else: message = "{} is blank".format(text) elif var_type == "bool": @@ -110,11 +108,11 @@ class Config: if os.path.exists(os.path.abspath(data[attribute])): return data[attribute] else: message = "Path {} does not exist".format(os.path.abspath(data[attribute])) elif var_type == "list": return util.get_list(data[attribute]) - elif var_type == "listpath": + elif var_type == "list_path": temp_list = [path for path in util.get_list(data[attribute], split=True) if os.path.exists(os.path.abspath(path))] if len(temp_list) > 0: return temp_list else: message = "No Paths exist" - elif var_type == "lowerlist": return util.get_list(data[attribute], lower=True) + elif var_type == "lower_list": return util.get_list(data[attribute], lower=True) elif test_list is None or data[attribute] in test_list: return data[attribute] else: message = "{}: {} is an invalid input".format(text, data[attribute]) if var_type == "path" and default and os.path.exists(os.path.abspath(default)): @@ -144,18 +142,18 @@ class Config: self.general["cache"] = check_for_attribute(self.data, "cache", parent="settings", options=" true (Create a cache to store ids)\n false (Do not create a cache to store ids)", var_type="bool", default=True) self.general["cache_expiration"] = check_for_attribute(self.data, "cache_expiration", parent="settings", var_type="int", default=60) if self.general["cache"]: - util.seperator() + util.separator() self.Cache = Cache(self.config_path, self.general["cache_expiration"]) else: self.Cache = None - self.general["asset_directory"] = check_for_attribute(self.data, "asset_directory", parent="settings", var_type="listpath", default=[os.path.join(default_dir, "assets")]) + self.general["asset_directory"] = check_for_attribute(self.data, "asset_directory", parent="settings", var_type="list_path", default=[os.path.join(default_dir, "assets")]) self.general["sync_mode"] = check_for_attribute(self.data, "sync_mode", parent="settings", default="append", test_list=["append", "sync"], options=" append (Only Add Items to the Collection)\n sync (Add & Remove Items from the Collection)") self.general["show_unmanaged"] = check_for_attribute(self.data, "show_unmanaged", parent="settings", var_type="bool", default=True) self.general["show_filtered"] = check_for_attribute(self.data, "show_filtered", parent="settings", var_type="bool", default=False) self.general["show_missing"] = check_for_attribute(self.data, "show_missing", parent="settings", var_type="bool", default=True) self.general["save_missing"] = check_for_attribute(self.data, "save_missing", parent="settings", var_type="bool", default=True) - util.seperator() + util.separator() self.TMDb = None if "tmdb" in self.data: @@ -169,7 +167,7 @@ class Config: else: raise Failed("Config Error: tmdb attribute not found") - util.seperator() + util.separator() self.Trakt = None if "trakt" in self.data: @@ -187,7 +185,7 @@ class Config: else: logger.warning("trakt attribute not found") - util.seperator() + util.separator() self.MyAnimeList = None self.MyAnimeListIDList = MyAnimeListIDList() @@ -210,7 +208,7 @@ class Config: self.IMDb = IMDbAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt, TVDb=self.TVDb) if self.TMDb or self.Trakt else None self.AniDB = AniDBAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt) - util.seperator() + util.separator() logger.info("Connecting to Plex Libraries...") @@ -227,7 +225,7 @@ class Config: self.general["radarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True) self.general["radarr"]["add"] = check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False) self.general["radarr"]["search"] = check_for_attribute(self.data, "search", parent="radarr", var_type="bool", default=False) - self.general["radarr"]["tag"] = check_for_attribute(self.data, "tag", parent="radarr", var_type="lowerlist", default_is_none=True) + self.general["radarr"]["tag"] = check_for_attribute(self.data, "tag", parent="radarr", var_type="lower_list", default_is_none=True) self.general["sonarr"] = {} self.general["sonarr"]["url"] = check_for_attribute(self.data, "url", parent="sonarr", default_is_none=True) @@ -237,7 +235,7 @@ class Config: self.general["sonarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True) self.general["sonarr"]["add"] = check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False) self.general["sonarr"]["search"] = check_for_attribute(self.data, "search", parent="sonarr", var_type="bool", default=False) - self.general["sonarr"]["tag"] = check_for_attribute(self.data, "tag", parent="sonarr", var_type="lowerlist", default_is_none=True) + self.general["sonarr"]["tag"] = check_for_attribute(self.data, "tag", parent="sonarr", var_type="lower_list", default_is_none=True) self.general["tautulli"] = {} self.general["tautulli"]["url"] = check_for_attribute(self.data, "url", parent="tautulli", default_is_none=True) @@ -247,7 +245,7 @@ class Config: try: libs = check_for_attribute(self.data, "libraries", throw=True) except Failed as e: raise Failed(e) for lib in libs: - util.seperator() + util.separator() params = {} if "library_name" in libs[lib] and libs[lib]["library_name"]: params["name"] = str(libs[lib]["library_name"]) @@ -255,9 +253,8 @@ class Config: else: params["name"] = str(lib) logger.info("Connecting to {} Library...".format(params["name"])) - default_lib = os.path.join(default_dir, "{}.yml".format(lib)) - params["asset_directory"] = check_for_attribute(libs[lib], "asset_directory", parent="settings", var_type="listpath", default=self.general["asset_directory"], default_is_none=True, save=False) + params["asset_directory"] = check_for_attribute(libs[lib], "asset_directory", parent="settings", var_type="list_path", default=self.general["asset_directory"], default_is_none=True, save=False) if params["asset_directory"] is None: logger.warning("Config Warning: Assets will not be used asset_directory attribute must be set under config or under this specific Library") @@ -292,7 +289,7 @@ class Config: radarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False) radarr_params["add"] = check_for_attribute(libs[lib], "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False) radarr_params["search"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False) - radarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="lowerlist", default=self.general["radarr"]["tag"], default_is_none=True, save=False) + radarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="lower_list", default=self.general["radarr"]["tag"], default_is_none=True, save=False) library.add_Radarr(RadarrAPI(self.TMDb, radarr_params)) except Failed as e: util.print_multiline(e) @@ -309,7 +306,7 @@ class Config: sonarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False) sonarr_params["add"] = check_for_attribute(libs[lib], "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False) sonarr_params["search"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False) - sonarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="lowerlist", default=self.general["sonarr"]["tag"], default_is_none=True, save=False) + sonarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="lower_list", default=self.general["sonarr"]["tag"], default_is_none=True, save=False) library.add_Sonarr(SonarrAPI(self.TVDb, sonarr_params, library.Plex.language)) except Failed as e: util.print_multiline(e) @@ -328,28 +325,28 @@ class Config: self.libraries.append(library) - util.seperator() + util.separator() if len(self.libraries) > 0: logger.info("{} Plex Library Connection{} Successful".format(len(self.libraries), "s" if len(self.libraries) > 1 else "")) else: raise Failed("Plex Error: No Plex libraries were found") - util.seperator() + util.separator() def update_libraries(self, test, requested_collections): for library in self.libraries: os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout) logger.info("") - util.seperator("{} Library".format(library.name)) + util.separator("{} Library".format(library.name)) try: library.update_metadata(self.TMDb, test) except Failed as e: logger.error(e) logger.info("") - util.seperator("{} Library {}Collections".format(library.name, "Test " if test else "")) + util.separator("{} Library {}Collections".format(library.name, "Test " if test else "")) collections = {c: library.collections[c] for c in util.get_list(requested_collections) if c in library.collections} if requested_collections else library.collections if collections: logger.info("") - util.seperator("Mapping {} Library".format(library.name)) + util.separator("Mapping {} Library".format(library.name)) logger.info("") movie_map, show_map = self.map_guids(library) for c in collections: @@ -358,21 +355,21 @@ class Config: if "template" in collections[c] and collections[c]["template"]: for data_template in util.get_list(collections[c]["template"], split=False): if "name" in data_template \ - and data_template["name"] \ - and library.templates \ - and data_template["name"] in self.library.templates \ - and self.library.templates[data_template["name"]] \ - and "test" in self.library.templates[data_template["name"]] \ - and self.library.templates[data_template["name"]]["test"] == True: + and data_template["name"] \ + and library.templates \ + and data_template["name"] in library.templates \ + and library.templates[data_template["name"]] \ + and "test" in library.templates[data_template["name"]] \ + and library.templates[data_template["name"]]["test"] is True: no_template_test = False if no_template_test: continue try: logger.info("") - util.seperator("{} Collection".format(c)) + util.separator("{} Collection".format(c)) logger.info("") - map = {} + rating_key_map = {} try: builder = CollectionBuilder(self, library, c, collections[c]) except Exception as e: @@ -383,19 +380,19 @@ class Config: try: collection_obj = library.get_collection(c) collection_name = collection_obj.title - except Failed as e: + except Failed: collection_obj = None collection_name = c if builder.schedule is not None: - print_multiline(builder.schedule, info=True) + util.print_multiline(builder.schedule, info=True) logger.info("") if builder.sync: logger.info("Sync Mode: sync") if collection_obj: for item in collection_obj.items(): - map[item.ratingKey] = item + rating_key_map[item.ratingKey] = item else: logger.info("Sync Mode: append") @@ -404,7 +401,7 @@ class Config: logger.info("") logger.info("Collection Filter {}: {}".format(f[0], f[1])) - builder.run_methods(collection_obj, collection_name, map, movie_map, show_map) + builder.run_methods(collection_obj, collection_name, rating_key_map, movie_map, show_map) try: plex_collection = library.get_collection(collection_name) @@ -419,14 +416,14 @@ class Config: logger.error("Unknown Error: {}".format(e)) if library.show_unmanaged is True and not test and not requested_collections: logger.info("") - util.seperator("Unmanaged Collections in {} Library".format(library.name)) + util.separator("Unmanaged Collections in {} Library".format(library.name)) logger.info("") unmanaged_count = 0 - collections_in_plex = [str(pcol) for pcol in collections] + collections_in_plex = [str(plex_col) for plex_col in collections] for col in library.get_all_collections(): - if col.title not in collections_in_plex: - logger.info(col.title) - unmanaged_count += 1 + if col.title not in collections_in_plex: + logger.info(col.title) + unmanaged_count += 1 logger.info("{} Unmanaged Collections".format(unmanaged_count)) else: logger.info("") @@ -436,7 +433,6 @@ class Config: movie_map = {} show_map = {} length = 0 - count = 0 logger.info("Mapping {} Library: {}".format("Movie" if library.is_movie else "Show", library.name)) items = library.Plex.all() for i, item in enumerate(items, 1): @@ -445,7 +441,7 @@ class Config: id_type, main_id = self.get_id(item, library, length) except BadRequest: util.print_stacktrace() - util.print_end(length, "{} {:<46} | {} for {}".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, error_message, item.title)) + util.print_end(length, "{} | {} for {} not found".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, item.title)) continue if isinstance(main_id, list): if id_type == "movie": diff --git a/modules/imdb.py b/modules/imdb.py index d4bc13a2..da3c0a89 100644 --- a/modules/imdb.py +++ b/modules/imdb.py @@ -1,4 +1,4 @@ -import logging, math, re, requests, time +import logging, math, re, requests from lxml import html from modules import util from modules.util import Failed @@ -33,8 +33,8 @@ class IMDbAPI: except IndexError: raise Failed("IMDb Error: Failed to parse URL: {}".format(imdb_url)) try: total = int(re.findall("(\\d+) title", results)[0]) except IndexError: raise Failed("IMDb Error: No Results at URL: {}".format(imdb_url)) - if "&start=" in current_url: current_url = re.sub("&start=\d+", "", current_url) - if "&count=" in current_url: current_url = re.sub("&count=\d+", "", current_url) + if "&start=" in current_url: current_url = re.sub("&start=\\d+", "", current_url) + if "&count=" in current_url: current_url = re.sub("&count=\\d+", "", current_url) if limit < 1 or total < limit: limit = total remainder = limit % 250 if remainder == 0: remainder = 250 @@ -66,7 +66,7 @@ class IMDbAPI: if tvdb_id: show_ids.append(tvdb_id) elif method == "imdb_list": if status_message: - logger.info("Processing {}: {}".format(pretty,"{} Items at {}".format(data["limit"], data["url"]) if data["limit"] > 0 else data["url"])) + logger.info("Processing {}: {}".format(pretty, "{} Items at {}".format(data["limit"], data["url"]) if data["limit"] > 0 else data["url"])) imdb_ids = self.get_imdb_ids_from_url(data["url"], language, data["limit"]) total_ids = len(imdb_ids) length = 0 @@ -86,6 +86,8 @@ class IMDbAPI: return movie_ids, show_ids def convert_from_imdb(self, imdb_id, language): + update_tmdb = False + update_tvdb = False if self.Cache: tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id) update_tmdb = False @@ -121,7 +123,7 @@ class IMDbAPI: try: if tvdb_id and not from_cache: self.TVDb.get_series(language, tvdb_id=tvdb_id) except Failed: tvdb_id = None - if not tmdb_id and not tvdb_id : raise Failed("IMDb Error: No TMDb ID or TVDb ID found for IMDb: {}".format(imdb_id)) + if not tmdb_id and not tvdb_id: raise Failed("IMDb Error: No TMDb ID or TVDb ID found for IMDb: {}".format(imdb_id)) if self.Cache: if tmdb_id and update_tmdb is not False: self.Cache.update_imdb("movie", update_tmdb, imdb_id, tmdb_id) diff --git a/modules/mal.py b/modules/mal.py index 7a9c3739..45cd5dc1 100644 --- a/modules/mal.py +++ b/modules/mal.py @@ -27,7 +27,7 @@ class MyAnimeListIDList: raise Failed("MyAnimeList Error: MyAnimeList ID: {} not found".format(mal_id)) class MyAnimeListAPI: - def __init__(self, params, MyAnimeListIDList, authorization=None): + def __init__(self, params, MyAnimeListIDList_in, authorization=None): self.urls = { "oauth_token": "https://myanimelist.net/v1/oauth2/token", "oauth_authorize": "https://myanimelist.net/v1/oauth2/authorize", @@ -40,7 +40,7 @@ class MyAnimeListAPI: self.client_secret = params["client_secret"] self.config_path = params["config_path"] self.authorization = authorization - self.MyAnimeListIDList = MyAnimeListIDList + self.MyAnimeListIDList = MyAnimeListIDList_in if not self.save_authorization(self.authorization): if not self.refresh_authorization(): self.get_authorization() diff --git a/modules/plex.py b/modules/plex.py index 99792fd8..ed618932 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -1,5 +1,5 @@ -import datetime, logging, os, requests -from lxml import html +import logging, os, re, requests +from datetime import datetime, timedelta from modules import util from modules.util import Failed from plexapi.exceptions import BadRequest, NotFound, Unauthorized @@ -16,7 +16,7 @@ class PlexAPI: try: self.PlexServer = PlexServer(params["plex"]["url"], params["plex"]["token"], timeout=params["plex"]["timeout"]) except Unauthorized: raise Failed("Plex Error: Plex token is invalid") except ValueError as e: raise Failed("Plex Error: {}".format(e)) - except requests.exceptions.ConnectionError as e: + except requests.exceptions.ConnectionError: util.print_stacktrace() raise Failed("Plex Error: Plex url is invalid") self.is_movie = params["library_type"] == "movie" @@ -30,7 +30,7 @@ class PlexAPI: if attribute in self.data: if self.data[attribute]: if isinstance(self.data[attribute], dict): return self.data[attribute] - else: logger.waring("Config Warning: {} must be a dictionary".format(attribute)) + else: logger.warning("Config Warning: {} must be a dictionary".format(attribute)) else: logger.warning("Config Warning: {} attribute is blank".format(attribute)) return None @@ -101,7 +101,7 @@ class PlexAPI: try: valid_collections.append(self.get_collection(collection)) except Failed as e: logger.error(e) if len(valid_collections) == 0: - raise Failed("Collection Error: No valid Plex Collections in {}".format(collections[c][m])) + raise Failed("Collection Error: No valid Plex Collections in {}".format(collections)) return valid_collections def add_missing(self, collection, items, is_movie): @@ -119,7 +119,7 @@ class PlexAPI: except yaml.scanner.ScannerError as e: logger.error("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) - def add_to_collection(self, collection, items, filters, show_filtered, map, movie_map, show_map): + def add_to_collection(self, collection, items, filters, show_filtered, rating_key_map, movie_map, show_map): name = collection.title if isinstance(collection, Collections) else collection collection_items = collection.items() if isinstance(collection, Collections) else [] total = len(items) @@ -145,7 +145,6 @@ class PlexAPI: break elif method == "original_language": terms = util.get_list(f[1], lower=True) - tmdb_id = None movie = None for key, value in movie_map.items(): if current.ratingKey == value: @@ -174,6 +173,7 @@ class PlexAPI: break else: terms = util.get_list(f[1]) + attrs = [] if method in ["video_resolution", "audio_language", "subtitle_language"]: for media in current.media: if method == "video_resolution": attrs = [media.videoResolution] @@ -189,20 +189,20 @@ class PlexAPI: length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title)) if match: util.print_end(length, "{} Collection | {} | {}".format(name, "=" if current in collection_items else "+", current.title)) - if current in collection_items: map[current.ratingKey] = None + if current in collection_items: rating_key_map[current.ratingKey] = None else: current.addCollection(name) elif show_filtered is True: logger.info("{} Collection | X | {}".format(name, current.title)) media_type = "{}{}".format("Movie" if self.is_movie else "Show", "s" if total > 1 else "") util.print_end(length, "{} {} Processed".format(total, media_type)) - return map + return rating_key_map def search_item(self, data, year=None): return util.choose_from_list(self.search(data, year=year), "movie" if self.is_movie else "show", str(data), exact=True) def update_metadata(self, TMDb, test): logger.info("") - util.seperator("{} Library Metadata".format(self.name)) + util.separator("{} Library Metadata".format(self.name)) logger.info("") if not self.metadata: raise Failed("No metadata to edit") @@ -210,11 +210,11 @@ class PlexAPI: if test and ("test" not in self.metadata[m] or self.metadata[m]["test"] is not True): continue logger.info("") - util.seperator() + util.separator() logger.info("") year = None if "year" in self.metadata[m]: - now = datetime.datetime.now() + now = datetime.now() if self.metadata[m]["year"] is None: logger.error("Metadata Error: year attribute is blank") elif not isinstance(self.metadata[m]["year"], int): logger.error("Metadata Error: year attribute must be an integer") elif self.metadata[m]["year"] not in range(1800, now.year + 2): logger.error("Metadata Error: year attribute must be between 1800-{}".format(now.year + 1)) @@ -253,7 +253,6 @@ class PlexAPI: except Failed as e: logger.error(e) - originally_available = tmdb_item.first_air_date if tmdb_item else None rating = tmdb_item.vote_average if tmdb_item else None original_title = tmdb_item.original_name if tmdb_item and tmdb_item.original_name != tmdb_item.name else None @@ -325,10 +324,10 @@ class PlexAPI: if self.metadata[m]["label_sync_mode"] is None: logger.error("Metadata Error: label_sync_mode attribute is blank defaulting to append") elif self.metadata[m]["label_sync_mode"] not in ["append", "sync"]: logger.error("Metadata Error: label_sync_mode attribute must be either 'append' or 'sync' defaulting to append") elif self.metadata[m]["label_sync_mode"] == "sync": - for label in (l for l in item_labels if l not in labels): + for label in (la for la in item_labels if la not in labels): item.removeLabel(label) logger.info("Detail: Label {} removed".format(label)) - for label in (l for l in labels if l not in item_labels): + for label in (la for la in labels if la not in item_labels): item.addLabel(label) logger.info("Detail: Label {} added".format(label)) else: @@ -381,7 +380,7 @@ class PlexAPI: if self.metadata[m]["episodes"]: for episode_str in self.metadata[m]["episodes"]: logger.info("") - match = re.search("[Ss]{1}\d+[Ee]{1}\d+", episode_str) + match = re.search("[Ss]\\d+[Ee]\\d+", episode_str) if match: output = match.group(0)[1:].split("E" if "E" in m.group(0) else "e") episode_id = int(output[0]) @@ -421,6 +420,6 @@ class PlexAPI: else: logger.info("Season: {} Episode: {} Details Update Not Needed".format(season_id, episode_id)) else: - logger.error("Metadata Error: episode {} invlaid must have S##E## format".format(episode_str)) + logger.error("Metadata Error: episode {} invalid must have S##E## format".format(episode_str)) else: logger.error("Metadata Error: episodes attribute is blank") diff --git a/modules/radarr.py b/modules/radarr.py index 10591f0f..af7c6dec 100644 --- a/modules/radarr.py +++ b/modules/radarr.py @@ -11,7 +11,7 @@ class RadarrAPI: self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") try: result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() - except Exception as e: + except Exception: util.print_stacktrace() raise Failed("Radarr Error: Could not connect to Radarr at {}".format(params["url"])) if "error" in result and result["error"] == "Unauthorized": @@ -94,7 +94,7 @@ class RadarrAPI: else: try: logger.error("Radarr Error: ({}) {}: ({}) {}".format(tmdb_id, movie.title, response.status_code, response.json()[0]["errorMessage"])) - except KeyError as e: + except KeyError: logger.debug(url_json) logger.error("Radarr Error: {}".format(response.json())) logger.info("{} Movie{} added to Radarr".format(add_count, "s" if add_count > 1 else "")) diff --git a/modules/sonarr.py b/modules/sonarr.py index 9b76f261..252ffcf0 100644 --- a/modules/sonarr.py +++ b/modules/sonarr.py @@ -11,7 +11,7 @@ class SonarrAPI: self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") try: result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() - except Exception as e: + except Exception: util.print_stacktrace() raise Failed("Sonarr Error: Could not connect to Sonarr at {}".format(params["url"])) if "error" in result and result["error"] == "Unauthorized": @@ -72,7 +72,7 @@ class SonarrAPI: "language": self.language, "monitored": True, "rootFolderPath": self.root_folder_path, - "seasons" : [], + "seasons": [], "images": [{"covertype": "poster", "url": show.poster_path}], "addOptions": {"searchForMissingEpisodes": self.search} } @@ -85,7 +85,7 @@ class SonarrAPI: else: try: logger.error("Sonarr Error: ({}) {}: ({}) {}".format(tvdb_id, show.title, response.status_code, response.json()[0]["errorMessage"])) - except KeyError as e: + except KeyError: logger.debug(url_json) logger.error("Sonarr Error: {}".format(response.json())) logger.info("{} Show{} added to Sonarr".format(add_count, "s" if add_count > 1 else "")) diff --git a/modules/tautulli.py b/modules/tautulli.py index 33b7065b..ace482b5 100644 --- a/modules/tautulli.py +++ b/modules/tautulli.py @@ -9,7 +9,7 @@ class TautulliAPI: def __init__(self, params): try: response = requests.get("{}/api/v2?apikey={}&cmd=get_library_names".format(params["url"], params["apikey"])).json() - except Exception as e: + except Exception: util.print_stacktrace() raise Failed("Tautulli Error: Invalid url") if response["response"]["result"] != "success": diff --git a/modules/tests.py b/modules/tests.py index cf7ed4ef..4ffb2b33 100644 --- a/modules/tests.py +++ b/modules/tests.py @@ -9,7 +9,7 @@ def run_tests(default_dir): try: config = Config(default_dir) logger.info("") - util.seperator("Mapping Tests") + util.separator("Mapping Tests") for library in config.libraries: config.map_guids(library) anidb_tests(config) @@ -19,13 +19,13 @@ def run_tests(default_dir): tmdb_tests(config) trakt_tests(config) tvdb_tests(config) - util.seperator("Finished All Plex Meta Manager Tests") + util.separator("Finished All Plex Meta Manager Tests") except KeyboardInterrupt: - util.seperator("Canceled Plex Meta Manager Tests") + util.separator("Canceled Plex Meta Manager Tests") def anidb_tests(config): if config.AniDB: - util.seperator("AniDB Tests") + util.separator("AniDB Tests") try: config.AniDB.convert_anidb_to_tvdb(69) @@ -84,11 +84,11 @@ def anidb_tests(config): logger.error("Failure | Validate AniDB List: {}".format(e)) else: - util.seperator("AniDB Not Configured") + util.separator("AniDB Not Configured") def imdb_tests(config): if config.IMDb: - util.seperator("IMDb Tests") + util.separator("IMDb Tests") tmdb_ids, tvdb_ids = config.IMDb.get_items("imdb_list", {"url": "https://www.imdb.com/search/title/?groups=top_1000", "limit": 0}, "en", status_message=False) if len(tmdb_ids) == 1000: logger.info("Success | IMDb URL get TMDb IDs") @@ -103,11 +103,11 @@ def imdb_tests(config): else: logger.error("Failure | IMDb ID get TMDb IDs: {} Should be 1".format(len(tmdb_ids))) else: - util.seperator("IMDb Not Configured") + util.separator("IMDb Not Configured") def mal_tests(config): if config.MyAnimeListIDList: - util.seperator("MyAnimeListXML Tests") + util.separator("MyAnimeListXML Tests") try: config.MyAnimeListIDList.convert_mal_to_tvdb(21) @@ -145,10 +145,10 @@ def mal_tests(config): logger.error("Failure | Find MyAnimeList ID: {}".format(e)) else: - util.seperator("MyAnimeListXML Not Configured") + util.separator("MyAnimeListXML Not Configured") if config.MyAnimeList: - util.seperator("MyAnimeList Tests") + util.separator("MyAnimeList Tests") mal_list_tests = [ ("mal_all", 10), @@ -173,11 +173,11 @@ def mal_tests(config): util.print_stacktrace() logger.error("Failure | Get Anime using {}: {}".format(util.pretty_names[mal_list_test[0]], e)) else: - util.seperator("MyAnimeList Not Configured") + util.separator("MyAnimeList Not Configured") def tautulli_tests(config): if config.libraries[0].Tautulli: - util.seperator("Tautulli Tests") + util.separator("Tautulli Tests") try: config.libraries[0].Tautulli.get_section_id(config.libraries[0].name) @@ -200,11 +200,11 @@ def tautulli_tests(config): util.print_stacktrace() logger.error("Failure | Get Top: {}".format(e)) else: - util.seperator("Tautulli Not Configured") + util.separator("Tautulli Not Configured") def tmdb_tests(config): if config.TMDb: - util.seperator("TMDb Tests") + util.separator("TMDb Tests") try: config.TMDb.convert_imdb_to_tmdb("tt0076759") @@ -284,11 +284,11 @@ def tmdb_tests(config): util.print_stacktrace() logger.error("Failure | Get {} using {}: {}".format("Movies" if tmdb_list_test[2] else "Shows", util.pretty_names[tmdb_list_test[0]], e)) else: - util.seperator("TMDb Not Configured") + util.separator("TMDb Not Configured") def trakt_tests(config): if config.Trakt: - util.seperator("Trakt Tests") + util.separator("Trakt Tests") try: config.Trakt.convert_imdb_to_tmdb("tt0076759") @@ -369,11 +369,11 @@ def trakt_tests(config): util.print_stacktrace() logger.error("Failure | Get {} using {}: {}".format("Movies" if trakt_list_test[2] else "Shows", util.pretty_names[trakt_list_test[0]], e)) else: - util.seperator("Trakt Not Configured") + util.separator("Trakt Not Configured") def tvdb_tests(config): if config.TVDb: - util.seperator("TVDb Tests") + util.separator("TVDb Tests") tmdb_ids, tvdb_ids = config.TVDb.get_items("tvdb_list", "https://www.thetvdb.com/lists/arrowverse", "en", status_message=False) if len(tvdb_ids) == 10 and len(tmdb_ids) == 0: logger.info("Success | TVDb URL get TVDb IDs and TMDb IDs") @@ -412,4 +412,4 @@ def tvdb_tests(config): logger.error("Failure | TVDb ID get TVDb Movie ID: {}".format(e)) else: - util.seperator("TVDb Not Configured") + util.separator("TVDb Not Configured") diff --git a/modules/tmdb.py b/modules/tmdb.py index 403a727c..157b8f4b 100644 --- a/modules/tmdb.py +++ b/modules/tmdb.py @@ -1,4 +1,4 @@ -import logging, os, tmdbv3api +import logging, tmdbv3api from modules import util from modules.util import Failed from retrying import retry @@ -30,9 +30,13 @@ class TMDbAPI: @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def convert_from_tmdb(self, tmdb_id, convert_to, is_movie): - try: return self.Movie.external_ids(tmdb_id)[convert_to] if is_movie else self.TV.external_ids(tmdb_id)[convert_to] - except TMDbException: raise Failed("TMDb Error: No {} found for TMDb ID {}".format(convert_to.upper().replace("B_", "b "), tmdb_id)) - + 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): + raise Failed("TMDb Error: No {} found for TMDb ID {}".format(convert_to.upper().replace("B_", "b "), tmdb_id)) + return id_to_return + except TMDbException: + raise Failed("TMDb Error: {} TMDb ID: {} not found".format("Movie" if is_movie else "Show", tmdb_id)) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def convert_to_tmdb(self, external_id, external_source, is_movie): @@ -105,6 +109,7 @@ class TMDbAPI: elif method == "tmdb_now_playing" and is_movie: tmdb_items = self.Movie.now_playing(x + 1) elif method == "tmdb_trending_daily": tmdb_items = self.Trending.movie_day(x + 1) if is_movie else self.Trending.tv_day(x + 1) elif method == "tmdb_trending_weekly": tmdb_items = self.Trending.movie_week(x + 1) if is_movie else self.Trending.tv_week(x + 1) + else: raise Failed("TMDb Error: {} method not supported".format(method)) for tmdb_item in tmdb_items: try: ids.append(tmdb_item.id if is_movie else self.convert_tmdb_to_tvdb(tmdb_item.id)) @@ -143,6 +148,9 @@ class TMDbAPI: movie_ids = [] show_ids = [] if method in ["tmdb_discover", "tmdb_company", "tmdb_keyword"] or (method == "tmdb_network" and not is_movie): + attrs = None + tmdb_id = "" + tmdb_name = "" if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]: tmdb_id = int(data) if method == "tmdb_company": @@ -163,7 +171,7 @@ class TMDbAPI: if status_message: if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]: logger.info("Processing {}: ({}) {} ({} {}{})".format(pretty, tmdb_id, tmdb_name, amount, media_type, "" if amount == 1 else "s")) - else: + elif method == "tmdb_discover": logger.info("Processing {}: {} {}{}".format(pretty, amount, media_type, "" if amount == 1 else "s")) for attr, value in attrs.items(): logger.info(" {}: {}".format(attr, value)) diff --git a/modules/trakt.py b/modules/trakttv.py similarity index 99% rename from modules/trakt.py rename to modules/trakttv.py index 2bbcad27..6d28e3f6 100644 --- a/modules/trakt.py +++ b/modules/trakttv.py @@ -119,7 +119,7 @@ class TraktAPI: except Failed as e: logger.error(e) if len(trakt_values) == 0: - raise Failed("Trakt Error: No valid Trakt Lists in {}".format(value)) + raise Failed("Trakt Error: No valid Trakt Lists in {}".format(values)) return trakt_values def validate_trakt_watchlist(self, values, is_movie): @@ -131,7 +131,7 @@ class TraktAPI: except Failed as e: logger.error(e) if len(trakt_values) == 0: - raise Failed("Trakt Error: No valid Trakt Watchlists in {}".format(value)) + raise Failed("Trakt Error: No valid Trakt Watchlists in {}".format(values)) return trakt_values def get_items(self, method, data, is_movie, status_message=True): diff --git a/modules/tvdb.py b/modules/tvdb.py index f483305e..f91bbfe5 100644 --- a/modules/tvdb.py +++ b/modules/tvdb.py @@ -1,4 +1,4 @@ -import logging, math, re, requests, time +import logging, requests from lxml import html from modules import util from modules.util import Failed @@ -20,6 +20,10 @@ class TVDbObj: results = response.xpath("//*[text()='TheTVDB.com {} ID']/parent::node()/span/text()".format(self.media_type)) if len(results) > 0: self.id = int(results[0]) + elif tvdb_url.startswith(TVDb.movie_id_url): + raise Failed("TVDb Error: Could not find a TVDb Movie using TVDb Movie ID: {}".format(tvdb_url[len(TVDb.series_id_url):])) + elif tvdb_url.startswith(TVDb.series_id_url): + raise Failed("TVDb Error: Could not find a TVDb Series using TVDb Series ID: {}".format(tvdb_url[len(TVDb.series_id_url):])) else: raise Failed("TVDb Error: Could not find a TVDb {} ID at the URL {}".format(self.media_type, tvdb_url)) @@ -41,7 +45,7 @@ class TVDbObj: if not tmdb_id: results = response.xpath("//*[text()='IMDB']/@href") if len(results) > 0: - try: tmdb_id = TVDb.convert_from_imdb(util.get_id_from_imdb_url(results[0]), language) + try: tmdb_id = TVDb.convert_from_imdb(util.get_id_from_imdb_url(results[0])) except Failed as e: logger.error(e) self.tmdb_id = tmdb_id self.tvdb_url = tvdb_url @@ -67,7 +71,7 @@ class TVDbAPI: def get_series(self, language, tvdb_url=None, tvdb_id=None): if not tvdb_url and not tvdb_id: - raise Failed("TVDB Error: getget_seriesmove requires either tvdb_url or tvdb_id") + raise Failed("TVDB Error: get_series requires either tvdb_url or tvdb_id") elif not tvdb_url and tvdb_id: tvdb_url = "{}{}".format(self.series_id_url, tvdb_id) return TVDbObj(tvdb_url, language, False, self) @@ -104,7 +108,7 @@ class TVDbAPI: if len(show_ids) > 0 or len(movie_ids) > 0: return movie_ids, show_ids raise Failed("TVDb Error: No TVDb IDs found at {}".format(tvdb_url)) - except requests.exceptions.MissingSchema as e: + except requests.exceptions.MissingSchema: util.print_stacktrace() raise Failed("TVDb Error: URL Lookup Failed for {}".format(tvdb_url)) else: @@ -137,10 +141,10 @@ class TVDbAPI: logger.debug("TVDb IDs Found: {}".format(show_ids)) return movie_ids, show_ids - def convert_from_imdb(self, imdb_id, language): + def convert_from_imdb(self, imdb_id): + update = False if self.Cache: tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id) - update = False if not tmdb_id: tmdb_id, update = self.Cache.get_tmdb_from_imdb(imdb_id) if update: diff --git a/modules/util.py b/modules/util.py index f5777b84..5fe7c6e8 100644 --- a/modules/util.py +++ b/modules/util.py @@ -18,7 +18,7 @@ class Failed(Exception): def retry_if_not_failed(exception): return not isinstance(exception, Failed) -seperating_character = "=" +separating_character = "=" screen_width = 100 method_alias = { @@ -524,7 +524,7 @@ def get_year_list(data, method): final_years.append(i) else: year = re.search("(\\d+)", str(value)).group(1) - if int(start) < 1800 or int(start) > current_year: + if int(year) < 1800 or int(year) > current_year: logger.error("Collection Error: Skipping {} year {} must be between 1800 and {}".format(method, year, current_year)) else: if len(str(year)) != len(str(value)): @@ -572,14 +572,14 @@ def windows_input(prompt, timeout=5): start_time = time.time() while True: if msvcrt.kbhit(): - chr = msvcrt.getwche() - if ord(chr) == 13: # enter_key + char = msvcrt.getwche() + if ord(char) == 13: # enter_key out = "".join(result) print("") logger.debug("{}: {}".format(prompt, out)) return out - elif ord(chr) >= 32: #space_char - result.append(chr) + elif ord(char) >= 32: #space_char + result.append(char) if (time.time() - start_time) > timeout: print("") raise TimeoutExpired @@ -631,13 +631,13 @@ def get_centered_text(text): side = int(space / 2) return "{}{}{}".format(" " * side, text, " " * side) -def seperator(text=None): +def separator(text=None): logger.handlers[0].setFormatter(logging.Formatter("%(message)-{}s".format(screen_width - 2))) logger.handlers[1].setFormatter(logging.Formatter("[%(asctime)s] %(filename)-27s %(levelname)-10s %(message)-{}s".format(screen_width - 2))) - logger.info("|{}|".format(seperating_character * screen_width)) + logger.info("|{}|".format(separating_character * screen_width)) if text: logger.info("| {} |".format(get_centered_text(text))) - logger.info("|{}|".format(seperating_character * screen_width)) + logger.info("|{}|".format(separating_character * screen_width)) logger.handlers[0].setFormatter(logging.Formatter("| %(message)-{}s |".format(screen_width - 2))) logger.handlers[1].setFormatter(logging.Formatter("[%(asctime)s] %(filename)-27s %(levelname)-10s | %(message)-{}s |".format(screen_width - 2))) diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 5d3341d4..48287bc0 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -1,9 +1,9 @@ -import argparse, logging, os, re, schedule, sys, time, traceback, datetime +import argparse, logging, os, re, schedule, sys, time, datetime from modules import tests, util from modules.config import Config parser = argparse.ArgumentParser() -parser.add_argument("--mytests", dest="tests", help=argparse.SUPPRESS, action="store_true", default=False) +parser.add_argument("--my-tests", dest="tests", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("--debug", dest="debug", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("-c", "--config", dest="config", help="Run with desired *.yml file", type=str) parser.add_argument("-t", "--time", dest="time", help="Time to update each day use format HH:MM (Default: 03:00)", default="03:00", type=str) @@ -17,7 +17,7 @@ args = parser.parse_args() if not re.match("^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$", args.time): raise util.Failed("Argument Error: time argument invalid: {} must be in the HH:MM format".format(args.time)) -util.seperating_character = args.divider[0] +util.separating_character = args.divider[0] if 90 <= args.width <= 300: util.screen_width = args.width else: @@ -51,27 +51,27 @@ logger.addHandler(file_handler) sys.excepthook = util.my_except_hook -util.seperator() +util.separator() logger.info(util.get_centered_text(" ")) logger.info(util.get_centered_text(" ____ _ __ __ _ __ __ ")) -logger.info(util.get_centered_text("| _ \| | _____ __ | \/ | ___| |_ __ _ | \/ | __ _ _ __ __ _ __ _ ___ _ __ ")) -logger.info(util.get_centered_text("| |_) | |/ _ \ \/ / | |\/| |/ _ \ __/ _` | | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|")) +logger.info(util.get_centered_text("| _ \\| | _____ __ | \\/ | ___| |_ __ _ | \\/ | __ _ _ __ __ _ __ _ ___ _ __ ")) +logger.info(util.get_centered_text("| |_) | |/ _ \\ \\/ / | |\\/| |/ _ \\ __/ _` | | |\\/| |/ _` | '_ \\ / _` |/ _` |/ _ \\ '__|")) logger.info(util.get_centered_text("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | ")) -logger.info(util.get_centered_text("|_| |_|\___/_/\_\ |_| |_|\___|\__\__,_| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| ")) +logger.info(util.get_centered_text("|_| |_|\\___/_/\\_\\ |_| |_|\\___|\\__\\__,_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| ")) logger.info(util.get_centered_text(" |___/ ")) logger.info(util.get_centered_text(" Version: 1.2.2 ")) -util.seperator() +util.separator() if args.tests: tests.run_tests(default_dir) sys.exit(0) def start(config_path, test, daily, collections): - if daily: type = "Daily " - elif test: type = "Test " - elif collections: type = "Collections " - else: type = "" - util.seperator("Starting {}Run".format(type)) + if daily: start_type = "Daily " + elif test: start_type = "Test " + elif collections: start_type = "Collections " + else: start_type = "" + util.separator("Starting {}Run".format(start_type)) try: config = Config(default_dir, config_path) config.update_libraries(test, collections) @@ -79,7 +79,7 @@ def start(config_path, test, daily, collections): util.print_stacktrace() logger.critical(e) logger.info("") - util.seperator("Finished {}Run".format(type)) + util.separator("Finished {}Run".format(start_type)) try: if args.run or args.test or args.collections: @@ -101,4 +101,4 @@ try: length = util.print_return(length, "Current Time: {} | {} until the daily run at {}".format(current, time_str, args.time)) time.sleep(1) except KeyboardInterrupt: - util.seperator("Exiting Plex Meta Manager") + util.separator("Exiting Plex Meta Manager")