From 4c8ccaa171f836931a7435f45a68c66950f8331e Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Wed, 24 Feb 2021 01:44:06 -0500 Subject: [PATCH] changed to f-Strings --- modules/anidb.py | 36 +++---- modules/builder.py | 227 ++++++++++++++++++++++--------------------- modules/cache.py | 8 +- modules/config.py | 100 +++++++++---------- modules/imdb.py | 35 +++---- modules/mal.py | 47 ++++----- modules/plex.py | 104 ++++++++++---------- modules/radarr.py | 38 ++++---- modules/sonarr.py | 30 +++--- modules/tautulli.py | 16 +-- modules/tests.py | 92 +++++++++--------- modules/tmdb.py | 50 +++++----- modules/trakttv.py | 26 ++--- modules/tvdb.py | 60 ++++++------ modules/util.py | 66 +++++++------ plex_meta_manager.py | 22 ++--- 16 files changed, 483 insertions(+), 474 deletions(-) diff --git a/modules/anidb.py b/modules/anidb.py index 292c753a..5597b0e9 100644 --- a/modules/anidb.py +++ b/modules/anidb.py @@ -23,14 +23,14 @@ class AniDBAPI: def convert_tvdb_to_anidb(self, tvdb_id): return self.convert_anidb(tvdb_id, "tvdbid", "anidbid") def convert_imdb_to_anidb(self, imdb_id): return self.convert_anidb(imdb_id, "imdbid", "anidbid") def convert_anidb(self, input_id, from_id, to_id): - ids = self.id_list.xpath("//anime[contains(@{}, '{}')]/@{}".format(from_id, input_id, to_id)) + ids = self.id_list.xpath(f"//anime[contains(@{from_id}, '{input_id}')]/@{to_id}") if len(ids) > 0: 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)) - else: raise Failed("AniDB Error: No {} ID found for {} ID: {}".format(util.pretty_ids[to_id], util.pretty_ids[from_id], input_id)) - else: raise Failed("AniDB Error: {} ID: {} not found".format(util.pretty_ids[from_id], input_id)) + except ValueError: raise Failed(f"AniDB Error: No {util.pretty_ids[to_id]} ID found for {util.pretty_ids[from_id]} ID: {input_id}") + else: raise Failed(f"AniDB Error: No {util.pretty_ids[to_id]} ID found for {util.pretty_ids[from_id]} ID: {input_id}") + else: raise Failed(f"AniDB Error: {util.pretty_ids[from_id]} ID: {input_id} not found") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_request(self, url, language): @@ -41,14 +41,14 @@ class AniDBAPI: return util.get_int_list(response.xpath("//td[@class='name anime']/a/@href"), "AniDB ID") def validate_anidb_id(self, anidb_id, language): - response = self.send_request("{}/{}".format(self.urls["anime"], anidb_id), language) - ids = response.xpath("//*[text()='a{}']/text()".format(anidb_id)) + response = self.send_request(f"{self.urls['anime']}/{anidb_id}", language) + ids = response.xpath(f"//*[text()='a{anidb_id}']/text()") if len(ids) > 0: return util.regex_first_int(ids[0], "AniDB ID") - raise Failed("AniDB Error: AniDB ID: {} not found".format(anidb_id)) + raise Failed(f"AniDB Error: AniDB ID: {anidb_id} not found") def get_anidb_relations(self, anidb_id, language): - response = self.send_request("{}/{}{}".format(self.urls["anime"], anidb_id, self.urls["relation"]), language) + response = self.send_request(f"{self.urls['anime']}/{anidb_id}{self.urls['relation']}", language) return util.get_int_list(response.xpath("//area/@href"), "AniDB ID") def validate_anidb_list(self, anidb_list, language): @@ -60,22 +60,22 @@ class AniDBAPI: logger.error(e) if len(anidb_values) > 0: return anidb_values - raise Failed("AniDB Error: No valid AniDB IDs in {}".format(anidb_list)) + raise Failed(f"AniDB Error: No valid AniDB IDs in {anidb_list}") def get_items(self, method, data, language, status_message=True): pretty = util.pretty_names[method] if method in util.pretty_names else method if status_message: - logger.debug("Data: {}".format(data)) + logger.debug(f"Data: {data}") anime_ids = [] if method == "anidb_popular": if status_message: - logger.info("Processing {}: {} Anime".format(pretty, data)) + logger.info(f"Processing {pretty}: {data} Anime") anime_ids.extend(self.get_popular(language)[:data]) else: - if status_message: logger.info("Processing {}: {}".format(pretty, data)) + if status_message: logger.info(f"Processing {pretty}: {data}") if method == "anidb_id": anime_ids.append(data) elif method == "anidb_relation": anime_ids.extend(self.get_anidb_relations(data, language)) - else: raise Failed("AniDB Error: Method {} not supported".format(method)) + else: raise Failed(f"AniDB Error: Method {method} not supported") show_ids = [] movie_ids = [] for anidb_id in anime_ids: @@ -85,11 +85,11 @@ class AniDBAPI: else: raise Failed except Failed: try: show_ids.append(self.convert_anidb_to_tvdb(anidb_id)) - except Failed: logger.error("AniDB Error: No TVDb ID or IMDb ID found for AniDB ID: {}".format(anidb_id)) + except Failed: logger.error(f"AniDB Error: No TVDb ID or IMDb ID found for AniDB ID: {anidb_id}") if status_message: - logger.debug("AniDB IDs Found: {}".format(anime_ids)) - logger.debug("TMDb IDs Found: {}".format(movie_ids)) - logger.debug("TVDb IDs Found: {}".format(show_ids)) + logger.debug(f"AniDB IDs Found: {anime_ids}") + logger.debug(f"TMDb IDs Found: {movie_ids}") + logger.debug(f"TVDb IDs Found: {show_ids}") return movie_ids, show_ids def convert_from_imdb(self, imdb_id): @@ -121,6 +121,6 @@ class AniDBAPI: if tmdb_id: output_tmdb_ids.append(tmdb_id) if self.Cache and tmdb_id and expired is not False: self.Cache.update_imdb("movie", expired, imdb, tmdb_id) - if len(output_tmdb_ids) == 0: raise Failed("AniDB Error: No TMDb ID found for IMDb: {}".format(imdb_id)) + if len(output_tmdb_ids) == 0: raise Failed(f"AniDB Error: No TMDb ID found for IMDb: {imdb_id}") elif len(output_tmdb_ids) == 1: return output_tmdb_ids[0] else: return output_tmdb_ids diff --git a/modules/builder.py b/modules/builder.py index b3e594fe..99343c67 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -37,13 +37,13 @@ class CollectionBuilder: elif not data_template["name"]: raise Failed("Collection Error: template sub-attribute name is blank") elif data_template["name"] not in self.library.templates: - raise Failed("Collection Error: template {} not found".format(data_template["name"])) + raise Failed(f"Collection Error: template {data_template['name']} not found") elif not isinstance(self.library.templates[data_template["name"]], dict): - raise Failed("Collection Error: template {} is not a dictionary".format(data_template["name"])) + raise Failed(f"Collection Error: template {data_template['name']} is not a dictionary") else: for tm in data_template: if not data_template[tm]: - raise Failed("Collection Error: template sub-attribute {} is blank".format(data_template[tm])) + raise Failed(f"Collection Error: template sub-attribute {data_template[tm]} is blank") template_name = data_template["name"] template = self.library.templates[template_name] @@ -55,7 +55,7 @@ class CollectionBuilder: if template["default"][dv]: default[dv] = template["default"][dv] else: - raise Failed("Collection Error: template default sub-attribute {} is blank".format(dv)) + raise Failed(f"Collection Error: template default sub-attribute {dv} is blank") else: raise Failed("Collection Error: template sub-attribute default is not a dictionary") else: @@ -67,13 +67,13 @@ class CollectionBuilder: def replace_txt(txt): txt = str(txt) 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 template_method != "name" and f"<<{template_method}>>" in txt: + txt = txt.replace(f"<<{template_method}>>", str(data_template[template_method])) if "<>" in txt: txt = txt.replace("<>", str(self.name)) for dm in default: - if "<<{}>>".format(dm) in txt: - txt = txt.replace("<<{}>>".format(dm), str(default[dm])) + if f"<<{dm}>>" in txt: + txt = txt.replace(f"<<{dm}>>", str(default[dm])) if txt in ["true", "True"]: return True elif txt in ["false", "False"]: return False else: @@ -103,7 +103,7 @@ class CollectionBuilder: attr = replace_txt(template[m]) self.data[m] = attr else: - raise Failed("Collection Error: template attribute {} is blank".format(m)) + raise Failed(f"Collection Error: template attribute {m} is blank") skip_collection = True if "schedule" not in data: @@ -127,48 +127,48 @@ class CollectionBuilder: if run_time.startswith("week"): if param.lower() in util.days_alias: weekday = util.days_alias[param.lower()] - self.schedule += "\nScheduled weekly on {}".format(util.pretty_days[weekday]) + self.schedule += f"\nScheduled weekly on {util.pretty_days[weekday]}" if weekday == current_time.weekday(): skip_collection = False else: - logger.error("Collection Error: weekly schedule attribute {} invalid must be a day of the week i.e. weekly(Monday)".format(schedule)) + logger.error(f"Collection Error: weekly schedule attribute {schedule} invalid must be a day of the week i.e. weekly(Monday)") elif run_time.startswith("month"): try: if 1 <= int(param) <= 31: - self.schedule += "\nScheduled monthly on the {}".format(util.make_ordinal(param)) + self.schedule += f"\nScheduled monthly on the {util.make_ordinal(param)}" if current_time.day == int(param) or (current_time.day == last_day.day and int(param) > last_day.day): skip_collection = False else: - logger.error("Collection Error: monthly schedule attribute {} invalid must be between 1 and 31".format(schedule)) + logger.error(f"Collection Error: monthly schedule attribute {schedule} invalid must be between 1 and 31") except ValueError: - logger.error("Collection Error: monthly schedule attribute {} invalid must be an integer".format(schedule)) + logger.error(f"Collection Error: monthly schedule attribute {schedule} invalid must be an integer") elif run_time.startswith("year"): match = re.match("^(1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])$", param) if match: month = int(match.group(1)) day = int(match.group(2)) - self.schedule += "\nScheduled yearly on {} {}".format(util.pretty_months[month], util.make_ordinal(day)) + self.schedule += f"\nScheduled yearly on {util.pretty_months[month]} {util.make_ordinal(day)}" if current_time.month == month and (current_time.day == day or (current_time.day == last_day.day and day > last_day.day)): skip_collection = False else: - logger.error("Collection Error: yearly schedule attribute {} invalid must be in the MM/DD format i.e. yearly(11/22)".format(schedule)) + logger.error(f"Collection Error: yearly schedule attribute {schedule} invalid must be in the MM/DD format i.e. yearly(11/22)") else: - logger.error("Collection Error: failed to parse schedule: {}".format(schedule)) + logger.error(f"Collection Error: failed to parse schedule: {schedule}") else: - logger.error("Collection Error: schedule attribute {} invalid".format(schedule)) + logger.error(f"Collection Error: schedule attribute {schedule} invalid") if self.schedule is None: skip_collection = False if skip_collection: - raise Failed("Skipping Collection {}".format(self.name)) + raise Failed(f"Skipping Collection {self.name}") - logger.info("Scanning {} Collection".format(self.name)) + logger.info(f"Scanning {self.name} Collection") self.collectionless = "plex_collectionless" in data self.sync = self.library.sync_mode == "sync" if "sync_mode" in data: - if not data["sync_mode"]: logger.warning("Collection Warning: sync_mode attribute is blank using general: {}".format(self.library.sync_mode)) - elif data["sync_mode"] not in ["append", "sync"]: logger.warning("Collection Warning: {} sync_mode invalid using general: {}".format(self.library.sync_mode, data["sync_mode"])) + if not data["sync_mode"]: logger.warning(f"Collection Warning: sync_mode attribute is blank using general: {self.library.sync_mode}") + elif data["sync_mode"] not in ["append", "sync"]: logger.warning(f"Collection Warning: {self.library.sync_mode} sync_mode invalid using general: {data['sync_mode']}") else: self.sync = data["sync_mode"] == "sync" if "tmdb_person" in data: @@ -180,35 +180,35 @@ class CollectionBuilder: if "summary" not in self.details and hasattr(person, "biography") and person.biography: self.details["summary"] = person.biography if "poster" not in self.details and hasattr(person, "profile_path") and person.profile_path: - self.details["poster"] = ("url", "{}{}".format(config.TMDb.image_url, person.profile_path), "tmdb_person") + self.details["poster"] = ("url", f"{config.TMDb.image_url}{person.profile_path}", "tmdb_person") if len(valid_names) > 0: self.details["tmdb_person"] = valid_names - else: raise Failed("Collection Error: No valid TMDb Person IDs in {}".format(data["tmdb_person"])) + else: raise Failed(f"Collection Error: No valid TMDb Person IDs in {data['tmdb_person']}") else: raise Failed("Collection Error: tmdb_person attribute is blank") for m in data: - if "tmdb" in m and not config.TMDb: raise Failed("Collection Error: {} requires TMDb to be configured".format(m)) - elif "trakt" in m and not config.Trakt: raise Failed("Collection Error: {} requires Trakt todo be configured".format(m)) - elif "imdb" in m and not config.IMDb: raise Failed("Collection Error: {} requires TMDb or Trakt to be configured".format(m)) - elif "tautulli" in m and not self.library.Tautulli: raise Failed("Collection Error: {} requires Tautulli to be configured".format(m)) - elif "mal" in m and not config.MyAnimeList: raise Failed("Collection Error: {} requires MyAnimeList to be configured".format(m)) + if "tmdb" in m and not config.TMDb: raise Failed(f"Collection Error: {m} requires TMDb to be configured") + elif "trakt" in m and not config.Trakt: raise Failed(f"Collection Error: {m} requires Trakt todo be configured") + elif "imdb" in m and not config.IMDb: raise Failed(f"Collection Error: {m} requires TMDb or Trakt to be configured") + elif "tautulli" in m and not self.library.Tautulli: raise Failed(f"Collection Error: {m} requires Tautulli to be configured") + elif "mal" in m and not config.MyAnimeList: raise Failed(f"Collection Error: {m} requires MyAnimeList to be configured") elif data[m] is not None: logger.debug("") - logger.debug("Method: {}".format(m)) - logger.debug("Value: {}".format(data[m])) + logger.debug(f"Method: {m}") + logger.debug(f"Value: {data[m]}") if m in util.method_alias: method_name = util.method_alias[m] - logger.warning("Collection Warning: {} attribute will run as {}".format(m, method_name)) + logger.warning(f"Collection Warning: {m} attribute will run as {method_name}") else: method_name = m if method_name in util.show_only_lists and self.library.is_movie: - raise Failed("Collection Error: {} attribute only works for show libraries".format(method_name)) + raise Failed(f"Collection Error: {method_name} attribute only works for show libraries") elif method_name in util.movie_only_lists and self.library.is_show: - raise Failed("Collection Error: {} attribute only works for movie libraries".format(method_name)) + raise Failed(f"Collection Error: {method_name} attribute only works for movie libraries") elif method_name in util.movie_only_searches and self.library.is_show: - raise Failed("Collection Error: {} plex search only works for movie libraries".format(method_name)) + raise Failed(f"Collection Error: {method_name} plex search only works for movie libraries") elif method_name not in util.collectionless_lists and self.collectionless: - raise Failed("Collection Error: {} attribute does not work for Collectionless collection".format(method_name)) + raise Failed(f"Collection Error: {method_name} attribute does not work for Collectionless collection") elif method_name == "tmdb_summary": self.details["summary"] = config.TMDb.get_movie_show_or_collection(util.regex_first_int(data[m], "TMDb ID"), self.library.is_movie).overview elif method_name == "tmdb_description": @@ -221,28 +221,28 @@ class CollectionBuilder: elif data[m] == "show_items": self.details[method_name] = "showItems" else: self.details[method_name] = data[m] else: - raise Failed("Collection Error: {} collection_mode Invalid\n| \tdefault (Library default)\n| \thide (Hide Collection)\n| \thide_items (Hide Items in this Collection)\n| \tshow_items (Show this Collection and its Items)".format(data[m])) + raise Failed(f"Collection Error: {data[m]} collection_mode Invalid\n| \tdefault (Library default)\n| \thide (Hide Collection)\n| \thide_items (Hide Items in this Collection)\n| \tshow_items (Show this Collection and its Items)") elif method_name == "collection_order": if data[m] in ["release", "alpha"]: self.details[method_name] = data[m] else: - raise Failed("Collection Error: {} collection_order Invalid\n| \trelease (Order Collection by release dates)\n| \talpha (Order Collection Alphabetically)".format(data[m])) + raise Failed(f"Collection Error: {data[m]} collection_order Invalid\n| \trelease (Order Collection by release dates)\n| \talpha (Order Collection Alphabetically)") elif method_name == "url_poster": self.posters.append(("url", data[m], method_name)) elif method_name == "tmdb_poster": - self.posters.append(("url", "{}{}".format(config.TMDb.image_url, config.TMDb.get_movie_show_or_collection(util.regex_first_int(data[m], "TMDb ID"), self.library.is_movie).poster_path), method_name)) + self.posters.append(("url", f"{config.TMDb.image_url}{config.TMDb.get_movie_show_or_collection(util.regex_first_int(data[m], 'TMDb ID'), self.library.is_movie).poster_path}", method_name)) elif method_name == "tmdb_profile": - self.posters.append(("url", "{}{}".format(config.TMDb.image_url, config.TMDb.get_person(util.regex_first_int(data[m], "TMDb Person ID")).profile_path), method_name)) + self.posters.append(("url", f"{config.TMDb.image_url}{config.TMDb.get_person(util.regex_first_int(data[m], 'TMDb Person ID')).profile_path}", method_name)) elif method_name == "file_poster": if os.path.exists(data[m]): self.posters.append(("file", os.path.abspath(data[m]), method_name)) - else: raise Failed("Collection Error: Poster Path Does Not Exist: {}".format(os.path.abspath(data[m]))) + else: raise Failed(f"Collection Error: Poster Path Does Not Exist: {os.path.abspath(data[m])}") elif method_name == "url_background": self.backgrounds.append(("url", data[m], method_name)) elif method_name == "tmdb_background": - self.backgrounds.append(("url", "{}{}".format(config.TMDb.image_url, config.TMDb.get_movie_show_or_collection(util.regex_first_int(data[m], "TMDb ID"), self.library.is_movie).poster_path), method_name)) + self.backgrounds.append(("url", f"{config.TMDb.image_url}{config.TMDb.get_movie_show_or_collection(util.regex_first_int(data[m], 'TMDb ID'), self.library.is_movie).poster_path}", method_name)) elif method_name == "file_background": if os.path.exists(data[m]): self.backgrounds.append(("file", os.path.abspath(data[m]), method_name)) - else: raise Failed("Collection Error: Background Path Does Not Exist: {}".format(os.path.abspath(data[m]))) + else: raise Failed(f"Collection Error: Background Path Does Not Exist: {os.path.abspath(data[m])}") elif method_name == "label_sync_mode": if data[m] in ["append", "sync"]: self.details[method_name] = data[m] else: raise Failed("Collection Error: label_sync_mode attribute must be either 'append' or 'sync'") @@ -250,7 +250,7 @@ class CollectionBuilder: self.details[method_name] = util.get_list(data[m]) elif method_name in util.boolean_details: if isinstance(data[m], bool): self.details[method_name] = data[m] - else: raise Failed("Collection Error: {} attribute must be either true or false".format(method_name)) + else: raise Failed(f"Collection Error: {method_name} attribute must be either true or false") elif method_name in util.all_details: self.details[method_name] = data[m] elif method_name in ["year", "year.not"]: @@ -302,24 +302,24 @@ class CollectionBuilder: elif method_name in util.dictionary_lists: if isinstance(data[m], dict): 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)) + if method not in data_in: logger.warning(f"Collection Warning: {parent} {method} attribute not found using {default} as default") + elif not data_in[method]: logger.warning(f"Collection Warning: {parent} {method} attribute is blank using {default} as 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)) + else: logger.warning(f"Collection Warning: {parent} {method} attribute {data_in[method]} invalid must an integer <= {maximum} using {default} as default") + else: logger.warning(f"Collection Warning: {parent} {method} attribute {data_in[method]} invalid must an integer >= {minimum} using {default} as 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_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)) + logger.warning(f"Collection Warning: {f} filter will run as {filter_method}") else: 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)) + if filter_method in util.movie_only_filters and self.library.is_show: raise Failed(f"Collection Error: {filter_method} filter only works for movie libraries") + elif data[m][f] is None: raise Failed(f"Collection Error: {filter_method} filter is blank") + elif filter_method in util.all_filters: self.filters.append((filter_method, data[m][f])) + else: raise Failed(f"Collection Error: {filter_method} filter not supported") elif method_name == "plex_collectionless": new_dictionary = {} prefix_list = [] @@ -343,13 +343,13 @@ class CollectionBuilder: for s in data[m]: if s in util.method_alias or (s.endswith(".not") and s[:-4] in util.method_alias): search = (util.method_alias[s[:-4]] + s[-4:]) if s.endswith(".not") else util.method_alias[s] - logger.warning("Collection Warning: {} plex search attribute will run as {}".format(s, search)) + logger.warning(f"Collection Warning: {s} plex search attribute will run as {search}") else: search = s if search in util.movie_only_searches and self.library.is_show: - raise Failed("Collection Error: {} plex search attribute only works for movie libraries".format(search)) + raise Failed(f"Collection Error: {search} plex search attribute only works for movie libraries") elif util.remove_not(search) in used: - raise Failed("Collection Error: Only one instance of {} can be used try using it as a filter instead".format(search)) + raise Failed(f"Collection Error: Only one instance of {search} can be used try using it as a filter instead") elif search in ["year", "year.not"]: years = util.get_year_list(data[m][s], search) if len(years) > 0: @@ -359,7 +359,7 @@ class CollectionBuilder: used.append(util.remove_not(search)) searches.append((search, util.get_list(data[m][s]))) else: - logger.error("Collection Error: {} plex search attribute not supported".format(search)) + logger.error(f"Collection Error: {search} plex search attribute not supported") self.methods.append((method_name, [searches])) elif method_name == "tmdb_discover": new_dictionary = {"limit": 100} @@ -371,71 +371,71 @@ 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(f"Collection Error: {m} attribute {attr}: {attr_data} must match pattern ([a-z]{{2}})-([A-Z]{{2}}) e.g. en-US") 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(f"Collection Error: {m} attribute {attr}: {attr_data} must match pattern ^[A-Z]{{2}}$ e.g. US") 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 else: - raise Failed("Collection Error: {} attribute {}: {} is invalid".format(m, attr, attr_data)) + raise Failed(f"Collection Error: {m} attribute {attr}: {attr_data} is invalid") elif attr == "certification_country": if "certification" in data[m] or "certification.lte" in data[m] or "certification.gte" in data[m]: new_dictionary[attr] = attr_data else: - raise Failed("Collection Error: {} attribute {}: must be used with either certification, certification.lte, or certification.gte".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr}: must be used with either certification, certification.lte, or certification.gte") elif attr in ["certification", "certification.lte", "certification.gte"]: if "certification_country" in data[m]: new_dictionary[attr] = attr_data else: - raise Failed("Collection Error: {} attribute {}: must be used with certification_country".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr}: must be used with certification_country") elif attr in ["include_adult", "include_null_first_air_dates", "screened_theatrically"]: if attr_data is True: new_dictionary[attr] = attr_data elif attr in ["primary_release_date.gte", "primary_release_date.lte", "release_date.gte", "release_date.lte", "air_date.gte", "air_date.lte", "first_air_date.gte", "first_air_date.lte"]: if re.compile("[0-1]?[0-9][/-][0-3]?[0-9][/-][1-2][890][0-9][0-9]").match(str(attr_data)): the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-") - new_dictionary[attr] = "{}-{}-{}".format(the_date[2], the_date[0], the_date[1]) + new_dictionary[attr] = f"{the_date[2]}-{the_date[0]}-{the_date[1]}" elif re.compile("[1-2][890][0-9][0-9][/-][0-1]?[0-9][/-][0-3]?[0-9]").match(str(attr_data)): the_date = str(attr_data).split("/") if "/" in str(attr_data) else str(attr_data).split("-") - new_dictionary[attr] = "{}-{}-{}".format(the_date[0], the_date[1], the_date[2]) + new_dictionary[attr] = f"{the_date[0]}-{the_date[1]}-{the_date[2]}" else: - raise Failed("Collection Error: {} attribute {}: {} must match pattern MM/DD/YYYY e.g. 12/25/2020".format(m, attr, attr_data)) + raise Failed(f"Collection Error: {m} attribute {attr}: {attr_data} must match pattern MM/DD/YYYY e.g. 12/25/2020") elif attr in ["primary_release_year", "year", "first_air_date_year"]: 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)) + raise Failed(f"Collection Error: {m} attribute {attr}: must be a valid year e.g. 1990") elif attr in ["vote_count.gte", "vote_count.lte", "vote_average.gte", "vote_average.lte", "with_runtime.gte", "with_runtime.lte"]: if (isinstance(attr_data, int) or isinstance(attr_data, float)) and 0 < attr_data: new_dictionary[attr] = attr_data else: - raise Failed("Collection Error: {} attribute {}: must be a valid number greater then 0".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr}: must be a valid number greater then 0") elif attr in ["with_cast", "with_crew", "with_people", "with_companies", "with_networks", "with_genres", "without_genres", "with_keywords", "without_keywords", "with_original_language", "timezone"]: new_dictionary[attr] = attr_data else: - raise Failed("Collection Error: {} attribute {} not supported".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr} not supported") elif attr == "limit": if isinstance(attr_data, int) and attr_data > 0: new_dictionary[attr] = attr_data else: - raise Failed("Collection Error: {} attribute {}: must be a valid number greater then 0".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr}: must be a valid number greater then 0") else: - raise Failed("Collection Error: {} attribute {} not supported".format(m, attr)) + raise Failed(f"Collection Error: {m} attribute {attr} not supported") else: - raise Failed("Collection Error: {} parameter {} is blank".format(m, attr)) + raise Failed(f"Collection Error: {m} parameter {attr} is blank") if len(new_dictionary) > 1: self.methods.append((method_name, [new_dictionary])) else: - raise Failed("Collection Error: {} had no valid fields".format(m)) + raise Failed(f"Collection Error: {m} had no valid fields") elif "tautulli" in method_name: new_dictionary = {} if method_name == "tautulli_popular": new_dictionary["list_type"] = "popular" elif method_name == "tautulli_watched": new_dictionary["list_type"] = "watched" - else: raise Failed("Collection Error: {} attribute not supported".format(method_name)) + else: raise Failed(f"Collection Error: {method_name} attribute not supported") new_dictionary["list_days"] = get_int(method_name, "list_days", data[m], 30) new_dictionary["list_size"] = get_int(method_name, "list_size", data[m], 10) @@ -445,7 +445,7 @@ class CollectionBuilder: new_dictionary = {"sort_by": "anime_num_list_users"} if "sort_by" not in data[m]: logger.warning("Collection Warning: mal_season sort_by attribute not found using members as default") elif not data[m]["sort_by"]: logger.warning("Collection Warning: mal_season sort_by attribute is blank using members as default") - elif data[m]["sort_by"] not in util.mal_season_sort: logger.warning("Collection Warning: mal_season sort_by attribute {} invalid must be either 'members' or 'score' using members as default".format(data[m]["sort_by"])) + elif data[m]["sort_by"] not in util.mal_season_sort: logger.warning(f"Collection Warning: mal_season sort_by attribute {data[m]['sort_by']} invalid must be either 'members' or 'score' using members as default") else: new_dictionary["sort_by"] = util.mal_season_sort[data[m]["sort_by"]] current_time = datetime.now() @@ -454,9 +454,9 @@ class CollectionBuilder: elif current_time.month in [7, 8, 9]: new_dictionary["season"] = "summer" elif current_time.month in [10, 11, 12]: new_dictionary["season"] = "fall" - if "season" not in data[m]: logger.warning("Collection Warning: mal_season season attribute not found using the current season: {} as default".format(new_dictionary["season"])) - elif not data[m]["season"]: logger.warning("Collection Warning: mal_season season attribute is blank using the current season: {} as default".format(new_dictionary["season"])) - 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"])) + if "season" not in data[m]: logger.warning(f"Collection Warning: mal_season season attribute not found using the current season: {new_dictionary['season']} as default") + elif not data[m]["season"]: logger.warning(f"Collection Warning: mal_season season attribute is blank using the current season: {new_dictionary['season']} as default") + elif data[m]["season"] not in util.pretty_seasons: logger.warning(f"Collection Warning: mal_season season attribute {data[m]['season']} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {new_dictionary['season']} as default") else: new_dictionary["season"] = data[m]["season"] new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, minimum=1917, maximum=current_time.year + 1) @@ -470,35 +470,35 @@ class CollectionBuilder: if "status" not in data[m]: logger.warning("Collection Warning: mal_season status attribute not found using all as default") elif not data[m]["status"]: logger.warning("Collection Warning: mal_season status attribute is blank using all as default") - elif data[m]["status"] not in util.mal_userlist_status: logger.warning("Collection Warning: mal_season status attribute {} invalid must be either 'all', 'watching', 'completed', 'on_hold', 'dropped' or 'plan_to_watch' using all as default".format(data[m]["status"])) + elif data[m]["status"] not in util.mal_userlist_status: logger.warning(f"Collection Warning: mal_season status attribute {data[m]['status']} invalid must be either 'all', 'watching', 'completed', 'on_hold', 'dropped' or 'plan_to_watch' using all as default") else: new_dictionary["status"] = util.mal_userlist_status[data[m]["status"]] if "sort_by" not in data[m]: logger.warning("Collection Warning: mal_season sort_by attribute not found using score as default") elif not data[m]["sort_by"]: logger.warning("Collection Warning: mal_season sort_by attribute is blank using score as default") - 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"])) + elif data[m]["sort_by"] not in util.mal_userlist_sort: logger.warning(f"Collection Warning: mal_season sort_by attribute {data[m]['sort_by']} invalid must be either 'score', 'last_updated', 'title' or 'start_date' using score as default") else: new_dictionary["sort_by"] = util.mal_userlist_sort[data[m]["sort_by"]] 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])) + raise Failed(f"Collection Error: {m} attribute is not a dictionary: {data[m]}") elif method_name in util.count_lists: list_count = util.regex_first_int(data[m], "List Size", default=20) if list_count < 1: - logger.warning("Collection Warning: {} must be an integer greater then 0 defaulting to 20".format(method_name)) + logger.warning(f"Collection Warning: {method_name} must be an integer greater then 0 defaulting to 20") list_count = 20 self.methods.append((method_name, [list_count])) elif method_name in util.tmdb_lists: - values = config.TMDb.validate_tmdb_list(util.get_int_list(data[m], "TMDb {} ID".format(util.tmdb_type[method_name])), util.tmdb_type[method_name]) + values = config.TMDb.validate_tmdb_list(util.get_int_list(data[m], f"TMDb {util.tmdb_type[method_name]} ID"), util.tmdb_type[method_name]) if method_name[-8:] == "_details": if method_name in ["tmdb_collection_details", "tmdb_movie_details", "tmdb_show_details"]: item = config.TMDb.get_movie_show_or_collection(values[0], self.library.is_movie) if "summary" not in self.details and hasattr(item, "overview") and item.overview: self.details["summary"] = item.overview if "background" not in self.details and hasattr(item, "backdrop_path") and item.backdrop_path: - self.details["background"] = ("url", "{}{}".format(config.TMDb.image_url, item.backdrop_path), method_name[:-8]) + self.details["background"] = ("url", f"{config.TMDb.image_url}{item.backdrop_path}", method_name[:-8]) if "poster" not in self.details and hasattr(item, "poster_path") and item.poster_path: - self.details["poster"] = ("url", "{}{}".format(config.TMDb.image_url, item.poster_path), method_name[:-8]) + self.details["poster"] = ("url", f"{config.TMDb.image_url}{item.poster_path}", method_name[:-8]) else: item = config.TMDb.get_list(values[0]) if "summary" not in self.details and hasattr(item, "description") and item.description: @@ -509,9 +509,9 @@ class CollectionBuilder: elif method_name in util.all_lists: self.methods.append((method_name, util.get_list(data[m]))) elif method_name not in util.other_attributes: - raise Failed("Collection Error: {} attribute not supported".format(method_name)) + raise Failed(f"Collection Error: {method_name} attribute not supported") else: - raise Failed("Collection Error: {} attribute is blank".format(m)) + raise Failed(f"Collection Error: {m} attribute is blank") self.do_arr = False if self.library.Radarr: @@ -523,8 +523,8 @@ class CollectionBuilder: items_found = 0 for method, values in self.methods: logger.debug("") - logger.debug("Method: {}".format(method)) - logger.debug("Values: {}".format(values)) + logger.debug(f"Method: {method}") + logger.debug(f"Values: {values}") pretty = util.pretty_names[method] if method in util.pretty_names else method for value in values: items = [] @@ -545,9 +545,9 @@ class CollectionBuilder: else: missing_shows.append(show_id) return items_found_inside logger.info("") - logger.debug("Value: {}".format(value)) + logger.debug(f"Value: {value}") if method == "plex_all": - logger.info("Processing {} {}".format(pretty, "Movies" if self.library.is_movie else "Shows")) + logger.info(f"Processing {pretty} {'Movies' if self.library.is_movie else 'Shows'}") items = self.library.Plex.all() items_found += len(items) elif method == "plex_collection": @@ -563,8 +563,9 @@ class CollectionBuilder: search_terms[final_method] = search_list ors = "" for o, param in enumerate(attr_pair[1]): - ors += "{}{}".format(" OR " if o > 0 else "{}(".format(attr_pair[0]), param) - logger.info("\t\t AND {})".format(ors) if i > 0 else "Processing {}: {})".format(pretty, ors)) + or_des = " OR " if o > 0 else f"{attr_pair[0]}(" + ors += f"{or_des}{param}" + logger.info(f"\t\t AND {ors})" if i > 0 else f"Processing {pretty}: {ors})") items = self.library.Plex.search(**search_terms) items_found += len(items) elif method == "plex_collectionless": @@ -585,7 +586,7 @@ class CollectionBuilder: all_items = self.library.Plex.all() length = 0 for i, item in enumerate(all_items, 1): - length = util.print_return(length, "Processing: {}/{} {}".format(i, len(all_items), item.title)) + length = util.print_return(length, f"Processing: {i}/{len(all_items)} {item.title}") add_item = True for collection in item.collections: if collection.tag.lower() in good_collections: @@ -594,7 +595,7 @@ class CollectionBuilder: if add_item: items.append(item) items_found += len(items) - util.print_end(length, "Processed {} {}".format(len(all_items), "Movies" if self.library.is_movie else "Shows")) + util.print_end(length, f"Processed {len(all_items)} {'Movies' if self.library.is_movie else 'Shows'}") elif "tautulli" in method: items = self.library.Tautulli.get_items(self.library, time_range=value["list_days"], stats_count=value["list_size"], list_type=value["list_type"], stats_count_buffer=value["list_buffer"]) items_found += len(items) @@ -604,7 +605,7 @@ class CollectionBuilder: elif "imdb" in method: items_found += check_map(self.config.IMDb.get_items(method, value, self.library.Plex.language)) elif "tmdb" in method: items_found += check_map(self.config.TMDb.get_items(method, value, self.library.is_movie)) 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)) + else: logger.error(f"Collection Error: {method} method not supported") 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 ") @@ -628,12 +629,12 @@ class CollectionBuilder: if not_lang is None or (not_lang is True and movie.original_language not in terms) or (not_lang is False and movie.original_language in terms): missing_movies_with_names.append((title, missing_id)) if self.details["show_missing"] is True: - logger.info("{} Collection | ? | {} (TMDb: {})".format(collection_name, title, missing_id)) + logger.info(f"{collection_name} Collection | ? | {title} (TMDb: {missing_id})") elif self.details["show_filtered"] is True: - logger.info("{} Collection | X | {} (TMDb: {})".format(collection_name, title, missing_id)) + logger.info(f"{collection_name} Collection | X | {title} (TMDb: {missing_id})") except Failed as e: logger.error(e) - logger.info("{} Movie{} Missing".format(len(missing_movies_with_names), "s" if len(missing_movies_with_names) > 1 else "")) + logger.info(f"{len(missing_movies_with_names)} Movie{'s' if len(missing_movies_with_names) > 1 else ''} Missing") if self.details["save_missing"] is True: self.library.add_missing(collection_name, missing_movies_with_names, True) if self.do_arr and self.library.Radarr: @@ -645,10 +646,10 @@ class CollectionBuilder: title = str(self.config.TVDb.get_series(self.library.Plex.language, tvdb_id=missing_id).title.encode("ascii", "replace").decode()) missing_shows_with_names.append((title, missing_id)) if self.details["show_missing"] is True: - logger.info("{} Collection | ? | {} (TVDB: {})".format(collection_name, title, missing_id)) + logger.info(f"{collection_name} Collection | ? | {title} (TVDB: {missing_id})") except Failed as e: logger.error(e) - logger.info("{} Show{} Missing".format(len(missing_shows_with_names), "s" if len(missing_shows_with_names) > 1 else "")) + logger.info(f"{len(missing_shows_with_names)} Show{'s' if len(missing_shows_with_names) > 1 else ''} Missing") if self.details["save_missing"] is True: self.library.add_missing(collection_name, missing_shows_with_names, False) if self.do_arr and self.library.Sonarr: @@ -659,10 +660,10 @@ class CollectionBuilder: count_removed = 0 for ratingKey, item in rating_key_map.items(): if item is not None: - logger.info("{} Collection | - | {}".format(collection_name, item.title)) + logger.info(f"{collection_name} Collection | - | {item.title}") item.removeCollection(collection_name) count_removed += 1 - logger.info("{} {}{} Removed".format(count_removed, "Movie" if self.library.is_movie else "Show", "s" if count_removed == 1 else "")) + logger.info(f"{count_removed} {'Movie' if self.library.is_movie else 'Show'}{'s' if count_removed == 1 else ''} Removed") logger.info("") def update_details(self, collection): @@ -692,10 +693,10 @@ class CollectionBuilder: if "label_sync_mode" in self.details and self.details["label_sync_mode"] == "sync": for label in (la for la in item_labels if la not in labels): collection.removeLabel(label) - logger.info("Detail: Label {} removed".format(label)) + logger.info(f"Detail: Label {label} removed") for label in (la for la in labels if la not in item_labels): collection.addLabel(label) - logger.info("Detail: Label {} added".format(label)) + logger.info(f"Detail: Label {label} added") if self.library.asset_directory: name_mapping = self.name @@ -703,14 +704,14 @@ class CollectionBuilder: if self.details["name_mapping"]: name_mapping = self.details["name_mapping"] else: logger.error("Collection Error: name_mapping attribute is blank") for ad in self.library.asset_directory: - path = os.path.join(ad, "{}".format(name_mapping)) + path = os.path.join(ad, f"{name_mapping}") if not os.path.isdir(path): continue - matches = glob.glob(os.path.join(ad, "{}".format(name_mapping), "poster.*")) + matches = glob.glob(os.path.join(ad, f"{name_mapping}", "poster.*")) if len(matches) > 0: for match in matches: self.posters.append(("file", os.path.abspath(match), "asset_directory")) - matches = glob.glob(os.path.join(ad, "{}".format(name_mapping), "background.*")) + matches = glob.glob(os.path.join(ad, f"{name_mapping}", "background.*")) if len(matches) > 0: for match in matches: self.backgrounds.append(("file", os.path.abspath(match), "asset_directory")) @@ -725,25 +726,25 @@ class CollectionBuilder: background_path = os.path.abspath(matches[0]) if len(matches) > 0 else None if poster_path: item.uploadPoster(filepath=poster_path) - logger.info("Detail: asset_directory updated {}'s poster to [file] {}".format(item.title, poster_path)) + logger.info(f"Detail: asset_directory updated {item.title}'s poster to [file] {poster_path}") if background_path: item.uploadArt(filepath=background_path) - logger.info("Detail: asset_directory updated {}'s background to [file] {}".format(item.title, background_path)) + logger.info(f"Detail: asset_directory updated {item.title}'s background to [file] {background_path}") if poster_path is None and background_path is None: - logger.warning("No Files Found: {}".format(os.path.join(path, folder))) + logger.warning(f"No Files Found: {os.path.join(path, folder)}") else: - logger.warning("No Folder: {}".format(os.path.join(path, folder))) + logger.warning(f"No Folder: {os.path.join(path, folder)}") poster = util.choose_from_list(self.posters, "poster", list_type="tuple") if not poster and "poster" in self.details: poster = self.details["poster"] if poster: if poster[0] == "url": collection.uploadPoster(url=poster[1]) else: collection.uploadPoster(filepath=poster[1]) - logger.info("Detail: {} updated collection poster to [{}] {}".format(poster[2], poster[0], poster[1])) + logger.info(f"Detail: {poster[2]} updated collection poster to [{poster[0]}] {poster[1]}") background = util.choose_from_list(self.backgrounds, "background", list_type="tuple") if not background and "background" in self.details: background = self.details["background"] if background: if background[0] == "url": collection.uploadArt(url=background[1]) else: collection.uploadArt(filepath=background[1]) - logger.info("Detail: {} updated collection background to [{}] {}".format(background[2], background[0], background[1])) + logger.info(f"Detail: {background[2]} updated collection background to [{background[0]}] {background[1]}") diff --git a/modules/cache.py b/modules/cache.py index de2eaa89..2be4cd4f 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -6,13 +6,13 @@ logger = logging.getLogger("Plex Meta Manager") class Cache: def __init__(self, config_path, expiration): - cache = "{}.cache".format(os.path.splitext(config_path)[0]) + cache = f"{os.path.splitext(config_path)[0]}.cache" with sqlite3.connect(cache) as connection: connection.row_factory = sqlite3.Row with closing(connection.cursor()) as cursor: cursor.execute("SELECT count(name) FROM sqlite_master WHERE type='table' AND name='guids'") if cursor.fetchone()[0] == 0: - logger.info("Initializing cache database at {}".format(cache)) + logger.info(f"Initializing cache database at {cache}") cursor.execute( """CREATE TABLE IF NOT EXISTS guids ( INTEGER PRIMARY KEY, @@ -34,7 +34,7 @@ class Cache: media_type TEXT)""" ) else: - logger.info("Using cache database at {}".format(cache)) + logger.info(f"Using cache database at {cache}") self.expiration = expiration self.cache_path = cache @@ -73,7 +73,7 @@ class Cache: with sqlite3.connect(self.cache_path) as connection: connection.row_factory = sqlite3.Row with closing(connection.cursor()) as cursor: - cursor.execute("SELECT * FROM guids WHERE {} = ? AND media_type = ?".format(from_id), (key, media_type)) + cursor.execute(f"SELECT * FROM guids WHERE {from_id} = ? AND media_type = ?", (key, media_type)) row = cursor.fetchone() if row and row[to_id]: datetime_object = datetime.strptime(row["expiration_date"], "%Y-%m-%d") diff --git a/modules/config.py b/modules/config.py index 66e8de47..50d10706 100644 --- a/modules/config.py +++ b/modules/config.py @@ -23,10 +23,10 @@ class Config: def __init__(self, default_dir, config_path=None): logger.info("Locating config...") if config_path and os.path.exists(config_path): self.config_path = os.path.abspath(config_path) - elif config_path and not os.path.exists(config_path): raise Failed("Config Error: config not found at {}".format(os.path.abspath(config_path))) + elif config_path and not os.path.exists(config_path): raise Failed(f"Config Error: config not found at {os.path.abspath(config_path)}") elif os.path.exists(os.path.join(default_dir, "config.yml")): self.config_path = os.path.abspath(os.path.join(default_dir, "config.yml")) - else: raise Failed("Config Error: config not found at {}".format(os.path.abspath(default_dir))) - logger.info("Using {} as config".format(self.config_path)) + else: raise Failed(f"Config Error: config not found at {os.path.abspath(default_dir)}") + logger.info(f"Using {self.config_path} as config") yaml.YAML().allow_duplicate_keys = True try: @@ -74,7 +74,7 @@ class Config: yaml.round_trip_dump(new_config, open(self.config_path, "w"), indent=ind, block_seq_indent=bsi) self.data = new_config except yaml.scanner.ScannerError as e: - raise Failed("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) + raise Failed(f"YAML Error: {util.tab_new_lines(e)}") 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): endline = "" @@ -85,28 +85,28 @@ class Config: data = None do_print = False save = False - text = "{} attribute".format(attribute) if parent is None else "{} sub-attribute {}".format(parent, attribute) + text = f"{attribute} attribute" if parent is None else f"{parent} sub-attribute {attribute}" if data is None or attribute not in data: - message = "{} not found".format(text) + message = f"{text} not found" if parent and save is True: 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) + endline = f"\n{parent} sub-attribute {attribute} added to config" 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) + else: message = f"{text} is blank" elif var_type == "bool": if isinstance(data[attribute], bool): return data[attribute] - else: message = "{} must be either true or false".format(text) + else: message = f"{text} must be either true or false" elif var_type == "int": if isinstance(data[attribute], int) and data[attribute] > 0: return data[attribute] - else: message = "{} must an integer > 0".format(text) + else: message = f"{text} must an integer > 0" elif var_type == "path": if os.path.exists(os.path.abspath(data[attribute])): return data[attribute] - else: message = "Path {} does not exist".format(os.path.abspath(data[attribute])) + else: message = f"Path {os.path.abspath(data[attribute])} does not exist" elif var_type == "list": return util.get_list(data[attribute]) 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))] @@ -114,26 +114,26 @@ class Config: else: message = "No Paths exist" 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]) + else: message = f"{text}: {data[attribute]} is an invalid input" if var_type == "path" and default and os.path.exists(os.path.abspath(default)): return default elif var_type == "path" and default: default = None if attribute in data and data[attribute]: - message = "neither {} or the default path {} could be found".format(data[attribute], default) + message = f"neither {data[attribute]} or the default path {default} could be found" else: - message = "no {} found and the default path {} could be found".format(text, default) + message = f"no {text} found and the default path {default} could be found" if default is not None or default_is_none: - message = message + " using {} as default".format(default) + message = message + f" using {default} as default" message = message + endline if req_default and default is None: - raise Failed("Config Error: {} attribute must be set under {} globally or under this specific Library".format(attribute, parent)) + raise Failed(f"Config Error: {attribute} attribute must be set under {parent} globally or under this specific Library") if (default is None and not default_is_none) or throw: if len(options) > 0: message = message + "\n" + options - raise Failed("Config Error: {}".format(message)) + raise Failed(f"Config Error: {message}") if do_print: - util.print_multiline("Config Warning: {}".format(message)) + util.print_multiline(f"Config Warning: {message}") if attribute in data and data[attribute] and test_list is not None and data[attribute] not in test_list: util.print_multiline(options) return default @@ -163,7 +163,7 @@ class Config: 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) - logger.info("TMDb Connection {}".format("Failed" if self.TMDb is None else "Successful")) + logger.info(f"TMDb Connection {'Failed' if self.TMDb is None else 'Successful'}") else: raise Failed("Config Error: tmdb attribute not found") @@ -181,7 +181,7 @@ class Config: self.Trakt = TraktAPI(self.trakt, authorization) except Failed as e: logger.error(e) - logger.info("Trakt Connection {}".format("Failed" if self.Trakt is None else "Successful")) + logger.info(f"Trakt Connection {'Failed' if self.Trakt is None else 'Successful'}") else: logger.warning("trakt attribute not found") @@ -200,7 +200,7 @@ class Config: self.MyAnimeList = MyAnimeListAPI(self.mal, self.MyAnimeListIDList, authorization) except Failed as e: logger.error(e) - logger.info("My Anime List Connection {}".format("Failed" if self.MyAnimeList is None else "Successful")) + logger.info(f"My Anime List Connection {'Failed' if self.MyAnimeList is None else 'Successful'}") else: logger.warning("mal attribute not found") @@ -249,10 +249,10 @@ class Config: params = {} if "library_name" in libs[lib] and libs[lib]["library_name"]: params["name"] = str(libs[lib]["library_name"]) - logger.info("Connecting to {} ({}) Library...".format(params["name"], lib)) + logger.info(f"Connecting to {params['name']} ({lib}) Library...") else: params["name"] = str(lib) - logger.info("Connecting to {} Library...".format(params["name"])) + logger.info(f"Connecting to {params['name']} Library...") 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: @@ -265,21 +265,21 @@ class Config: params["save_missing"] = check_for_attribute(libs[lib], "save_missing", parent="settings", var_type="bool", default=self.general["save_missing"], save=False) try: - params["metadata_path"] = check_for_attribute(libs[lib], "metadata_path", var_type="path", default=os.path.join(default_dir, "{}.yml".format(lib)), throw=True) + params["metadata_path"] = check_for_attribute(libs[lib], "metadata_path", var_type="path", default=os.path.join(default_dir, f"{lib}.yml"), throw=True) params["library_type"] = check_for_attribute(libs[lib], "library_type", test_list=["movie", "show"], options=" movie (For Movie Libraries)\n show (For Show Libraries)", throw=True) params["plex"] = {} params["plex"]["url"] = check_for_attribute(libs[lib], "url", parent="plex", default=self.general["plex"]["url"], req_default=True, save=False) params["plex"]["token"] = check_for_attribute(libs[lib], "token", parent="plex", default=self.general["plex"]["token"], req_default=True, save=False) params["plex"]["timeout"] = check_for_attribute(libs[lib], "timeout", parent="plex", var_type="int", default=self.general["plex"]["timeout"], save=False) library = PlexAPI(params, self.TMDb, self.TVDb) - logger.info("{} Library Connection Successful".format(params["name"])) + logger.info(f"{params['name']} Library Connection Successful") except Failed as e: util.print_multiline(e) - logger.info("{} Library Connection Failed".format(params["name"])) + logger.info(f"{params['name']} Library Connection Failed") continue if self.general["radarr"]["url"] or "radarr" in libs[lib]: - logger.info("Connecting to {} library's Radarr...".format(params["name"])) + logger.info(f"Connecting to {params['name']} library's Radarr...") radarr_params = {} try: radarr_params["url"] = check_for_attribute(libs[lib], "url", parent="radarr", default=self.general["radarr"]["url"], req_default=True, save=False) @@ -293,10 +293,10 @@ class Config: library.add_Radarr(RadarrAPI(self.TMDb, radarr_params)) except Failed as e: util.print_multiline(e) - logger.info("{} library's Radarr Connection {}".format(params["name"], "Failed" if library.Radarr is None else "Successful")) + logger.info(f"{params['name']} library's Radarr Connection {'Failed' if library.Radarr is None else 'Successful'}") if self.general["sonarr"]["url"] or "sonarr" in libs[lib]: - logger.info("Connecting to {} library's Sonarr...".format(params["name"])) + logger.info(f"Connecting to {params['name']} library's Sonarr...") sonarr_params = {} try: sonarr_params["url"] = check_for_attribute(libs[lib], "url", parent="sonarr", default=self.general["sonarr"]["url"], req_default=True, save=False) @@ -310,10 +310,10 @@ class Config: library.add_Sonarr(SonarrAPI(self.TVDb, sonarr_params, library.Plex.language)) except Failed as e: util.print_multiline(e) - logger.info("{} library's Sonarr Connection {}".format(params["name"], "Failed" if library.Sonarr is None else "Successful")) + logger.info(f"{params['name']} library's Sonarr Connection {'Failed' if library.Sonarr is None else 'Successful'}") if self.general["tautulli"]["url"] or "tautulli" in libs[lib]: - logger.info("Connecting to {} library's Tautulli...".format(params["name"])) + logger.info(f"Connecting to {params['name']} library's Tautulli...") tautulli_params = {} try: tautulli_params["url"] = check_for_attribute(libs[lib], "url", parent="tautulli", default=self.general["tautulli"]["url"], req_default=True, save=False) @@ -321,14 +321,14 @@ class Config: library.add_Tautulli(TautulliAPI(tautulli_params)) except Failed as e: util.print_multiline(e) - logger.info("{} library's Tautulli Connection {}".format(params["name"], "Failed" if library.Tautulli is None else "Successful")) + logger.info(f"{params['name']} library's Tautulli Connection {'Failed' if library.Tautulli is None else 'Successful'}") self.libraries.append(library) util.separator() if len(self.libraries) > 0: - logger.info("{} Plex Library Connection{} Successful".format(len(self.libraries), "s" if len(self.libraries) > 1 else "")) + logger.info(f"{len(self.libraries)} Plex Library Connection{'s' if len(self.libraries) > 1 else ''} Successful") else: raise Failed("Plex Error: No Plex libraries were found") @@ -338,15 +338,15 @@ class Config: for library in self.libraries: os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout) logger.info("") - util.separator("{} Library".format(library.name)) + util.separator(f"{library.name} Library") try: library.update_metadata(self.TMDb, test) except Failed as e: logger.error(e) logger.info("") - util.separator("{} Library {}Collections".format(library.name, "Test " if test else "")) + util.separator(f"{library.name} Library {'Test ' if test else ''}Collections") 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.separator("Mapping {} Library".format(library.name)) + util.separator(f"Mapping {library.name} Library") logger.info("") movie_map, show_map = self.map_guids(library) for c in collections: @@ -366,7 +366,7 @@ class Config: continue try: logger.info("") - util.separator("{} Collection".format(c)) + util.separator(f"{c} Collection") logger.info("") rating_key_map = {} @@ -399,7 +399,7 @@ class Config: for i, f in enumerate(builder.filters): if i == 0: logger.info("") - logger.info("Collection Filter {}: {}".format(f[0], f[1])) + logger.info(f"Collection Filter {f[0]}: {f[1]}") builder.run_methods(collection_obj, collection_name, rating_key_map, movie_map, show_map) @@ -413,10 +413,10 @@ class Config: except Exception as e: util.print_stacktrace() - logger.error("Unknown Error: {}".format(e)) + logger.error(f"Unknown Error: {e}") if library.show_unmanaged is True and not test and not requested_collections: logger.info("") - util.separator("Unmanaged Collections in {} Library".format(library.name)) + util.separator(f"Unmanaged Collections in {library.name} Library") logger.info("") unmanaged_count = 0 collections_in_plex = [str(plex_col) for plex_col in collections] @@ -433,15 +433,15 @@ class Config: movie_map = {} show_map = {} length = 0 - logger.info("Mapping {} Library: {}".format("Movie" if library.is_movie else "Show", library.name)) + logger.info(f"Mapping {'Movie' if library.is_movie else 'Show'} Library: {library.name}") items = library.Plex.all() for i, item in enumerate(items, 1): - length = util.print_return(length, "Processing: {}/{} {}".format(i, len(items), item.title)) + length = util.print_return(length, f"Processing: {i}/{len(items)} {item.title}") try: id_type, main_id = self.get_id(item, library, length) except BadRequest: util.print_stacktrace() - util.print_end(length, "{} | {} for {} not found".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, item.title)) + 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": @@ -451,7 +451,7 @@ class Config: else: if id_type == "movie": movie_map[main_id] = item.ratingKey elif id_type == "show": show_map[main_id] = item.ratingKey - util.print_end(length, "Processed {} {}".format(len(items), "Movies" if library.is_movie else "Shows")) + util.print_end(length, f"Processed {len(items)} {'Movies' if library.is_movie else 'Shows'}") return movie_map, show_map def get_id(self, item, library, length): @@ -484,10 +484,10 @@ class Config: 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 = "Hama Agent ID: {} not supported".format(check_id) + 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 = "Agent {} not supported".format(item_type) + else: error_message = f"Agent {item_type} not supported" if not error_message: if anidb_id and not tvdb_id: @@ -501,7 +501,7 @@ class Config: ids = self.MyAnimeListIDList.find_mal_ids(mal_id) if "thetvdb_id" in ids and int(ids["thetvdb_id"]) > 0: tvdb_id = int(ids["thetvdb_id"]) elif "themoviedb_id" in ids and int(ids["themoviedb_id"]) > 0: tmdb_id = int(ids["themoviedb_id"]) - else: raise Failed("MyAnimeList Error: MyAnimeList ID: {} has no other IDs associated with it".format(mal_id)) + else: raise Failed(f"MyAnimeList Error: MyAnimeList ID: {mal_id} has no other IDs associated with it") except Failed: pass if mal_id and not tvdb_id: @@ -560,8 +560,8 @@ class Config: elif self.Trakt: api_name = "Trakt" else: api_name = None - if tmdb_id and imdb_id: id_name = "TMDb ID: {} or IMDb ID: {}".format(tmdb_id, imdb_id) - elif imdb_id and tvdb_id: id_name = "IMDb ID: {} or TVDb ID: {}".format(imdb_id, tvdb_id) + 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 = "TMDb ID: {}".format(tmdb_id) elif imdb_id: id_name = "IMDb ID: {}".format(imdb_id) elif tvdb_id: id_name = "TVDb ID: {}".format(tvdb_id) @@ -575,7 +575,7 @@ class Config: if self.Cache and (tmdb_id and library.is_movie) or ((tvdb_id or ((anidb_id or mal_id) and tmdb_id)) and library.is_show): if isinstance(tmdb_id, list): for i in range(len(tmdb_id)): - util.print_end(length, "Cache | {} | {:<46} | {:<6} | {:<10} | {:<6} | {:<5} | {:<5} | {}".format("^" if expired is True else "+", item.guid, tmdb_id[i] if tmdb_id[i] else "None", imdb_id[i] if imdb_id[i] else "None", tvdb_id if tvdb_id else "None", anidb_id if anidb_id else "None", mal_id if mal_id else "None", item.title)) + 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_id[i] if imdb_id[i] else 'None':<10} | {tvdb_id if tvdb_id else 'None':<6} | {anidb_id if anidb_id else 'None':<5} | {mal_id if mal_id else 'None':<5} | {item.title}") self.Cache.update_guid("movie" if library.is_movie else "show", item.guid, tmdb_id[i], imdb_id[i], tvdb_id, anidb_id, mal_id, expired) else: util.print_end(length, "Cache | {} | {:<46} | {:<6} | {:<10} | {:<6} | {:<5} | {:<5} | {}".format("^" if expired is True else "+", item.guid, tmdb_id if tmdb_id else "None", imdb_id if imdb_id else "None", tvdb_id if tvdb_id else "None", anidb_id if anidb_id else "None", mal_id if mal_id else "None", item.title)) diff --git a/modules/imdb.py b/modules/imdb.py index da3c0a89..ba17d336 100644 --- a/modules/imdb.py +++ b/modules/imdb.py @@ -18,21 +18,21 @@ class IMDbAPI: def get_imdb_ids_from_url(self, imdb_url, language, limit): imdb_url = imdb_url.strip() if not imdb_url.startswith("https://www.imdb.com/list/ls") and not imdb_url.startswith("https://www.imdb.com/search/title/?"): - raise Failed("IMDb Error: {} must begin with either:\n| https://www.imdb.com/list/ls (For Lists)\n| https://www.imdb.com/search/title/? (For Searches)".format(imdb_url)) + raise Failed(f"IMDb Error: {imdb_url} must begin with either:\n| https://www.imdb.com/list/ls (For Lists)\n| https://www.imdb.com/search/title/? (For Searches)") if imdb_url.startswith("https://www.imdb.com/list/ls"): try: list_id = re.search("(\\d+)", str(imdb_url)).group(1) - except AttributeError: raise Failed("IMDb Error: Failed to parse List ID from {}".format(imdb_url)) - current_url = "https://www.imdb.com/search/title/?lists=ls{}".format(list_id) + except AttributeError: raise Failed(f"IMDb Error: Failed to parse List ID from {imdb_url}") + current_url = f"https://www.imdb.com/search/title/?lists=ls{list_id}" else: current_url = imdb_url header = {"Accept-Language": language} length = 0 imdb_ids = [] try: results = self.send_request(current_url, header).xpath("//div[@class='desc']/span/text()")[0].replace(",", "") - except IndexError: raise Failed("IMDb Error: Failed to parse URL: {}".format(imdb_url)) + except IndexError: raise Failed(f"IMDb Error: Failed to parse URL: {imdb_url}") try: total = int(re.findall("(\\d+) title", results)[0]) - except IndexError: raise Failed("IMDb Error: No Results at URL: {}".format(imdb_url)) + except IndexError: raise Failed(f"IMDb Error: No Results at URL: {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 limit < 1 or total < limit: limit = total @@ -41,12 +41,12 @@ class IMDbAPI: num_of_pages = math.ceil(int(limit) / 250) for i in range(1, num_of_pages + 1): start_num = (i - 1) * 250 + 1 - length = util.print_return(length, "Parsing Page {}/{} {}-{}".format(i, num_of_pages, start_num, limit if i == num_of_pages else i * 250)) - response = self.send_request("{}&count={}&start={}".format(current_url, remainder if i == num_of_pages else 250, start_num), header) + length = util.print_return(length, f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * 250}") + response = self.send_request(f"{current_url}&count={remainder if i == num_of_pages else 250}&start={start_num}", header) imdb_ids.extend(response.xpath("//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst")) util.print_end(length) if imdb_ids: return imdb_ids - else: raise Failed("IMDb Error: No Movies Found at {}".format(imdb_url)) + else: raise Failed(f"IMDb Error: No Movies Found at {imdb_url}") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_request(self, url, header): @@ -55,34 +55,35 @@ class IMDbAPI: def get_items(self, method, data, language, status_message=True): pretty = util.pretty_names[method] if method in util.pretty_names else method if status_message: - logger.debug("Data: {}".format(data)) + logger.debug(f"Data: {data}") show_ids = [] movie_ids = [] if method == "imdb_id": if status_message: - logger.info("Processing {}: {}".format(pretty, data)) + logger.info(f"Processing {pretty}: {data}") tmdb_id, tvdb_id = self.convert_from_imdb(data, language) if tmdb_id: movie_ids.append(tmdb_id) 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"])) + status = f"{data['limit']} Items at " if data['limit'] > 0 else '' + logger.info(f"Processing {pretty}: {status}{data['url']}") imdb_ids = self.get_imdb_ids_from_url(data["url"], language, data["limit"]) total_ids = len(imdb_ids) length = 0 for i, imdb_id in enumerate(imdb_ids, 1): - length = util.print_return(length, "Converting IMDb ID {}/{}".format(i, total_ids)) + length = util.print_return(length, f"Converting IMDb ID {i}/{total_ids}") try: tmdb_id, tvdb_id = self.convert_from_imdb(imdb_id, language) if tmdb_id: movie_ids.append(tmdb_id) if tvdb_id: show_ids.append(tvdb_id) except Failed as e: logger.warning(e) - util.print_end(length, "Processed {} IMDb IDs".format(total_ids)) + util.print_end(length, f"Processed {total_ids} IMDb IDs") else: - raise Failed("IMDb Error: Method {} not supported".format(method)) + raise Failed(f"IMDb Error: Method {method} not supported") if status_message: - logger.debug("TMDb IDs Found: {}".format(movie_ids)) - logger.debug("TVDb IDs Found: {}".format(show_ids)) + logger.debug(f"TMDb IDs Found: {movie_ids}") + logger.debug(f"TVDb IDs Found: {show_ids}") return movie_ids, show_ids def convert_from_imdb(self, imdb_id, language): @@ -123,7 +124,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(f"IMDb Error: No TMDb ID or TVDb ID found for IMDb: {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 45cd5dc1..ae822b4a 100644 --- a/modules/mal.py +++ b/modules/mal.py @@ -18,13 +18,13 @@ class MyAnimeListIDList: for attrs in self.ids: if from_id in attrs and int(attrs[from_id]) == int(input_id) and to_id in attrs and int(attrs[to_id]) > 0: return int(attrs[to_id]) - raise Failed("MyAnimeList Error: {} ID not found for {}: {}".format(util.pretty_ids[to_id], util.pretty_ids[from_id], input_id)) + raise Failed(f"MyAnimeList Error: {util.pretty_ids[to_id]} ID not found for {util.pretty_ids[from_id]}: {input_id}") def find_mal_ids(self, mal_id): for mal in self.ids: if "mal_id" in mal and int(mal["mal_id"]) == int(mal_id): return mal - raise Failed("MyAnimeList Error: MyAnimeList ID: {} not found".format(mal_id)) + raise Failed(f"MyAnimeList Error: MyAnimeList ID: {mal_id} not found") class MyAnimeListAPI: def __init__(self, params, MyAnimeListIDList_in, authorization=None): @@ -47,9 +47,9 @@ class MyAnimeListAPI: def get_authorization(self): code_verifier = secrets.token_urlsafe(100)[:128] - url = "{}?response_type=code&client_id={}&code_challenge={}".format(self.urls["oauth_authorize"], self.client_id, code_verifier) + url = f"{self.urls['oauth_authorize']}?response_type=code&client_id={self.client_id}&code_challenge={code_verifier}" logger.info("") - logger.info("Navigate to: {}".format(url)) + logger.info(f"Navigate to: {url}") logger.info("") logger.info("Login and click the Allow option. You will then be redirected to a localhost") logger.info("url that most likely won't load, which is fine. Copy the URL and paste it below") @@ -106,7 +106,7 @@ class MyAnimeListAPI: "expires_in": authorization["expires_in"], "refresh_token": authorization["refresh_token"] } - logger.info("Saving authorization information to {}".format(self.config_path)) + logger.info(f"Saving authorization information to {self.config_path}") yaml.round_trip_dump(config, open(self.config_path, "w"), indent=ind, block_seq_indent=bsi) self.authorization = authorization return True @@ -119,8 +119,8 @@ class MyAnimeListAPI: @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def send_request(self, url, authorization=None): new_authorization = authorization if authorization else self.authorization - response = requests.get(url, headers={"Authorization": "Bearer {}".format(new_authorization["access_token"])}).json() - if "error" in response: raise Failed("MyAnimeList Error: {}".format(response["error"])) + response = requests.get(url, headers={"Authorization": f"Bearer {new_authorization['access_token']}"}).json() + if "error" in response: raise Failed(f"MyAnimeList Error: {response['error']}") else: return response def parse_mal_ids(self, data): @@ -131,50 +131,51 @@ class MyAnimeListAPI: return mal_ids def get_username(self): - return self.send_request("{}/@me".format(self.urls["user"]))["name"] + return self.send_request(f"{self.urls['user']}/@me")["name"] def get_ranked(self, ranking_type, limit): - url = "{}?ranking_type={}&limit={}".format(self.urls["ranking"], ranking_type, limit) + url = f"{self.urls['ranking']}?ranking_type={ranking_type}&limit={limit}" return self.parse_mal_ids(self.send_request(url)) def get_season(self, season, year, sort_by, limit): - url = "{}/{}/{}?sort={}&limit={}".format(self.urls["season"], year, season, sort_by, limit) + url = f"{self.urls['season']}/{year}/{season}?sort={sort_by}&limit={limit}" return self.parse_mal_ids(self.send_request(url)) def get_suggestions(self, limit): - url = "{}?limit={}".format(self.urls["suggestions"], limit) + url = f"{self.urls['suggestions']}?limit={limit}" return self.parse_mal_ids(self.send_request(url)) def get_userlist(self, username, status, sort_by, limit): - url = "{}/{}/animelist?{}sort={}&limit={}".format(self.urls["user"], username, "" if status == "all" else "status={}&".format(status), sort_by, limit) + final_status = "" if status == "all" else f"status={status}&" + url = f"{self.urls['user']}/{username}/animelist?{final_status}sort={sort_by}&limit={limit}" return self.parse_mal_ids(self.send_request(url)) def get_items(self, method, data, status_message=True): if status_message: - logger.debug("Data: {}".format(data)) + logger.debug(f"Data: {data}") pretty = util.pretty_names[method] if method in util.pretty_names else method if method == "mal_id": mal_ids = [data] if status_message: - logger.info("Processing {}: {}".format(pretty, data)) + logger.info(f"Processing {pretty}: {data}") elif method in util.mal_ranked_name: mal_ids = self.get_ranked(util.mal_ranked_name[method], data) if status_message: - logger.info("Processing {}: {} Anime".format(pretty, data)) + logger.info(f"Processing {pretty}: {data} Anime") elif method == "mal_season": mal_ids = self.get_season(data["season"], data["year"], data["sort_by"], data["limit"]) if status_message: - logger.info("Processing {}: {} Anime from {} {} sorted by {}".format(pretty, data["limit"], util.pretty_seasons[data["season"]], data["year"], util.mal_pretty[data["sort_by"]])) + logger.info(f"Processing {pretty}: {data['limit']} Anime from {util.pretty_seasons[data['season']]} {data['year']} sorted by {util.mal_pretty[data['sort_by']]}") elif method == "mal_suggested": mal_ids = self.get_suggestions(data) if status_message: - logger.info("Processing {}: {} Anime".format(pretty, data)) + logger.info(f"Processing {pretty}: {data} Anime") elif method == "mal_userlist": mal_ids = self.get_userlist(data["username"], data["status"], data["sort_by"], data["limit"]) if status_message: - logger.info("Processing {}: {} Anime from {}'s {} list sorted by {}".format(pretty, data["limit"], self.get_username() if data["username"] == "@me" else data["username"], util.mal_pretty[data["status"]], util.mal_pretty[data["sort_by"]])) + logger.info(f"Processing {pretty}: {data['limit']} Anime from {self.get_username() if data['username'] == '@me' else data['username']}'s {util.mal_pretty[data['status']]} list sorted by {util.mal_pretty[data['sort_by']]}") else: - raise Failed("MyAnimeList Error: Method {} not supported".format(method)) + raise Failed(f"MyAnimeList Error: Method {method} not supported") show_ids = [] movie_ids = [] for mal_id in mal_ids: @@ -182,12 +183,12 @@ class MyAnimeListAPI: ids = self.MyAnimeListIDList.find_mal_ids(mal_id) if "thetvdb_id" in ids and int(ids["thetvdb_id"]) > 0: show_ids.append(int(ids["thetvdb_id"])) elif "themoviedb_id" in ids and int(ids["themoviedb_id"]) > 0: movie_ids.append(int(ids["themoviedb_id"])) - else: raise Failed("MyAnimeList Error: MyAnimeList ID: {} has no other IDs associated with it".format(mal_id)) + else: raise Failed(f"MyAnimeList Error: MyAnimeList ID: {mal_id} has no other IDs associated with it") except Failed as e: if status_message: logger.error(e) if status_message: - logger.debug("MyAnimeList IDs Found: {}".format(mal_ids)) - logger.debug("Shows Found: {}".format(show_ids)) - logger.debug("Movies Found: {}".format(movie_ids)) + logger.debug(f"MyAnimeList IDs Found: {mal_ids}") + logger.debug(f"Shows Found: {show_ids}") + logger.debug(f"Movies Found: {movie_ids}") return movie_ids, show_ids diff --git a/modules/plex.py b/modules/plex.py index ed618932..2b8c3673 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -15,23 +15,23 @@ class PlexAPI: def __init__(self, params, TMDb, TVDb): 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 ValueError as e: raise Failed(f"Plex Error: {e}") except requests.exceptions.ConnectionError: util.print_stacktrace() raise Failed("Plex Error: Plex url is invalid") self.is_movie = params["library_type"] == "movie" self.is_show = params["library_type"] == "show" self.Plex = next((s for s in self.PlexServer.library.sections() if s.title == params["name"] and ((self.is_movie and isinstance(s, MovieSection)) or (self.is_show and isinstance(s, ShowSection)))), None) - if not self.Plex: raise Failed("Plex Error: Plex Library {} not found".format(params["name"])) + if not self.Plex: raise Failed(f"Plex Error: Plex Library {params['name']} not found") try: self.data, ind, bsi = yaml.util.load_yaml_guess_indent(open(params["metadata_path"], encoding="utf-8")) - except yaml.scanner.ScannerError as e: raise Failed("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) + except yaml.scanner.ScannerError as e: raise Failed(f"YAML Error: {util.tab_new_lines(e)}") def get_dict(attribute): if attribute in self.data: if self.data[attribute]: if isinstance(self.data[attribute], dict): return self.data[attribute] - else: logger.warning("Config Warning: {} must be a dictionary".format(attribute)) - else: logger.warning("Config Warning: {} attribute is blank".format(attribute)) + else: logger.warning(f"Config Warning: {attribute} must be a dictionary") + else: logger.warning(f"Config Warning: {attribute} attribute is blank") return None self.metadata = get_dict("metadata") @@ -43,7 +43,7 @@ class PlexAPI: if params["asset_directory"]: for ad in params["asset_directory"]: - logger.info("Using Asset Directory: {}".format(ad)) + logger.info(f"Using Asset Directory: {ad}") self.TMDb = TMDb self.TVDb = TVDb @@ -51,7 +51,7 @@ class PlexAPI: self.Sonarr = None self.Tautulli = None self.name = params["name"] - self.missing_path = os.path.join(os.path.dirname(os.path.abspath(params["metadata_path"])), "{}_missing.yml".format(os.path.splitext(os.path.basename(params["metadata_path"]))[0])) + self.missing_path = os.path.join(os.path.dirname(os.path.abspath(params["metadata_path"])), f"{os.path.splitext(os.path.basename(params['metadata_path']))[0]}_missing.yml") self.metadata_path = params["metadata_path"] self.asset_directory = params["asset_directory"] self.sync_mode = params["sync_mode"] @@ -93,7 +93,7 @@ class PlexAPI: def get_collection(self, data): collection = util.choose_from_list(self.search(str(data), libtype="collection"), "collection", str(data), exact=True) if collection: return collection - else: raise Failed("Plex Error: Collection {} not found".format(data)) + else: raise Failed(f"Plex Error: Collection {data} not found") def validate_collections(self, collections): valid_collections = [] @@ -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)) + raise Failed(f"Collection Error: No valid Plex Collections in {collections}") return valid_collections def add_missing(self, collection, items, is_movie): @@ -117,7 +117,7 @@ class PlexAPI: try: yaml.round_trip_dump(self.missing, open(self.missing_path, "w")) except yaml.scanner.ScannerError as e: - logger.error("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) + logger.error(f"YAML Error: {util.tab_new_lines(e)}") 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 @@ -129,11 +129,11 @@ class PlexAPI: try: current = self.fetchItem(item.ratingKey if isinstance(item, (Movie, Show)) else int(item)) except (BadRequest, NotFound): - logger.error("Plex Error: Item {} not found".format(item)) + logger.error(f"Plex Error: Item {item} not found") continue match = True if filters: - length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title)) + length = util.print_return(length, f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}") for f in filters: modifier = f[0][-4:] method = util.filter_alias[f[0][:-4]] if modifier in [".not", ".lte", ".gte"] else util.filter_alias[f[0]] @@ -154,7 +154,7 @@ class PlexAPI: except Failed: pass if movie is None: - logger.warning("Filter Error: No TMDb ID found for {}".format(current.title)) + logger.warning(f"Filter Error: No TMDb ID found for {current.title}") continue if (modifier == ".not" and movie.original_language in terms) or (modifier != ".not" and movie.original_language not in terms): match = False @@ -186,15 +186,15 @@ class PlexAPI: if (not list(set(terms) & set(attrs)) and modifier != ".not") or (list(set(terms) & set(attrs)) and modifier == ".not"): match = False break - length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title)) + length = util.print_return(length, f"Filtering {(' ' * (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)) + util.print_end(length, f"{name} Collection | {'=' if current in collection_items else '+'} | {current.title}") 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)) + logger.info(f"{name} Collection | X | {current.title}") + media_type = f"{'Movie' if self.is_movie else 'Show'}{'s' if total > 1 else ''}" + util.print_end(length, f"{total} {media_type} Processed") return rating_key_map def search_item(self, data, year=None): @@ -202,7 +202,7 @@ class PlexAPI: def update_metadata(self, TMDb, test): logger.info("") - util.separator("{} Library Metadata".format(self.name)) + util.separator(f"{self.name} Library Metadata") logger.info("") if not self.metadata: raise Failed("No metadata to edit") @@ -217,7 +217,7 @@ class PlexAPI: 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)) + elif self.metadata[m]["year"] not in range(1800, now.year + 2): logger.error(f"Metadata Error: year attribute must be between 1800-{now.year + 1}") else: year = self.metadata[m]["year"] title = m @@ -228,7 +228,7 @@ class PlexAPI: item = self.search_item(title, year=year) if item is None: - item = self.search_item("{} (SUB)".format(title), year=year) + item = self.search_item(f"{title} (SUB)", year=year) if item is None and "alt_title" in self.metadata[m]: if self.metadata[m]["alt_title"] is None: @@ -238,11 +238,12 @@ class PlexAPI: item = self.search_item(alt_title, year=year) if item is None: - logger.error("Plex Error: Item {} not found".format(m)) - logger.error("Skipping {}".format(m)) + logger.error(f"Plex Error: Item {m} not found") + logger.error(f"Skipping {m}") continue - logger.info("Updating {}: {}...".format("Movie" if self.is_movie else "Show", title)) + item_type = "Movie" if self.is_movie else "Show" + logger.info(f"Updating {item_type}: {title}...") tmdb_item = None try: @@ -267,10 +268,10 @@ class PlexAPI: if key is None: key = name if value is None: value = group[name] if str(current) != str(value): - edits["{}.value".format(key)] = value - edits["{}.locked".format(key)] = 1 + edits[f"{key}.value"] = value + edits[f"{key}.locked"] = 1 else: - logger.error("Metadata Error: {} attribute is blank".format(name)) + logger.error(f"Metadata Error: {name} attribute is blank") add_edit("title", item.title, self.metadata[m], value=title) add_edit("sort_title", item.titleSort, self.metadata[m], key="titleSort") add_edit("originally_available", str(item.originallyAvailableAt)[:-9], self.metadata[m], key="originallyAvailableAt", value=originally_available) @@ -283,16 +284,16 @@ class PlexAPI: add_edit("tagline", item_tagline, self.metadata[m], value=tagline) add_edit("summary", item.summary, self.metadata[m], value=summary) if len(edits) > 0: - logger.debug("Details Update: {}".format(edits)) + logger.debug(f"Details Update: {edits}") try: item.edit(**edits) item.reload() - logger.info("{}: {} Details Update Successful".format("Movie" if self.is_movie else "Show", m)) + logger.info(f"{item_type}: {m} Details Update Successful") except BadRequest: util.print_stacktrace() - logger.error("{}: {} Details Update Failed".format("Movie" if self.is_movie else "Show", m)) + logger.error(f"{item_type}: {m} Details Update Failed") else: - logger.info("{}: {} Details Update Not Needed".format("Movie" if self.is_movie else "Show", m)) + logger.info(f"{item_type}: {m} Details Update Not Needed") genres = [] @@ -311,10 +312,10 @@ class PlexAPI: elif self.metadata[m]["genre_sync_mode"] == "sync": for genre in (g for g in item_genres if g not in genres): item.removeGenre(genre) - logger.info("Detail: Genre {} removed".format(genre)) + logger.info(f"Detail: Genre {genre} removed") for genre in (g for g in genres if g not in item_genres): item.addGenre(genre) - logger.info("Detail: Genre {} added".format(genre)) + logger.info(f"Detail: Genre {genre} added") if "label" in self.metadata[m]: if self.metadata[m]["label"]: @@ -326,10 +327,10 @@ class PlexAPI: elif self.metadata[m]["label_sync_mode"] == "sync": for label in (la for la in item_labels if la not in labels): item.removeLabel(label) - logger.info("Detail: Label {} removed".format(label)) + logger.info(f"Detail: Label {label} removed") for label in (la for la in labels if la not in item_labels): item.addLabel(label) - logger.info("Detail: Label {} added".format(label)) + logger.info(f"Detail: Label {label} added") else: logger.error("Metadata Error: label attribute is blank") @@ -337,10 +338,10 @@ class PlexAPI: if self.metadata[m]["seasons"]: for season_id in self.metadata[m]["seasons"]: logger.info("") - logger.info("Updating season {} of {}...".format(season_id, m)) + logger.info(f"Updating season {season_id} of {m}...") if isinstance(season_id, int): try: season = item.season(season_id) - except NotFound: logger.error("Metadata Error: Season: {} not found".format(season_id)) + except NotFound: logger.error(f"Metadata Error: Season: {season_id} not found") else: if "title" in self.metadata[m]["seasons"][season_id] and self.metadata[m]["seasons"][season_id]["title"]: @@ -351,7 +352,7 @@ class PlexAPI: if self.metadata[m]["seasons"][season_id]["sub"] is None: logger.error("Metadata Error: sub attribute is blank") elif self.metadata[m]["seasons"][season_id]["sub"] is True and "(SUB)" not in title: - title = "{} (SUB)".format(title) + title = f"{title} (SUB)" elif self.metadata[m]["seasons"][season_id]["sub"] is False and title.endswith(" (SUB)"): title = title[:-6] else: @@ -361,18 +362,18 @@ class PlexAPI: add_edit("title", season.title, self.metadata[m]["seasons"][season_id], value=title) add_edit("summary", season.summary, self.metadata[m]["seasons"][season_id]) if len(edits) > 0: - logger.debug("Season: {} Details Update: {}".format(season_id, edits)) + logger.debug(f"Season: {season_id} Details Update: {edits}") try: season.edit(**edits) season.reload() - logger.info("Season: {} Details Update Successful".format(season_id)) + logger.info(f"Season: {season_id} Details Update Successful") except BadRequest: util.print_stacktrace() - logger.error("Season: {} Details Update Failed".format(season_id)) + logger.error(f"Season: {season_id} Details Update Failed") else: - logger.info("Season: {} Details Update Not Needed".format(season_id)) + logger.info(f"Season: {season_id} Details Update Not Needed") else: - logger.error("Metadata Error: Season: {} invalid, it must be an integer".format(season_id)) + logger.error(f"Metadata Error: Season: {season_id} invalid, it must be an integer") else: logger.error("Metadata Error: seasons attribute is blank") @@ -385,9 +386,9 @@ class PlexAPI: output = match.group(0)[1:].split("E" if "E" in m.group(0) else "e") episode_id = int(output[0]) season_id = int(output[1]) - logger.info("Updating episode S{}E{} of {}...".format(episode_id, season_id, m)) + logger.info(f"Updating episode S{episode_id}E{season_id} of {m}...") try: episode = item.episode(season=season_id, episode=episode_id) - except NotFound: logger.error("Metadata Error: episode {} of season {} not found".format(episode_id, season_id)) + except NotFound: logger.error(f"Metadata Error: episode {episode_id} of season {season_id} not found") else: if "title" in self.metadata[m]["episodes"][episode_str] and self.metadata[m]["episodes"][episode_str]["title"]: title = self.metadata[m]["episodes"][episode_str]["title"] @@ -397,7 +398,7 @@ class PlexAPI: if self.metadata[m]["episodes"][episode_str]["sub"] is None: logger.error("Metadata Error: sub attribute is blank") elif self.metadata[m]["episodes"][episode_str]["sub"] is True and "(SUB)" not in title: - title = "{} (SUB)".format(title) + title = f"{title} (SUB)" elif self.metadata[m]["episodes"][episode_str]["sub"] is False and title.endswith(" (SUB)"): title = title[:-6] else: @@ -409,17 +410,18 @@ class PlexAPI: add_edit("originally_available", str(episode.originallyAvailableAt)[:-9], self.metadata[m]["episodes"][episode_str], key="originallyAvailableAt") add_edit("summary", episode.summary, self.metadata[m]["episodes"][episode_str]) if len(edits) > 0: - logger.debug("Season: {} Episode: {} Details Update: {}".format(season_id, episode_id, edits)) + logger.debug(f"Season: {season_id} Episode: {episode_id} Details Update: {edits}") try: episode.edit(**edits) episode.reload() - logger.info("Season: {} Episode: {} Details Update Successful".format(season_id, episode_id)) + logger.info( + f"Season: {season_id} Episode: {episode_id} Details Update Successful") except BadRequest: util.print_stacktrace() - logger.error("Season: {} Episode: {} Details Update Failed".format(season_id, episode_id)) + logger.error(f"Season: {season_id} Episode: {episode_id} Details Update Failed") else: - logger.info("Season: {} Episode: {} Details Update Not Needed".format(season_id, episode_id)) + logger.info(f"Season: {season_id} Episode: {episode_id} Details Update Not Needed") else: - logger.error("Metadata Error: episode {} invalid must have S##E## format".format(episode_str)) + logger.error(f"Metadata Error: episode {episode_str} invalid must have S##E## format") else: logger.error("Metadata Error: episodes attribute is blank") diff --git a/modules/radarr.py b/modules/radarr.py index af7c6dec..f1a891c4 100644 --- a/modules/radarr.py +++ b/modules/radarr.py @@ -7,27 +7,27 @@ logger = logging.getLogger("Plex Meta Manager") class RadarrAPI: def __init__(self, tmdb, params): - self.url_params = {"apikey": "{}".format(params["token"])} - self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") + self.url_params = {"apikey": f"{params['token']}"} + self.base_url = f"{params['url']}/api{'/v3' if params['version'] == 'v3' else ''}/" try: - result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() + result = requests.get(f"{self.base_url}system/status", params=self.url_params).json() except Exception: util.print_stacktrace() - raise Failed("Radarr Error: Could not connect to Radarr at {}".format(params["url"])) + raise Failed(f"Radarr Error: Could not connect to Radarr at {params['url']}") if "error" in result and result["error"] == "Unauthorized": raise Failed("Radarr Error: Invalid API Key") if "version" not in result: raise Failed("Radarr Error: Unexpected Response Check URL") self.quality_profile_id = None profiles = "" - for profile in self.send_get("{}{}".format(self.base_url, "qualityProfile" if params["version"] == "v3" else "profile")): + for profile in self.send_get(f"{self.base_url}{'qualityProfile' if params['version'] == 'v3' else 'profile'}"): if len(profiles) > 0: profiles += ", " profiles += profile["name"] if profile["name"] == params["quality_profile"]: self.quality_profile_id = profile["id"] if not self.quality_profile_id: - raise Failed("Radarr Error: quality_profile: {} does not exist in radarr. Profiles available: {}".format(params["quality_profile"], profiles)) + raise Failed(f"Radarr Error: quality_profile: {params['quality_profile']} does not exist in radarr. Profiles available: {profiles}") self.tmdb = tmdb self.url = params["url"] self.version = params["version"] @@ -39,7 +39,7 @@ class RadarrAPI: def add_tmdb(self, tmdb_ids, tag=None): logger.info("") - logger.debug("TMDb IDs: {}".format(tmdb_ids)) + logger.debug(f"TMDb IDs: {tmdb_ids}") tag_nums = [] add_count = 0 if tag is None: @@ -47,8 +47,8 @@ class RadarrAPI: if tag: tag_cache = {} for label in tag: - self.send_post("{}tag".format(self.base_url), {"label": str(label)}) - for t in self.send_get("{}tag".format(self.base_url)): + self.send_post(f"{self.base_url}tag", {"label": str(label)}) + for t in self.send_get(f"{self.base_url}tag"): tag_cache[t["label"]] = t["id"] for label in tag: if label in tag_cache: @@ -63,20 +63,20 @@ class RadarrAPI: try: year = movie.release_date.split("-")[0] except AttributeError: - logger.error("TMDb Error: No year for ({}) {}".format(tmdb_id, movie.title)) + logger.error(f"TMDb Error: No year for ({tmdb_id}) {movie.title}") continue if year.isdigit() is False: - logger.error("TMDb Error: No release date yet for ({}) {}".format(tmdb_id, movie.title)) + logger.error(f"TMDb Error: No release date yet for ({tmdb_id}) {movie.title}") continue - poster = "https://image.tmdb.org/t/p/original{}".format(movie.poster_path) + poster = f"https://image.tmdb.org/t/p/original{movie.poster_path}" - titleslug = re.sub(r"([^\s\w]|_)+", "", "{} {}".format(movie.title, year)).replace(" ", "-").lower() + titleslug = re.sub(r"([^\s\w]|_)+", "", f"{movie.title} {year}").replace(" ", "-").lower() url_json = { "title": movie.title, - "{}".format("qualityProfileId" if self.version == "v3" else "profileId"): self.quality_profile_id, + f"{'qualityProfileId' if self.version == 'v3' else 'profileId'}": self.quality_profile_id, "year": int(year), "tmdbid": int(tmdb_id), "titleslug": titleslug, @@ -87,17 +87,17 @@ class RadarrAPI: } if tag_nums: url_json["tags"] = tag_nums - response = self.send_post("{}movie".format(self.base_url), url_json) + response = self.send_post(f"{self.base_url}movie", url_json) if response.status_code < 400: - logger.info("Added to Radarr | {:<6} | {}".format(tmdb_id, movie.title)) + logger.info(f"Added to Radarr | {tmdb_id:<6} | {movie.title}") add_count += 1 else: try: - logger.error("Radarr Error: ({}) {}: ({}) {}".format(tmdb_id, movie.title, response.status_code, response.json()[0]["errorMessage"])) + logger.error(f"Radarr Error: ({tmdb_id}) {movie.title}: ({response.status_code}) {response.json()[0]['errorMessage']}") 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 "")) + logger.error(f"Radarr Error: {response.json()}") + logger.info(f"{add_count} Movie{'s' if add_count > 1 else ''} added to Radarr") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_get(self, url): diff --git a/modules/sonarr.py b/modules/sonarr.py index 252ffcf0..e6f9f030 100644 --- a/modules/sonarr.py +++ b/modules/sonarr.py @@ -7,27 +7,27 @@ logger = logging.getLogger("Plex Meta Manager") class SonarrAPI: def __init__(self, tvdb, params, language): - self.url_params = {"apikey": "{}".format(params["token"])} - self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") + self.url_params = {"apikey": f"{params['token']}"} + self.base_url = f"{params['url']}/api{'/v3/' if params['version'] == 'v3' else '/'}" try: - result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() + result = requests.get(f"{self.base_url}system/status", params=self.url_params).json() except Exception: util.print_stacktrace() - raise Failed("Sonarr Error: Could not connect to Sonarr at {}".format(params["url"])) + raise Failed(f"Sonarr Error: Could not connect to Sonarr at {params['url']}") if "error" in result and result["error"] == "Unauthorized": raise Failed("Sonarr Error: Invalid API Key") if "version" not in result: raise Failed("Sonarr Error: Unexpected Response Check URL") self.quality_profile_id = None profiles = "" - for profile in self.send_get("{}{}".format(self.base_url, "qualityProfile" if params["version"] == "v3" else "profile")): + for profile in self.send_get(f"{self.base_url}{'qualityProfile' if params['version'] == 'v3' else 'profile'}"): if len(profiles) > 0: profiles += ", " profiles += profile["name"] if profile["name"] == params["quality_profile"]: self.quality_profile_id = profile["id"] if not self.quality_profile_id: - raise Failed("Sonarr Error: quality_profile: {} does not exist in sonarr. Profiles available: {}".format(params["quality_profile"], profiles)) + raise Failed(f"Sonarr Error: quality_profile: {params['quality_profile']} does not exist in sonarr. Profiles available: {profiles}") self.tvdb = tvdb self.language = language self.url = params["url"] @@ -40,7 +40,7 @@ class SonarrAPI: def add_tvdb(self, tvdb_ids, tag=None): logger.info("") - logger.debug("TVDb IDs: {}".format(tvdb_ids)) + logger.debug(f"TVDb IDs: {tvdb_ids}") tag_nums = [] add_count = 0 if tag is None: @@ -48,8 +48,8 @@ class SonarrAPI: if tag: tag_cache = {} for label in tag: - self.send_post("{}tag".format(self.base_url), {"label": str(label)}) - for t in self.send_get("{}tag".format(self.base_url)): + self.send_post(f"{self.base_url}tag", {"label": str(label)}) + for t in self.send_get(f"{self.base_url}tag"): tag_cache[t["label"]] = t["id"] for label in tag: if label in tag_cache: @@ -65,7 +65,7 @@ class SonarrAPI: url_json = { "title": show.title, - "{}".format("qualityProfileId" if self.version == "v3" else "profileId"): self.quality_profile_id, + f"{'qualityProfileId' if self.version == 'v3' else 'profileId'}": self.quality_profile_id, "languageProfileId": 1, "tvdbId": int(tvdb_id), "titleslug": titleslug, @@ -78,17 +78,17 @@ class SonarrAPI: } if tag_nums: url_json["tags"] = tag_nums - response = self.send_post("{}series".format(self.base_url), url_json) + response = self.send_post(f"{self.base_url}series", url_json) if response.status_code < 400: - logger.info("Added to Sonarr | {:<6} | {}".format(tvdb_id, show.title)) + logger.info(f"Added to Sonarr | {tvdb_id:<6} | {show.title}") add_count += 1 else: try: - logger.error("Sonarr Error: ({}) {}: ({}) {}".format(tvdb_id, show.title, response.status_code, response.json()[0]["errorMessage"])) + logger.error(f"Sonarr Error: ({tvdb_id}) {show.title}: ({response.status_code}) {response.json()[0]['errorMessage']}") 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 "")) + logger.error(f"Sonarr Error: {response.json()}") + logger.info(f"{add_count} Show{'s' if add_count > 1 else ''} added to Sonarr") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_get(self, url): diff --git a/modules/tautulli.py b/modules/tautulli.py index ace482b5..5b9bc517 100644 --- a/modules/tautulli.py +++ b/modules/tautulli.py @@ -8,12 +8,12 @@ logger = logging.getLogger("Plex Meta Manager") class TautulliAPI: def __init__(self, params): try: - response = requests.get("{}/api/v2?apikey={}&cmd=get_library_names".format(params["url"], params["apikey"])).json() + response = requests.get(f"{params['url']}/api/v2?apikey={params['apikey']}&cmd=get_library_names").json() except Exception: util.print_stacktrace() raise Failed("Tautulli Error: Invalid url") if response["response"]["result"] != "success": - raise Failed("Tautulli Error: {}".format(response["response"]["message"])) + raise Failed(f"Tautulli Error: {response['response']['message']}") self.url = params["url"] self.apikey = params["apikey"] @@ -25,9 +25,9 @@ class TautulliAPI: def get_items(self, library, time_range=30, stats_count=20, list_type="popular", stats_count_buffer=20, status_message=True): if status_message: - logger.info("Processing Tautulli Most {}: {} {}".format("Popular" if list_type == "popular" else "Watched", stats_count, "Movies" if library.is_movie else "Shows")) - response = self.send_request("{}/api/v2?apikey={}&cmd=get_home_stats&time_range={}&stats_count={}".format(self.url, self.apikey, time_range, int(stats_count) + int(stats_count_buffer))) - stat_id = "{}_{}".format("popular" if list_type == "popular" else "top", "movies" if library.is_movie else "tv") + logger.info(f"Processing Tautulli Most {'Popular' if list_type == 'popular' else 'Watched'}: {stats_count} {'Movies' if library.is_movie else 'Shows'}") + response = self.send_request(f"{self.url}/api/v2?apikey={self.apikey}&cmd=get_home_stats&time_range={time_range}&stats_count={int(stats_count) + int(stats_count_buffer)}") + stat_id = f"{'popular' if list_type == 'popular' else 'top'}_{'movies' if library.is_movie else 'tv'}" items = None for entry in response["response"]["data"]: @@ -47,16 +47,16 @@ class TautulliAPI: return rating_keys def get_section_id(self, library_name): - response = self.send_request("{}/api/v2?apikey={}&cmd=get_library_names".format(self.url, self.apikey)) + response = self.send_request(f"{self.url}/api/v2?apikey={self.apikey}&cmd=get_library_names") section_id = None for entry in response["response"]["data"]: if entry["section_name"] == library_name: section_id = entry["section_id"] break if section_id: return section_id - else: raise Failed("Tautulli Error: No Library named {} in the response".format(library_name)) + else: raise Failed(f"Tautulli Error: No Library named {library_name} in the response") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_request(self, url): - logger.debug("Tautulli URL: {}".format(url.replace(self.apikey, "################################"))) + logger.debug(f"Tautulli URL: {url.replace(self.apikey, '################################')}") return requests.get(url).json() diff --git a/modules/tests.py b/modules/tests.py index 4ffb2b33..d3452e01 100644 --- a/modules/tests.py +++ b/modules/tests.py @@ -32,56 +32,56 @@ def anidb_tests(config): logger.info("Success | Convert AniDB to TVDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert AniDB to TVDb: {}".format(e)) + logger.error(f"Failure | Convert AniDB to TVDb: {e}") try: config.AniDB.convert_anidb_to_imdb(112) logger.info("Success | Convert AniDB to IMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert AniDB to IMDb: {}".format(e)) + logger.error(f"Failure | Convert AniDB to IMDb: {e}") try: config.AniDB.convert_tvdb_to_anidb(81797) logger.info("Success | Convert TVDb to AniDB") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TVDb to AniDB: {}".format(e)) + logger.error(f"Failure | Convert TVDb to AniDB: {e}") try: config.AniDB.convert_imdb_to_anidb("tt0245429") logger.info("Success | Convert IMDb to AniDB") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert IMDb to AniDB: {}".format(e)) + logger.error(f"Failure | Convert IMDb to AniDB: {e}") try: config.AniDB.get_items("anidb_id", 69, "en", status_message=False) logger.info("Success | Get AniDB ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get AniDB ID: {}".format(e)) + logger.error(f"Failure | Get AniDB ID: {e}") try: config.AniDB.get_items("anidb_relation", 69, "en", status_message=False) logger.info("Success | Get AniDB Relation") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get AniDB Relation: {}".format(e)) + logger.error(f"Failure | Get AniDB Relation: {e}") try: config.AniDB.get_items("anidb_popular", 30, "en", status_message=False) logger.info("Success | Get AniDB Popular") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get AniDB Popular: {}".format(e)) + logger.error(f"Failure | Get AniDB Popular: {e}") try: config.AniDB.validate_anidb_list(["69", "112"], "en") logger.info("Success | Validate AniDB List") except Failed as e: util.print_stacktrace() - logger.error("Failure | Validate AniDB List: {}".format(e)) + logger.error(f"Failure | Validate AniDB List: {e}") else: util.separator("AniDB Not Configured") @@ -92,15 +92,15 @@ def imdb_tests(config): 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") - else: logger.error("Failure | IMDb URL get TMDb IDs: {} Should be 1000".format(len(tmdb_ids))) + else: logger.error(f"Failure | IMDb URL get TMDb IDs: {len(tmdb_ids)} Should be 1000") tmdb_ids, tvdb_ids = config.IMDb.get_items("imdb_list", {"url": "https://www.imdb.com/list/ls026173135/", "limit": 0}, "en", status_message=False) if len(tmdb_ids) == 250: logger.info("Success | IMDb URL get TMDb IDs") - else: logger.error("Failure | IMDb URL get TMDb IDs: {} Should be 250".format(len(tmdb_ids))) + else: logger.error(f"Failure | IMDb URL get TMDb IDs: {len(tmdb_ids)} Should be 250") tmdb_ids, tvdb_ids = config.IMDb.get_items("imdb_id", "tt0814243", "en", status_message=False) if len(tmdb_ids) == 1: logger.info("Success | IMDb ID get TMDb IDs") - else: logger.error("Failure | IMDb ID get TMDb IDs: {} Should be 1".format(len(tmdb_ids))) + else: logger.error(f"Failure | IMDb ID get TMDb IDs: {len(tmdb_ids)} Should be 1") else: util.separator("IMDb Not Configured") @@ -114,35 +114,35 @@ def mal_tests(config): logger.info("Success | Convert MyAnimeList to TVDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert MyAnimeList to TVDb: {}".format(e)) + logger.error(f"Failure | Convert MyAnimeList to TVDb: {e}") try: config.MyAnimeListIDList.convert_mal_to_tmdb(199) logger.info("Success | Convert MyAnimeList to TMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert MyAnimeList to TMDb: {}".format(e)) + logger.error(f"Failure | Convert MyAnimeList to TMDb: {e}") try: config.MyAnimeListIDList.convert_tvdb_to_mal(81797) logger.info("Success | Convert TVDb to MyAnimeList") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TVDb to MyAnimeList: {}".format(e)) + logger.error(f"Failure | Convert TVDb to MyAnimeList: {e}") try: config.MyAnimeListIDList.convert_tmdb_to_mal(129) logger.info("Success | Convert TMDb to MyAnimeList") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TMDb to MyAnimeList: {}".format(e)) + logger.error(f"Failure | Convert TMDb to MyAnimeList: {e}") try: config.MyAnimeListIDList.find_mal_ids(21) logger.info("Success | Find MyAnimeList ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | Find MyAnimeList ID: {}".format(e)) + logger.error(f"Failure | Find MyAnimeList ID: {e}") else: util.separator("MyAnimeListXML Not Configured") @@ -168,10 +168,10 @@ def mal_tests(config): for mal_list_test in mal_list_tests: try: config.MyAnimeList.get_items(mal_list_test[0], mal_list_test[1], status_message=False) - logger.info("Success | Get Anime using {}".format(util.pretty_names[mal_list_test[0]])) + logger.info(f"Success | Get Anime using {util.pretty_names[mal_list_test[0]]}") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Anime using {}: {}".format(util.pretty_names[mal_list_test[0]], e)) + logger.error(f"Failure | Get Anime using {util.pretty_names[mal_list_test[0]]}: {e}") else: util.separator("MyAnimeList Not Configured") @@ -184,21 +184,21 @@ def tautulli_tests(config): logger.info("Success | Get Section ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Section ID: {}".format(e)) + logger.error(f"Failure | Get Section ID: {e}") try: config.libraries[0].Tautulli.get_popular(config.libraries[0], status_message=False) logger.info("Success | Get Popular") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Popular: {}".format(e)) + logger.error(f"Failure | Get Popular: {e}") try: config.libraries[0].Tautulli.get_top(config.libraries[0], status_message=False) logger.info("Success | Get Top") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Top: {}".format(e)) + logger.error(f"Failure | Get Top: {e}") else: util.separator("Tautulli Not Configured") @@ -211,28 +211,28 @@ def tmdb_tests(config): logger.info("Success | Convert IMDb to TMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert IMDb to TMDb: {}".format(e)) + logger.error(f"Failure | Convert IMDb to TMDb: {e}") try: config.TMDb.convert_tmdb_to_imdb(11) logger.info("Success | Convert TMDb to IMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TMDb to IMDb: {}".format(e)) + logger.error(f"Failure | Convert TMDb to IMDb: {e}") try: config.TMDb.convert_imdb_to_tvdb("tt0458290") logger.info("Success | Convert IMDb to TVDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert IMDb to TVDb: {}".format(e)) + logger.error(f"Failure | Convert IMDb to TVDb: {e}") try: config.TMDb.convert_tvdb_to_imdb(83268) logger.info("Success | Convert TVDb to IMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TVDb to IMDb: {}".format(e)) + logger.error(f"Failure | Convert TVDb to IMDb: {e}") tmdb_list_tests = [ ([11], "Movie"), @@ -247,10 +247,10 @@ def tmdb_tests(config): for tmdb_list_test in tmdb_list_tests: try: config.TMDb.validate_tmdb_list(tmdb_list_test[0], tmdb_type=tmdb_list_test[1]) - logger.info("Success | Get TMDb {}".format(tmdb_list_test[1])) + logger.info(f"Success | Get TMDb {tmdb_list_test[1]}") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get TMDb {}: {}".format(tmdb_list_test[1], e)) + logger.error(f"Failure | Get TMDb {tmdb_list_test[1]}: {e}") tmdb_list_tests = [ ("tmdb_discover", {"sort_by": "popularity.desc", "limit": 100}, True), @@ -279,10 +279,10 @@ def tmdb_tests(config): for tmdb_list_test in tmdb_list_tests: try: config.TMDb.get_items(tmdb_list_test[0], tmdb_list_test[1], tmdb_list_test[2], status_message=False) - logger.info("Success | Get {} using {}".format("Movies" if tmdb_list_test[2] else "Shows", util.pretty_names[tmdb_list_test[0]])) + logger.info(f"Success | Get {'Movies' if tmdb_list_test[2] else 'Shows'} using {util.pretty_names[tmdb_list_test[0]]}") except Failed as e: 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)) + logger.error(f"Failure | Get {'Movies' if tmdb_list_test[2] else 'Shows'} using {util.pretty_names[tmdb_list_test[0]]}: {e}") else: util.separator("TMDb Not Configured") @@ -295,63 +295,63 @@ def trakt_tests(config): logger.info("Success | Convert IMDb to TMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert IMDb to TMDb: {}".format(e)) + logger.error(f"Failure | Convert IMDb to TMDb: {e}") try: config.Trakt.convert_tmdb_to_imdb(11) logger.info("Success | Convert TMDb to IMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TMDb to IMDb: {}".format(e)) + logger.error(f"Failure | Convert TMDb to IMDb: {e}") try: config.Trakt.convert_imdb_to_tvdb("tt0458290") logger.info("Success | Convert IMDb to TVDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert IMDb to TVDb: {}".format(e)) + logger.error(f"Failure | Convert IMDb to TVDb: {e}") try: config.Trakt.convert_tvdb_to_imdb(83268) logger.info("Success | Convert TVDb to IMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TVDb to IMDb: {}".format(e)) + logger.error(f"Failure | Convert TVDb to IMDb: {e}") try: config.Trakt.convert_tmdb_to_tvdb(11) logger.info("Success | Convert TMDb to TVDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TMDb to TVDb: {}".format(e)) + logger.error(f"Failure | Convert TMDb to TVDb: {e}") try: config.Trakt.convert_tvdb_to_tmdb(83268) logger.info("Success | Convert TVDb to TMDb") except Failed as e: util.print_stacktrace() - logger.error("Failure | Convert TVDb to TMDb: {}".format(e)) + logger.error(f"Failure | Convert TVDb to TMDb: {e}") try: config.Trakt.validate_trakt_list(["https://trakt.tv/users/movistapp/lists/christmas-movies"]) logger.info("Success | Get List") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get List: {}".format(e)) + logger.error(f"Failure | Get List: {e}") try: config.Trakt.validate_trakt_watchlist(["me"], True) logger.info("Success | Get Watchlist Movies") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Watchlist Movies: {}".format(e)) + logger.error(f"Failure | Get Watchlist Movies: {e}") try: config.Trakt.validate_trakt_watchlist(["me"], False) logger.info("Success | Get Watchlist Shows") except Failed as e: util.print_stacktrace() - logger.error("Failure | Get Watchlist Shows: {}".format(e)) + logger.error(f"Failure | Get Watchlist Shows: {e}") trakt_list_tests = [ ("trakt_list", "https://trakt.tv/users/movistapp/lists/christmas-movies", True), @@ -364,10 +364,10 @@ def trakt_tests(config): for trakt_list_test in trakt_list_tests: try: config.Trakt.get_items(trakt_list_test[0], trakt_list_test[1], trakt_list_test[2], status_message=False) - logger.info("Success | Get {} using {}".format("Movies" if trakt_list_test[2] else "Shows", util.pretty_names[trakt_list_test[0]])) + logger.info(f"Success | Get {'Movies' if trakt_list_test[2] else 'Shows'} using {util.pretty_names[trakt_list_test[0]]}") except Failed as e: 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)) + logger.error(f"Failure | Get {'Movies' if trakt_list_test[2] else 'Shows'} using {util.pretty_names[trakt_list_test[0]]}: {e}") else: util.separator("Trakt Not Configured") @@ -377,39 +377,39 @@ def tvdb_tests(config): 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") - else: logger.error("Failure | TVDb URL get TVDb IDs and TMDb IDs: {} Should be 10 and {} Should be 0".format(len(tvdb_ids), len(tmdb_ids))) + else: logger.error(f"Failure | TVDb URL get TVDb IDs and TMDb IDs: {len(tvdb_ids)} Should be 10 and {len(tmdb_ids)} Should be 0") tmdb_ids, tvdb_ids = config.TVDb.get_items("tvdb_list", "https://www.thetvdb.com/lists/6957", "en", status_message=False) if len(tvdb_ids) == 4 and len(tmdb_ids) == 2: logger.info("Success | TVDb URL get TVDb IDs and TMDb IDs") - else: logger.error("Failure | TVDb URL get TVDb IDs and TMDb IDs: {} Should be 4 and {} Should be 2".format(len(tvdb_ids), len(tmdb_ids))) + else: logger.error(f"Failure | TVDb URL get TVDb IDs and TMDb IDs: {len(tvdb_ids)} Should be 4 and {len(tmdb_ids)} Should be 2") try: config.TVDb.get_items("tvdb_show", "https://www.thetvdb.com/series/arrow", "en", status_message=False) logger.info("Success | TVDb URL get TVDb Series ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | TVDb URL get TVDb Series ID: {}".format(e)) + logger.error(f"Failure | TVDb URL get TVDb Series ID: {e}") try: config.TVDb.get_items("tvdb_show", 279121, "en", status_message=False) logger.info("Success | TVDb ID get TVDb Series ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | TVDb ID get TVDb Series ID: {}".format(e)) + logger.error(f"Failure | TVDb ID get TVDb Series ID: {e}") try: config.TVDb.get_items("tvdb_movie", "https://www.thetvdb.com/movies/the-lord-of-the-rings-the-fellowship-of-the-ring", "en", status_message=False) logger.info("Success | TVDb URL get TVDb Movie ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | TVDb URL get TVDb Movie ID: {}".format(e)) + logger.error(f"Failure | TVDb URL get TVDb Movie ID: {e}") try: config.TVDb.get_items("tvdb_movie", 107, "en", status_message=False) logger.info("Success | TVDb ID get TVDb Movie ID") except Failed as e: util.print_stacktrace() - logger.error("Failure | TVDb ID get TVDb Movie ID: {}".format(e)) + logger.error(f"Failure | TVDb ID get TVDb Movie ID: {e}") else: util.separator("TVDb Not Configured") diff --git a/modules/tmdb.py b/modules/tmdb.py index 157b8f4b..0a2a1a61 100644 --- a/modules/tmdb.py +++ b/modules/tmdb.py @@ -13,7 +13,7 @@ class TMDbAPI: self.TMDb.language = params["language"] response = tmdbv3api.Configuration().info() if hasattr(response, "status_message"): - raise Failed("TMDb Error: {}".format(response.status_message)) + raise Failed(f"TMDb Error: {response.status_message}") self.apikey = params["apikey"] self.language = params["language"] self.Movie = tmdbv3api.Movie() @@ -33,17 +33,17 @@ class TMDbAPI: 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)) + raise Failed(f"TMDb Error: No {convert_to.upper().replace('B_', 'b ')} found for TMDb ID {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)) + 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 convert_to_tmdb(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("TMDb Error: No TMDb ID found for {} {}".format(external_source.upper().replace("B_", "b "), external_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.convert_from_tmdb(tmdb_id, "imdb_id", is_movie) def convert_imdb_to_tmdb(self, imdb_id, is_movie=True): return self.convert_to_tmdb(imdb_id, "imdb_id", is_movie) @@ -57,48 +57,48 @@ class TMDbAPI: try: return self.get_collection(tmdb_id) except Failed: try: return self.get_movie(tmdb_id) - except Failed: raise Failed("TMDb Error: No Movie or Collection found for TMDb ID {}".format(tmdb_id)) + except Failed: raise Failed(f"TMDb Error: No Movie or Collection found for TMDb ID {tmdb_id}") else: return self.get_show(tmdb_id) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_movie(self, tmdb_id): try: return self.Movie.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Movie found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Movie found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_show(self, tmdb_id): try: return self.TV.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Show found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Show found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_collection(self, tmdb_id): try: return self.Collection.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Collection found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Collection found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_person(self, tmdb_id): try: return self.Person.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Person found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Person found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_company(self, tmdb_id): try: return self.Company.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Company found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Company found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_network(self, tmdb_id): try: return self.Network.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Network found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Network found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_keyword(self, tmdb_id): try: return self.Keyword.details(tmdb_id) - except TMDbException as e: raise Failed("TMDb Error: No Keyword found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No Keyword found for TMDb ID {tmdb_id}: {e}") @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def get_list(self, tmdb_id): try: return self.List.details(tmdb_id, all_details=True) - except TMDbException as e: raise Failed("TMDb Error: No List found for TMDb ID {}: {}".format(tmdb_id, e)) + except TMDbException as e: raise Failed(f"TMDb Error: No List found for TMDb ID {tmdb_id}: {e}") def get_pagenation(self, method, amount, is_movie): ids = [] @@ -109,7 +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)) + 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)) @@ -142,7 +142,7 @@ class TMDbAPI: def get_items(self, method, data, is_movie, status_message=True): if status_message: - logger.debug("Data: {}".format(data)) + logger.debug(f"Data: {data}") pretty = util.pretty_names[method] if method in util.pretty_names else method media_type = "Movie" if is_movie else "Show" movie_ids = [] @@ -170,16 +170,16 @@ class TMDbAPI: else: show_ids, amount = self.get_discover(attrs, limit, is_movie) 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")) + logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({amount} {media_type}{'' if amount == 1 else 's'})") elif method == "tmdb_discover": - logger.info("Processing {}: {} {}{}".format(pretty, amount, media_type, "" if amount == 1 else "s")) + logger.info(f"Processing {pretty}: {amount} {media_type}{'' if amount == 1 else 's'}") for attr, value in attrs.items(): - logger.info(" {}: {}".format(attr, value)) + logger.info(f" {attr}: {value}") elif method in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]: if is_movie: movie_ids = self.get_pagenation(method, data, is_movie) else: show_ids = self.get_pagenation(method, data, is_movie) if status_message: - logger.info("Processing {}: {} {}{}".format(pretty, data, media_type, "" if data == 1 else "s")) + logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}") else: tmdb_id = int(data) if method == "tmdb_list": @@ -204,14 +204,14 @@ class TMDbAPI: try: show_ids.append(self.convert_tmdb_to_tvdb(tmdb_id)) except Failed: pass else: - raise Failed("TMDb Error: Method {} not supported".format(method)) + raise Failed(f"TMDb Error: Method {method} not supported") if status_message and len(movie_ids) > 0: - logger.info("Processing {}: ({}) {} ({} Movie{})".format(pretty, tmdb_id, tmdb_name, len(movie_ids), "" if len(movie_ids) == 1 else "s")) + logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({len(movie_ids)} Movie{'' if len(movie_ids) == 1 else 's'})") if status_message and len(show_ids) > 0: - logger.info("Processing {}: ({}) {} ({} Show{})".format(pretty, tmdb_id, tmdb_name, len(show_ids), "" if len(show_ids) == 1 else "s")) + logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({len(show_ids)} Show{'' if len(show_ids) == 1 else 's'})") if status_message: - logger.debug("TMDb IDs Found: {}".format(movie_ids)) - logger.debug("TVDb IDs Found: {}".format(show_ids)) + logger.debug(f"TMDb IDs Found: {movie_ids}") + logger.debug(f"TVDb IDs Found: {show_ids}") return movie_ids, show_ids def validate_tmdb_list(self, tmdb_list, tmdb_type): @@ -219,7 +219,7 @@ class TMDbAPI: for tmdb_id in tmdb_list: try: tmdb_values.append(self.validate_tmdb(tmdb_id, tmdb_type)) except Failed as e: logger.error(e) - if len(tmdb_values) == 0: raise Failed("TMDb Error: No valid TMDb IDs in {}".format(tmdb_list)) + if len(tmdb_values) == 0: raise Failed(f"TMDb Error: No valid TMDb IDs in {tmdb_list}") return tmdb_values def validate_tmdb(self, tmdb_id, tmdb_type): diff --git a/modules/trakttv.py b/modules/trakttv.py index 6d28e3f6..c82d235c 100644 --- a/modules/trakttv.py +++ b/modules/trakttv.py @@ -30,7 +30,7 @@ class TraktAPI: def get_authorization(self): url = Trakt["oauth"].authorize_url(self.redirect_uri) - logger.info("Navigate to: {}".format(url)) + logger.info(f"Navigate to: {url}") logger.info("If you get an OAuth error your client_id or client_secret is invalid") webbrowser.open(url, new=2) try: pin = util.logger_input("Trakt pin (case insensitive)", timeout=300).strip() @@ -70,7 +70,7 @@ class TraktAPI: "scope": authorization["scope"], "created_at": authorization["created_at"] } - logger.info("Saving authorization information to {}".format(self.config_path)) + logger.info(f"Saving authorization information to {self.config_path}") yaml.round_trip_dump(config, open(self.config_path, "w"), indent=ind, block_seq_indent=bsi) self.authorization = authorization Trakt.configuration.defaults.oauth.from_response(self.authorization) @@ -91,7 +91,7 @@ class TraktAPI: lookup = lookup[0] if isinstance(lookup, list) else lookup if lookup.get_key(to_source): return lookup.get_key(to_source) if to_source == "imdb" else int(lookup.get_key(to_source)) - raise Failed("No {} ID found for {} ID {}".format(to_source.upper().replace("B", "b"), from_source.upper().replace("B", "b"), external_id)) + raise Failed(f"No {to_source.upper().replace('B', 'b')} ID found for {from_source.upper().replace('B', 'b')} ID {external_id}") @retry(stop_max_attempt_number=6, wait_fixed=10000) def trending(self, amount, is_movie): @@ -99,7 +99,7 @@ class TraktAPI: @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) def watchlist(self, data, is_movie): - items = Trakt["users/{}/watchlist".format(data)].movies() if is_movie else Trakt["users/{}/watchlist".format(data)].shows() + items = Trakt[f"users/{data}/watchlist"].movies() if is_movie else Trakt[f"users/{data}/watchlist"].shows() if items is None: raise Failed("Trakt Error: No List found") else: return [i for i in items] @@ -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(values)) + raise Failed(f"Trakt Error: No valid Trakt Lists in {values}") return trakt_values def validate_trakt_watchlist(self, values, is_movie): @@ -131,23 +131,23 @@ class TraktAPI: except Failed as e: logger.error(e) if len(trakt_values) == 0: - raise Failed("Trakt Error: No valid Trakt Watchlists in {}".format(values)) + raise Failed(f"Trakt Error: No valid Trakt Watchlists in {values}") return trakt_values def get_items(self, method, data, is_movie, status_message=True): if status_message: - logger.debug("Data: {}".format(data)) + logger.debug(f"Data: {data}") pretty = self.aliases[method] if method in self.aliases else method media_type = "Movie" if is_movie else "Show" if method == "trakt_trending": trakt_items = self.trending(int(data), is_movie) if status_message: - logger.info("Processing {}: {} {}{}".format(pretty, data, media_type, "" if data == 1 else "s")) + logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}") else: if method == "trakt_watchlist": trakt_items = self.watchlist(data, is_movie) elif method == "trakt_list": trakt_items = self.standard_list(data) - else: raise Failed("Trakt Error: Method {} not supported".format(method)) - if status_message: logger.info("Processing {}: {}".format(pretty, data)) + else: raise Failed(f"Trakt Error: Method {method} not supported") + if status_message: logger.info(f"Processing {pretty}: {data}") show_ids = [] movie_ids = [] for trakt_item in trakt_items: @@ -155,7 +155,7 @@ class TraktAPI: elif isinstance(trakt_item, Show) and trakt_item.pk[1] not in show_ids: show_ids.append(int(trakt_item.pk[1])) elif (isinstance(trakt_item, (Season, Episode))) and trakt_item.show.pk[1] not in show_ids: show_ids.append(int(trakt_item.show.pk[1])) if status_message: - logger.debug("Trakt {} Found: {}".format(media_type, trakt_items)) - logger.debug("TMDb IDs Found: {}".format(movie_ids)) - logger.debug("TVDb IDs Found: {}".format(show_ids)) + logger.debug(f"Trakt {media_type} Found: {trakt_items}") + logger.debug(f"TMDb IDs Found: {movie_ids}") + logger.debug(f"TVDb IDs Found: {show_ids}") return movie_ids, show_ids diff --git a/modules/tvdb.py b/modules/tvdb.py index f91bbfe5..bbed87ab 100644 --- a/modules/tvdb.py +++ b/modules/tvdb.py @@ -14,24 +14,24 @@ class TVDbObj: elif is_movie and tvdb_url.startswith((TVDb.movies_url, TVDb.alt_movies_url, TVDb.movie_id_url)): self.media_type = "Movie" else: - raise Failed("TVDb Error: {} must begin with {}".format(tvdb_url, TVDb.movies_url if is_movie else TVDb.series_url)) + raise Failed(f"TVDb Error: {tvdb_url} must begin with {TVDb.movies_url if is_movie else TVDb.series_url}") response = TVDb.send_request(tvdb_url, language) - results = response.xpath("//*[text()='TheTVDB.com {} ID']/parent::node()/span/text()".format(self.media_type)) + results = response.xpath(f"//*[text()='TheTVDB.com {self.media_type} ID']/parent::node()/span/text()") 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):])) + raise Failed(f"TVDb Error: Could not find a TVDb Movie using TVDb Movie ID: {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):])) + raise Failed(f"TVDb Error: Could not find a TVDb Series using TVDb Series ID: {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)) + raise Failed(f"TVDb Error: Could not find a TVDb {self.media_type} ID at the URL {tvdb_url}") results = response.xpath("//div[@class='change_translation_text' and @data-language='eng']/@data-title") if len(results) > 0 and len(results[0]) > 0: self.title = results[0] else: - raise Failed("TVDb Error: Name not found from TVDb URL: {}".format(tvdb_url)) + raise Failed(f"TVDb Error: Name not found from TVDb URL: {tvdb_url}") results = response.xpath("//div[@class='row hidden-xs hidden-sm']/div/img/@src") self.poster_path = results[0] if len(results) > 0 and len(results[0]) > 0 else None @@ -60,27 +60,27 @@ class TVDbAPI: self.Trakt = Trakt self.site_url = "https://www.thetvdb.com" self.alt_site_url = "https://thetvdb.com" - self.list_url = "{}/lists/".format(self.site_url) - self.alt_list_url = "{}/lists/".format(self.alt_site_url) - self.series_url = "{}/series/".format(self.site_url) - self.alt_series_url = "{}/series/".format(self.alt_site_url) - self.movies_url = "{}/movies/".format(self.site_url) - self.alt_movies_url = "{}/movies/".format(self.alt_site_url) - self.series_id_url = "{}/dereferrer/series/".format(self.site_url) - self.movie_id_url = "{}/dereferrer/movie/".format(self.site_url) + self.list_url = f"{self.site_url}/lists/" + self.alt_list_url = f"{self.alt_site_url}/lists/" + self.series_url = f"{self.site_url}/series/" + self.alt_series_url = f"{self.alt_site_url}/series/" + self.movies_url = f"{self.site_url}/movies/" + self.alt_movies_url = f"{self.alt_site_url}/movies/" + self.series_id_url = f"{self.site_url}/dereferrer/series/" + self.movie_id_url = f"{self.site_url}/dereferrer/movie/" def get_series(self, language, tvdb_url=None, tvdb_id=None): if not tvdb_url and not 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) + tvdb_url = f"{self.series_id_url}{tvdb_id}" return TVDbObj(tvdb_url, language, False, self) def get_movie(self, language, tvdb_url=None, tvdb_id=None): if not tvdb_url and not tvdb_id: raise Failed("TVDB Error: get_movie requires either tvdb_url or tvdb_id") elif not tvdb_url and tvdb_id: - tvdb_url = "{}{}".format(self.movie_id_url, tvdb_id) + tvdb_url = f"{self.movie_id_url}{tvdb_id}" return TVDbObj(tvdb_url, language, True, self) def get_tvdb_ids_from_url(self, tvdb_url, language): @@ -94,25 +94,25 @@ class TVDbAPI: title = item.xpath(".//div[@class='col-xs-12 col-sm-9 mt-2']//a/text()")[0] item_url = item.xpath(".//div[@class='col-xs-12 col-sm-9 mt-2']//a/@href")[0] if item_url.startswith("/series/"): - try: show_ids.append(self.get_series(language, tvdb_url="{}{}".format(self.site_url, item_url)).id) - except Failed as e: logger.error("{} for series {}".format(e, title)) + try: show_ids.append(self.get_series(language, tvdb_url=f"{self.site_url}{item_url}").id) + except Failed as e: logger.error(f"{e} for series {title}") elif item_url.startswith("/movies/"): try: - tmdb_id = self.get_movie(language, tvdb_url="{}{}".format(self.site_url, item_url)).tmdb_id + tmdb_id = self.get_movie(language, tvdb_url=f"{self.site_url}{item_url}").tmdb_id if tmdb_id: movie_ids.append(tmdb_id) - else: raise Failed("TVDb Error: TMDb ID not found from TVDb URL: {}".format(tvdb_url)) + else: raise Failed(f"TVDb Error: TMDb ID not found from TVDb URL: {tvdb_url}") except Failed as e: - logger.error("{} for series {}".format(e, title)) + logger.error(f"{e} for series {title}") else: - logger.error("TVDb Error: Skipping Movie: {}".format(title)) + logger.error(f"TVDb Error: Skipping Movie: {title}") 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)) + raise Failed(f"TVDb Error: No TVDb IDs found at {tvdb_url}") except requests.exceptions.MissingSchema: util.print_stacktrace() - raise Failed("TVDb Error: URL Lookup Failed for {}".format(tvdb_url)) + raise Failed(f"TVDb Error: URL Lookup Failed for {tvdb_url}") else: - raise Failed("TVDb Error: {} must begin with {}".format(tvdb_url, self.list_url)) + raise Failed(f"TVDb Error: {tvdb_url} must begin with {self.list_url}") @retry(stop_max_attempt_number=6, wait_fixed=10000) def send_request(self, url, language): @@ -123,7 +123,7 @@ class TVDbAPI: show_ids = [] movie_ids = [] if status_message: - logger.info("Processing {}: {}".format(pretty, data)) + logger.info(f"Processing {pretty}: {data}") if method == "tvdb_show": try: show_ids.append(self.get_series(language, tvdb_id=int(data)).id) except ValueError: show_ids.append(self.get_series(language, tvdb_url=data).id) @@ -135,10 +135,10 @@ class TVDbAPI: movie_ids.extend(tmdb_ids) show_ids.extend(tvdb_ids) else: - raise Failed("TVDb Error: Method {} not supported".format(method)) + raise Failed(f"TVDb Error: Method {method} not supported") if status_message: - logger.debug("TMDb IDs Found: {}".format(movie_ids)) - logger.debug("TVDb IDs Found: {}".format(show_ids)) + logger.debug(f"TMDb IDs Found: {movie_ids}") + logger.debug(f"TVDb IDs Found: {show_ids}") return movie_ids, show_ids def convert_from_imdb(self, imdb_id): @@ -162,7 +162,7 @@ class TVDbAPI: try: if tmdb_id and not from_cache: self.TMDb.get_movie(tmdb_id) except Failed: tmdb_id = None - if not tmdb_id: raise Failed("TVDb Error: No TMDb ID found for IMDb: {}".format(imdb_id)) + if not tmdb_id: raise Failed(f"TVDb Error: No TMDb ID found for IMDb: {imdb_id}") if self.Cache and tmdb_id and update is not False: self.Cache.update_imdb("movie", update, imdb_id, tmdb_id) return tmdb_id diff --git a/modules/util.py b/modules/util.py index 5fe7c6e8..d8793246 100644 --- a/modules/util.py +++ b/modules/util.py @@ -443,6 +443,9 @@ discover_tv_sort = [ "popularity.desc", "popularity.asc" ] +def tab_new_lines(data): + return str(data).replace("\n", "\n|\t ") if "\n" in str(data) else str(data) + def adjust_space(old_length, display_title): display_title = str(display_title) space_length = old_length - len(display_title) @@ -461,30 +464,31 @@ def choose_from_list(datalist, description, data=None, list_type="title", exact= if len(datalist) > 0: if len(datalist) == 1 and (description != "collection" or datalist[0].title == data): return datalist[0] - message = "Multiple {}s Found\n0) {}".format(description, "Create New Collection: {}".format(data) if description == "collection" else "Do Nothing") + zero_option = f"Create New Collection: {data}" if description == "collection" else "Do Nothing" + message = f"Multiple {description}s Found\n0) {zero_option}" for i, d in enumerate(datalist, 1): if list_type == "title": if d.title == data: return d - message += "\n{}) {}".format(i, d.title) + message += f"\n{i}) {d.title}" else: - message += "\n{}) [{}] {}".format(i, d[0], d[1]) + message += f"\n{i}) [{d[0]}] {d[1]}" if exact: return None print_multiline(message, info=True) while True: try: - selection = int(logger_input("Choose {} number".format(description))) - 1 + selection = int(logger_input(f"Choose {description} number")) - 1 if selection >= 0: return datalist[selection] elif selection == -1: return None - else: logger.info("Invalid {} number".format(description)) - except IndexError: logger.info("Invalid {} number".format(description)) + else: logger.info(f"Invalid {description} number") + except IndexError: logger.info(f"Invalid {description} number") except TimeoutExpired: if list_type == "title": - logger.warning("Input Timeout: using {}".format(data)) + logger.warning(f"Input Timeout: using {data}") return None else: - logger.warning("Input Timeout: using {}".format(datalist[0][1])) + logger.warning(f"Input Timeout: using {datalist[0][1]}") return datalist[0][1] else: return None @@ -516,22 +520,22 @@ def get_year_list(data, method): end = year_range.group(2) if end == "NOW": end = current_year - if int(start) < 1800 or int(start) > current_year: logger.error("Collection Error: Skipping {} starting year {} must be between 1800 and {}".format(method, start, current_year)) - elif int(end) < 1800 or int(end) > current_year: logger.error("Collection Error: Skipping {} ending year {} must be between 1800 and {}".format(method, end, current_year)) - elif int(start) > int(end): logger.error("Collection Error: Skipping {} starting year {} cannot be greater then ending year {}".format(method, start, end)) + if int(start) < 1800 or int(start) > current_year: logger.error(f"Collection Error: Skipping {method} starting year {start} must be between 1800 and {current_year}") + elif int(end) < 1800 or int(end) > current_year: logger.error(f"Collection Error: Skipping {method} ending year {end} must be between 1800 and {current_year}") + elif int(start) > int(end): logger.error(f"Collection Error: Skipping {method} starting year {start} cannot be greater then ending year {end}") else: for i in range(int(start), int(end) + 1): final_years.append(i) else: year = re.search("(\\d+)", str(value)).group(1) 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)) + logger.error(f"Collection Error: Skipping {method} year {year} must be between 1800 and {current_year}") else: if len(str(year)) != len(str(value)): - logger.warning("Collection Warning: {} can be replaced with {}".format(value, year)) + logger.warning(f"Collection Warning: {value} can be replaced with {year}") final_years.append(year) except AttributeError: - logger.error("Collection Error: Skipping {} failed to parse year from {}".format(method, value)) + logger.error(f"Collection Error: Skipping {method} failed to parse year from {value}") return final_years def logger_input(prompt, timeout=60): @@ -543,14 +547,14 @@ def alarm_handler(signum, frame): raise TimeoutExpired def unix_input(prompt, timeout=60): - prompt = "| {}: ".format(prompt) + prompt = f"| {prompt}: " signal.signal(signal.SIGALRM, alarm_handler) signal.alarm(timeout) try: return input(prompt) finally: signal.alarm(0) def old_windows_input(prompt, timeout=60, timer=time.monotonic): - prompt = "| {}: ".format(prompt) + prompt = f"| {prompt}: " sys.stdout.write(prompt) sys.stdout.flush() endtime = timer() + timeout @@ -560,13 +564,13 @@ def old_windows_input(prompt, timeout=60, timer=time.monotonic): result.append(msvcrt.getwche()) if result[-1] == "\n": out = "".join(result[:-1]) - logger.debug("{}{}".format(prompt[2:], out)) + logger.debug(f"{prompt[2:]}{out}") return out time.sleep(0.04) raise TimeoutExpired def windows_input(prompt, timeout=5): - sys.stdout.write("| {}: ".format(prompt)) + sys.stdout.write(f"| {prompt}: ") sys.stdout.flush() result = [] start_time = time.time() @@ -576,7 +580,7 @@ def windows_input(prompt, timeout=5): if ord(char) == 13: # enter_key out = "".join(result) print("") - logger.debug("{}: {}".format(prompt, out)) + logger.debug(f"{prompt}: {out}") return out elif ord(char) >= 32: #space_char result.append(char) @@ -606,17 +610,17 @@ def my_except_hook(exctype, value, tb): def get_id_from_imdb_url(imdb_url): match = re.search("(tt\\d+)", str(imdb_url)) if match: return match.group(1) - else: raise Failed("Regex Error: Failed to parse IMDb ID from IMDb URL: {}".format(imdb_url)) + else: raise Failed(f"Regex Error: Failed to parse IMDb ID from IMDb URL: {imdb_url}") def regex_first_int(data, id_type, default=None): match = re.search("(\\d+)", str(data)) if match: return int(match.group(1)) elif default: - logger.warning("Regex Warning: Failed to parse {} from {} using {} as default".format(id_type, data, default)) + logger.warning(f"Regex Warning: Failed to parse {id_type} from {data} using {default} as default") return int(default) else: - raise Failed("Regex Error: Failed to parse {} from {}".format(id_type, data)) + raise Failed(f"Regex Error: Failed to parse {id_type} from {data}") def remove_not(method): return method[:-4] if method.endswith(".not") else method @@ -629,20 +633,20 @@ def get_centered_text(text): text += " " space -= 1 side = int(space / 2) - return "{}{}{}".format(" " * side, text, " " * side) + return f"{' ' * side}{text}{' ' * side}" 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(separating_character * screen_width)) + logger.handlers[0].setFormatter(logging.Formatter(f"%(message)-{screen_width - 2}s")) + logger.handlers[1].setFormatter(logging.Formatter(f"[%(asctime)s] %(filename)-27s %(levelname)-10s %(message)-{screen_width - 2}s")) + logger.info(f"|{separating_character * screen_width}|") if text: - logger.info("| {} |".format(get_centered_text(text))) - 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))) + logger.info(f"| {get_centered_text(text)} |") + logger.info(f"|{separating_character * screen_width}|") + logger.handlers[0].setFormatter(logging.Formatter(f"| %(message)-{screen_width - 2}s |")) + logger.handlers[1].setFormatter(logging.Formatter(f"[%(asctime)s] %(filename)-27s %(levelname)-10s | %(message)-{screen_width - 2}s |")) def print_return(length, text): - print(adjust_space(length, "| {}".format(text)), end="\r") + print(adjust_space(length, f"| {text}"), end="\r") return len(text) + 2 def print_end(length, text=None): diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 48287bc0..25e453cc 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -15,18 +15,18 @@ parser.add_argument("-w", "--width", dest="width", help="Screen Width (Default: 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)) + raise util.Failed(f"Argument Error: time argument invalid: {args.time} must be in the HH:MM format") util.separating_character = args.divider[0] if 90 <= args.width <= 300: util.screen_width = args.width else: - raise util.Failed("Argument Error: width argument invalid: {} must be an integer between 90 and 300".format(args.width)) + raise util.Failed(f"Argument Error: width argument invalid: {args.width} must be an integer between 90 and 300") default_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config") if args.config and os.path.exists(args.config): default_dir = os.path.join(os.path.dirname(os.path.abspath(args.config))) -elif args.config and not os.path.exists(args.config): raise util.Failed("Config Error: config not found at {}".format(os.path.abspath(args.config))) -elif not os.path.exists(os.path.join(default_dir, "config.yml")): raise util.Failed("Config Error: config not found at {}".format(os.path.abspath(default_dir))) +elif args.config and not os.path.exists(args.config): raise util.Failed(f"Config Error: config not found at {os.path.abspath(args.config)}") +elif not os.path.exists(os.path.join(default_dir, "config.yml")): raise util.Failed(f"Config Error: config not found at {os.path.abspath(default_dir)}") os.makedirs(os.path.join(default_dir, "logs"), exist_ok=True) @@ -34,8 +34,8 @@ logger = logging.getLogger("Plex Meta Manager") logger.setLevel(logging.DEBUG) def fmt_filter(record): - record.levelname = "[{}]".format(record.levelname) - record.filename = "[{}:{}]".format(record.filename, record.lineno) + record.levelname = f"[{record.levelname}]" + record.filename = f"[{record.filename}:{record.lineno}]" return True file_handler = logging.handlers.TimedRotatingFileHandler(os.path.join(default_dir, "logs", "meta.log"), when="midnight", backupCount=10, encoding="utf-8") @@ -71,7 +71,7 @@ def start(config_path, test, daily, collections): elif test: start_type = "Test " elif collections: start_type = "Collections " else: start_type = "" - util.separator("Starting {}Run".format(start_type)) + util.separator(f"Starting {start_type}Run") 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.separator("Finished {}Run".format(start_type)) + util.separator(f"Finished {start_type}Run") try: if args.run or args.test or args.collections: @@ -95,10 +95,10 @@ try: if hours < 0: hours += 24 minutes = int((seconds % 3600) // 60) - time_str = "{} Hour{} and ".format(hours, "s" if hours > 1 else "") if hours > 0 else "" - time_str += "{} Minute{}".format(minutes, "s" if minutes > 1 else "") + time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 else "" + time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}" - length = util.print_return(length, "Current Time: {} | {} until the daily run at {}".format(current, time_str, args.time)) + length = util.print_return(length, f"Current Time: {current} | {time_str} until the daily run at {args.time}") time.sleep(1) except KeyboardInterrupt: util.separator("Exiting Plex Meta Manager")