pull/76/head
meisnate12 4 years ago
parent 1a3b224244
commit e5c43e0fa7

1
.gitignore vendored

@ -7,6 +7,7 @@ __pycache__/
*.so *.so
# Distribution / packaging # Distribution / packaging
.idea
.Python .Python
/modules/test.py /modules/test.py
logs/ logs/

@ -25,7 +25,7 @@ class AniDBAPI:
def convert_anidb(self, input_id, from_id, to_id): 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("//anime[contains(@{}, '{}')]/@{}".format(from_id, input_id, to_id))
if len(ids) > 0: if len(ids) > 0:
if from_id == "tvdbid": return [int(id) for id in ids] if from_id == "tvdbid": return [int(i) for i in ids]
if len(ids[0]) > 0: if len(ids[0]) > 0:
try: return ids[0].split(",") if to_id == "imdbid" else int(ids[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)) except ValueError: raise Failed("AniDB Error: No {} ID found for {} ID: {}".format(util.pretty_ids[to_id], util.pretty_ids[from_id], input_id))
@ -80,7 +80,7 @@ class AniDBAPI:
movie_ids = [] movie_ids = []
for anidb_id in anime_ids: for anidb_id in anime_ids:
try: try:
tmdb_id = self.convert_from_imdb(self.convert_anidb_to_imdb(anidb_id), language) tmdb_id = self.convert_from_imdb(self.convert_anidb_to_imdb(anidb_id))
if tmdb_id: movie_ids.append(tmdb_id) if tmdb_id: movie_ids.append(tmdb_id)
else: raise Failed else: raise Failed
except Failed: except Failed:
@ -92,15 +92,15 @@ class AniDBAPI:
logger.debug("TVDb IDs Found: {}".format(show_ids)) logger.debug("TVDb IDs Found: {}".format(show_ids))
return movie_ids, show_ids return movie_ids, show_ids
def convert_from_imdb(self, imdb_id, language): def convert_from_imdb(self, imdb_id):
output_tmdb_ids = [] output_tmdb_ids = []
if not isinstance(imdb_id, list): if not isinstance(imdb_id, list):
imdb_id = [imdb_id] imdb_id = [imdb_id]
for imdb in imdb_id: for imdb in imdb_id:
expired = False
if self.Cache: if self.Cache:
tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb) tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb)
expired = False
if not tmdb_id: if not tmdb_id:
tmdb_id, expired = self.Cache.get_tmdb_from_imdb(imdb) tmdb_id, expired = self.Cache.get_tmdb_from_imdb(imdb)
if expired: if expired:

@ -61,17 +61,14 @@ class CollectionBuilder:
else: else:
raise Failed("Collection Error: template sub-attribute default is blank") raise Failed("Collection Error: template sub-attribute default is blank")
for m in template: for m in template:
if m not in self.data and m != "default": if m not in self.data and m != "default":
if template[m]: if template[m]:
attr = None
def replace_txt(txt): def replace_txt(txt):
txt = str(txt) txt = str(txt)
for tm in data_template: for template_method in data_template:
if tm != "name" and "<<{}>>".format(tm) in txt: if template_method != "name" and "<<{}>>".format(template_method) in txt:
txt = txt.replace("<<{}>>".format(tm), str(data_template[tm])) txt = txt.replace("<<{}>>".format(template_method), str(data_template[template_method]))
if "<<collection_name>>" in txt: if "<<collection_name>>" in txt:
txt = txt.replace("<<collection_name>>", str(self.name)) txt = txt.replace("<<collection_name>>", str(self.name))
for dm in default: for dm in default:
@ -134,7 +131,7 @@ class CollectionBuilder:
if weekday == current_time.weekday(): if weekday == current_time.weekday():
skip_collection = False skip_collection = False
else: else:
logger.error("Collection Error: weekly schedule attribute {} invalid must be a day of the weeek i.e. weekly(Monday)".format(schedule)) logger.error("Collection Error: weekly schedule attribute {} invalid must be a day of the week i.e. weekly(Monday)".format(schedule))
elif run_time.startswith("month"): elif run_time.startswith("month"):
try: try:
if 1 <= int(param) <= 31: if 1 <= int(param) <= 31:
@ -162,7 +159,7 @@ class CollectionBuilder:
if self.schedule is None: if self.schedule is None:
skip_collection = False skip_collection = False
if skip_collection: if skip_collection:
raise Failed("Skipping Collection {}".format(c)) raise Failed("Skipping Collection {}".format(self.name))
logger.info("Scanning {} Collection".format(self.name)) logger.info("Scanning {} Collection".format(self.name))
@ -293,7 +290,6 @@ class CollectionBuilder:
elif method_name == "imdb_list": elif method_name == "imdb_list":
new_list = [] new_list = []
for imdb_list in util.get_list(data[m], split=False): for imdb_list in util.get_list(data[m], split=False):
new_dictionary = {}
if isinstance(imdb_list, dict): if isinstance(imdb_list, dict):
if "url" in imdb_list and imdb_list["url"]: imdb_url = imdb_list["url"] if "url" in imdb_list and imdb_list["url"]: imdb_url = imdb_list["url"]
else: raise Failed("Collection Error: imdb_list attribute url is required") else: raise Failed("Collection Error: imdb_list attribute url is required")
@ -305,25 +301,25 @@ class CollectionBuilder:
self.methods.append((method_name, new_list)) self.methods.append((method_name, new_list))
elif method_name in util.dictionary_lists: elif method_name in util.dictionary_lists:
if isinstance(data[m], dict): if isinstance(data[m], dict):
def get_int(parent, method, data, default, min=1, max=None): def get_int(parent, method, data_in, default_in, minimum=1, maximum=None):
if method not in data: logger.warning("Collection Warning: {} {} attribute not found using {} as default".format(parent, method, default)) if method not in data_in: logger.warning("Collection Warning: {} {} attribute not found using {} as default".format(parent, method, default))
elif not data[method]: logger.warning("Collection Warning: {} {} attribute is blank using {} as default".format(parent, method, default)) elif not data_in[method]: logger.warning("Collection Warning: {} {} attribute is blank using {} as default".format(parent, method, default))
elif isinstance(data[method], int) and data[method] >= min: elif isinstance(data_in[method], int) and data_in[method] >= minimum:
if max is None or data[method] <= max: return data[method] 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[method], max, default)) 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[method], min, default)) else: logger.warning("Collection Warning: {} {} attribute {} invalid must an integer >= {} using {} as default".format(parent, method, data_in[method], minimum, default))
return default return default_in
if method_name == "filters": if method_name == "filters":
for f in data[m]: for f in data[m]:
if f in util.method_alias or (f.endswith(".not") and f[:-4] in util.method_alias): if f in util.method_alias or (f.endswith(".not") and f[:-4] in util.method_alias):
filter = (util.method_alias[f[:-4]] + f[-4:]) if f.endswith(".not") else util.method_alias[f] 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)) logger.warning("Collection Warning: {} filter will run as {}".format(f, filter_method))
else: else:
filter = f filter_method = f
if filter in util.movie_only_filters and self.library.is_show: raise Failed("Collection Error: {} filter only works for movie libraries".format(filter)) 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)) elif data[m][f] is None: raise Failed("Collection Error: {} filter is blank".format(filter_method))
elif filter in util.all_filters: self.filters.append((filter, data[m][f])) 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)) else: raise Failed("Collection Error: {} filter not supported".format(filter_method))
elif method_name == "plex_collectionless": elif method_name == "plex_collectionless":
new_dictionary = {} new_dictionary = {}
prefix_list = [] prefix_list = []
@ -375,12 +371,12 @@ class CollectionBuilder:
if re.compile("([a-z]{2})-([A-Z]{2})").match(str(attr_data)): if re.compile("([a-z]{2})-([A-Z]{2})").match(str(attr_data)):
new_dictionary[attr] = str(attr_data) new_dictionary[attr] = str(attr_data)
else: else:
raise Failed("Collection Error: {} attribute {}: {} must match pattern ([a-z]{2})-([A-Z]{2}) e.g. en-US".format(m, attr, attr_data)) raise Failed("Collection Error: {} attribute {}: {} must match pattern ([a-z]{{2}})-([A-Z]{{2}}) e.g. en-US".format(m, attr, attr_data))
elif attr == "region": elif attr == "region":
if re.compile("^[A-Z]{2}$").match(str(attr_data)): if re.compile("^[A-Z]{2}$").match(str(attr_data)):
new_dictionary[attr] = str(attr_data) new_dictionary[attr] = str(attr_data)
else: else:
raise Failed("Collection Error: {} attribute {}: {} must match pattern ^[A-Z]{2}$ e.g. US".format(m, attr, attr_data)) raise Failed("Collection Error: {} attribute {}: {} must match pattern ^[A-Z]{{2}}$ e.g. US".format(m, attr, attr_data))
elif attr == "sort_by": 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): 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 new_dictionary[attr] = attr_data
@ -409,7 +405,7 @@ class CollectionBuilder:
else: else:
raise Failed("Collection Error: {} attribute {}: {} must match pattern MM/DD/YYYY e.g. 12/25/2020".format(m, attr, attr_data)) raise Failed("Collection Error: {} attribute {}: {} must match pattern MM/DD/YYYY e.g. 12/25/2020".format(m, attr, attr_data))
elif attr in ["primary_release_year", "year", "first_air_date_year"]: elif attr in ["primary_release_year", "year", "first_air_date_year"]:
if isinstance(attr_data, int) and 1800 < attr_data and attr_data < 2200: if isinstance(attr_data, int) and 1800 < attr_data < 2200:
new_dictionary[attr] = attr_data new_dictionary[attr] = attr_data
else: else:
raise Failed("Collection Error: {} attribute {}: must be a valid year e.g. 1990".format(m, attr)) raise Failed("Collection Error: {} attribute {}: must be a valid year e.g. 1990".format(m, attr))
@ -463,8 +459,8 @@ class CollectionBuilder:
elif data[m]["season"] not in util.pretty_seasons: logger.warning("Collection Warning: mal_season season attribute {} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {} as default".format(data[m]["season"], new_dictionary["season"])) elif data[m]["season"] not in util.pretty_seasons: logger.warning("Collection Warning: mal_season season attribute {} invalid must be either 'winter', 'spring', 'summer' or 'fall' using the current season: {} as default".format(data[m]["season"], new_dictionary["season"]))
else: new_dictionary["season"] = data[m]["season"] else: new_dictionary["season"] = data[m]["season"]
new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, min=1917, max=current_time.year + 1) new_dictionary["year"] = get_int(method_name, "year", data[m], current_time.year, minimum=1917, maximum=current_time.year + 1)
new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, max=500) new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, maximum=500)
self.methods.append((method_name, [new_dictionary])) self.methods.append((method_name, [new_dictionary]))
elif method_name == "mal_userlist": elif method_name == "mal_userlist":
new_dictionary = {"status": "all", "sort_by": "list_score"} new_dictionary = {"status": "all", "sort_by": "list_score"}
@ -482,7 +478,7 @@ class CollectionBuilder:
elif data[m]["sort_by"] not in util.mal_userlist_sort: logger.warning("Collection Warning: mal_season sort_by attribute {} invalid must be either 'score', 'last_updated', 'title' or 'start_date' using score as default".format(data[m]["sort_by"])) elif data[m]["sort_by"] not in util.mal_userlist_sort: logger.warning("Collection Warning: mal_season sort_by attribute {} invalid must be either 'score', 'last_updated', 'title' or 'start_date' using score as default".format(data[m]["sort_by"]))
else: new_dictionary["sort_by"] = util.mal_userlist_sort[data[m]["sort_by"]] else: new_dictionary["sort_by"] = util.mal_userlist_sort[data[m]["sort_by"]]
new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, max=1000) new_dictionary["limit"] = get_int(method_name, "limit", data[m], 100, maximum=1000)
self.methods.append((method_name, [new_dictionary])) self.methods.append((method_name, [new_dictionary]))
else: else:
raise Failed("Collection Error: {} attribute is not a dictionary: {}".format(m, data[m])) raise Failed("Collection Error: {} attribute is not a dictionary: {}".format(m, data[m]))
@ -523,7 +519,7 @@ class CollectionBuilder:
if self.library.Sonarr: if self.library.Sonarr:
self.do_arr = self.details["add_to_arr"] if "add_to_arr" in self.details else self.library.Sonarr.add self.do_arr = self.details["add_to_arr"] if "add_to_arr" in self.details else self.library.Sonarr.add
def run_methods(self, collection_obj, collection_name, map, movie_map, show_map): def run_methods(self, collection_obj, collection_name, rating_key_map, movie_map, show_map):
items_found = 0 items_found = 0
for method, values in self.methods: for method, values in self.methods:
logger.debug("") logger.debug("")
@ -559,7 +555,6 @@ class CollectionBuilder:
items_found += len(items) items_found += len(items)
elif method == "plex_search": elif method == "plex_search":
search_terms = {} search_terms = {}
output = ""
for i, attr_pair in enumerate(value): for i, attr_pair in enumerate(value):
search_list = attr_pair[1] search_list = attr_pair[1]
final_method = attr_pair[0][:-4] + "!" if attr_pair[0][-4:] == ".not" else attr_pair[0] final_method = attr_pair[0][:-4] + "!" if attr_pair[0][-4:] == ".not" else attr_pair[0]
@ -611,7 +606,7 @@ class CollectionBuilder:
elif "trakt" in method: items_found += check_map(self.config.Trakt.get_items(method, value, self.library.is_movie)) 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("Collection Error: {} method not supported".format(method))
if len(items) > 0: map = self.library.add_to_collection(collection_obj if collection_obj else collection_name, items, self.filters, self.details["show_filtered"], map, movie_map, show_map) if len(items) > 0: rating_key_map = self.library.add_to_collection(collection_obj if collection_obj else collection_name, items, self.filters, self.details["show_filtered"], rating_key_map, movie_map, show_map)
else: logger.error("No items found to add to this collection ") else: logger.error("No items found to add to this collection ")
if len(missing_movies) > 0 or len(missing_shows) > 0: if len(missing_movies) > 0 or len(missing_shows) > 0:
@ -662,7 +657,7 @@ class CollectionBuilder:
if self.sync and items_found > 0: if self.sync and items_found > 0:
logger.info("") logger.info("")
count_removed = 0 count_removed = 0
for ratingKey, item in map.items(): for ratingKey, item in rating_key_map.items():
if item is not None: if item is not None:
logger.info("{} Collection | - | {}".format(collection_name, item.title)) logger.info("{} Collection | - | {}".format(collection_name, item.title))
item.removeCollection(collection_name) item.removeCollection(collection_name)
@ -695,10 +690,10 @@ class CollectionBuilder:
item_labels = [label.tag for label in collection.labels] item_labels = [label.tag for label in collection.labels]
labels = util.get_list(self.details["label"]) labels = util.get_list(self.details["label"])
if "label_sync_mode" in self.details and self.details["label_sync_mode"] == "sync": if "label_sync_mode" in self.details and self.details["label_sync_mode"] == "sync":
for label in (l for l in item_labels if l not in labels): for label in (la for la in item_labels if la not in labels):
collection.removeLabel(label) collection.removeLabel(label)
logger.info("Detail: Label {} removed".format(label)) logger.info("Detail: Label {} removed".format(label))
for label in (l for l in labels if l not in item_labels): for label in (la for la in labels if la not in item_labels):
collection.addLabel(label) collection.addLabel(label)
logger.info("Detail: Label {} added".format(label)) logger.info("Detail: Label {} added".format(label))

@ -1,4 +1,4 @@
import glob, logging, os, re, requests import logging, os, re, requests
from modules import util from modules import util
from modules.anidb import AniDBAPI from modules.anidb import AniDBAPI
from modules.builder import CollectionBuilder from modules.builder import CollectionBuilder
@ -11,7 +11,7 @@ from modules.radarr import RadarrAPI
from modules.sonarr import SonarrAPI from modules.sonarr import SonarrAPI
from modules.tautulli import TautulliAPI from modules.tautulli import TautulliAPI
from modules.tmdb import TMDbAPI from modules.tmdb import TMDbAPI
from modules.trakt import TraktAPI from modules.trakttv import TraktAPI
from modules.tvdb import TVDbAPI from modules.tvdb import TVDbAPI
from modules.util import Failed from modules.util import Failed
from plexapi.exceptions import BadRequest from plexapi.exceptions import BadRequest
@ -77,7 +77,6 @@ class Config:
raise Failed("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) raise Failed("YAML Error: {}".format(str(e).replace("\n", "\n|\t ")))
def check_for_attribute(data, attribute, parent=None, test_list=None, options="", default=None, do_print=True, default_is_none=False, req_default=False, var_type="str", throw=False, save=True): def check_for_attribute(data, attribute, parent=None, test_list=None, options="", default=None, do_print=True, default_is_none=False, req_default=False, var_type="str", throw=False, save=True):
message = ""
endline = "" endline = ""
if parent is not None: if parent is not None:
if parent in data: if parent in data:
@ -90,14 +89,13 @@ class Config:
if data is None or attribute not in data: if data is None or attribute not in data:
message = "{} not found".format(text) message = "{} not found".format(text)
if parent and save is True: if parent and save is True:
new_config, ind, bsi = yaml.util.load_yaml_guess_indent(open(self.config_path)) loaded_config, ind_in, bsi_in = yaml.util.load_yaml_guess_indent(open(self.config_path))
endline = "\n{} sub-attribute {} added to config".format(parent, attribute) endline = "\n{} sub-attribute {} added to config".format(parent, attribute)
if parent not in new_config: new_config = {parent: {attribute: default}} if parent not in loaded_config or not loaded_config[parent]: loaded_config[parent] = {attribute: default}
elif not new_config[parent]: new_config[parent] = {attribute: default} elif attribute not in loaded_config[parent]: loaded_config[parent][attribute] = default
elif attribute not in new_config[parent]: new_config[parent][attribute] = default else: endline = ""
else: endLine = "" yaml.round_trip_dump(loaded_config, open(self.config_path, "w"), indent=ind_in, block_seq_indent=bsi_in)
yaml.round_trip_dump(new_config, open(self.config_path, "w"), indent=ind, block_seq_indent=bsi) elif not data[attribute] and data[attribute] is not False:
elif not data[attribute] and data[attribute] != False:
if default_is_none is True: return None if default_is_none is True: return None
else: message = "{} is blank".format(text) else: message = "{} is blank".format(text)
elif var_type == "bool": elif var_type == "bool":
@ -110,11 +108,11 @@ class Config:
if os.path.exists(os.path.abspath(data[attribute])): return data[attribute] 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 = "Path {} does not exist".format(os.path.abspath(data[attribute]))
elif var_type == "list": return util.get_list(data[attribute]) elif var_type == "list": return util.get_list(data[attribute])
elif var_type == "listpath": elif var_type == "list_path":
temp_list = [path for path in util.get_list(data[attribute], split=True) if os.path.exists(os.path.abspath(path))] temp_list = [path for path in util.get_list(data[attribute], split=True) if os.path.exists(os.path.abspath(path))]
if len(temp_list) > 0: return temp_list if len(temp_list) > 0: return temp_list
else: message = "No Paths exist" else: message = "No Paths exist"
elif var_type == "lowerlist": return util.get_list(data[attribute], lower=True) elif var_type == "lower_list": return util.get_list(data[attribute], lower=True)
elif test_list is None or data[attribute] in test_list: return data[attribute] 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 = "{}: {} is an invalid input".format(text, data[attribute])
if var_type == "path" and default and os.path.exists(os.path.abspath(default)): if var_type == "path" and default and os.path.exists(os.path.abspath(default)):
@ -144,18 +142,18 @@ class Config:
self.general["cache"] = check_for_attribute(self.data, "cache", parent="settings", options=" true (Create a cache to store ids)\n false (Do not create a cache to store ids)", var_type="bool", default=True) self.general["cache"] = check_for_attribute(self.data, "cache", parent="settings", options=" true (Create a cache to store ids)\n false (Do not create a cache to store ids)", var_type="bool", default=True)
self.general["cache_expiration"] = check_for_attribute(self.data, "cache_expiration", parent="settings", var_type="int", default=60) self.general["cache_expiration"] = check_for_attribute(self.data, "cache_expiration", parent="settings", var_type="int", default=60)
if self.general["cache"]: if self.general["cache"]:
util.seperator() util.separator()
self.Cache = Cache(self.config_path, self.general["cache_expiration"]) self.Cache = Cache(self.config_path, self.general["cache_expiration"])
else: else:
self.Cache = None self.Cache = None
self.general["asset_directory"] = check_for_attribute(self.data, "asset_directory", parent="settings", var_type="listpath", default=[os.path.join(default_dir, "assets")]) self.general["asset_directory"] = check_for_attribute(self.data, "asset_directory", parent="settings", var_type="list_path", default=[os.path.join(default_dir, "assets")])
self.general["sync_mode"] = check_for_attribute(self.data, "sync_mode", parent="settings", default="append", test_list=["append", "sync"], options=" append (Only Add Items to the Collection)\n sync (Add & Remove Items from the Collection)") self.general["sync_mode"] = check_for_attribute(self.data, "sync_mode", parent="settings", default="append", test_list=["append", "sync"], options=" append (Only Add Items to the Collection)\n sync (Add & Remove Items from the Collection)")
self.general["show_unmanaged"] = check_for_attribute(self.data, "show_unmanaged", parent="settings", var_type="bool", default=True) self.general["show_unmanaged"] = check_for_attribute(self.data, "show_unmanaged", parent="settings", var_type="bool", default=True)
self.general["show_filtered"] = check_for_attribute(self.data, "show_filtered", parent="settings", var_type="bool", default=False) self.general["show_filtered"] = check_for_attribute(self.data, "show_filtered", parent="settings", var_type="bool", default=False)
self.general["show_missing"] = check_for_attribute(self.data, "show_missing", parent="settings", var_type="bool", default=True) self.general["show_missing"] = check_for_attribute(self.data, "show_missing", parent="settings", var_type="bool", default=True)
self.general["save_missing"] = check_for_attribute(self.data, "save_missing", parent="settings", var_type="bool", default=True) self.general["save_missing"] = check_for_attribute(self.data, "save_missing", parent="settings", var_type="bool", default=True)
util.seperator() util.separator()
self.TMDb = None self.TMDb = None
if "tmdb" in self.data: if "tmdb" in self.data:
@ -169,7 +167,7 @@ class Config:
else: else:
raise Failed("Config Error: tmdb attribute not found") raise Failed("Config Error: tmdb attribute not found")
util.seperator() util.separator()
self.Trakt = None self.Trakt = None
if "trakt" in self.data: if "trakt" in self.data:
@ -187,7 +185,7 @@ class Config:
else: else:
logger.warning("trakt attribute not found") logger.warning("trakt attribute not found")
util.seperator() util.separator()
self.MyAnimeList = None self.MyAnimeList = None
self.MyAnimeListIDList = MyAnimeListIDList() self.MyAnimeListIDList = MyAnimeListIDList()
@ -210,7 +208,7 @@ class Config:
self.IMDb = IMDbAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt, TVDb=self.TVDb) if self.TMDb or self.Trakt else None self.IMDb = IMDbAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt, TVDb=self.TVDb) if self.TMDb or self.Trakt else None
self.AniDB = AniDBAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt) self.AniDB = AniDBAPI(Cache=self.Cache, TMDb=self.TMDb, Trakt=self.Trakt)
util.seperator() util.separator()
logger.info("Connecting to Plex Libraries...") logger.info("Connecting to Plex Libraries...")
@ -227,7 +225,7 @@ class Config:
self.general["radarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True) self.general["radarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True)
self.general["radarr"]["add"] = check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False) self.general["radarr"]["add"] = check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False)
self.general["radarr"]["search"] = check_for_attribute(self.data, "search", parent="radarr", var_type="bool", default=False) self.general["radarr"]["search"] = check_for_attribute(self.data, "search", parent="radarr", var_type="bool", default=False)
self.general["radarr"]["tag"] = check_for_attribute(self.data, "tag", parent="radarr", var_type="lowerlist", default_is_none=True) self.general["radarr"]["tag"] = check_for_attribute(self.data, "tag", parent="radarr", var_type="lower_list", default_is_none=True)
self.general["sonarr"] = {} self.general["sonarr"] = {}
self.general["sonarr"]["url"] = check_for_attribute(self.data, "url", parent="sonarr", default_is_none=True) self.general["sonarr"]["url"] = check_for_attribute(self.data, "url", parent="sonarr", default_is_none=True)
@ -237,7 +235,7 @@ class Config:
self.general["sonarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True) self.general["sonarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True)
self.general["sonarr"]["add"] = check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False) self.general["sonarr"]["add"] = check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False)
self.general["sonarr"]["search"] = check_for_attribute(self.data, "search", parent="sonarr", var_type="bool", default=False) self.general["sonarr"]["search"] = check_for_attribute(self.data, "search", parent="sonarr", var_type="bool", default=False)
self.general["sonarr"]["tag"] = check_for_attribute(self.data, "tag", parent="sonarr", var_type="lowerlist", default_is_none=True) self.general["sonarr"]["tag"] = check_for_attribute(self.data, "tag", parent="sonarr", var_type="lower_list", default_is_none=True)
self.general["tautulli"] = {} self.general["tautulli"] = {}
self.general["tautulli"]["url"] = check_for_attribute(self.data, "url", parent="tautulli", default_is_none=True) self.general["tautulli"]["url"] = check_for_attribute(self.data, "url", parent="tautulli", default_is_none=True)
@ -247,7 +245,7 @@ class Config:
try: libs = check_for_attribute(self.data, "libraries", throw=True) try: libs = check_for_attribute(self.data, "libraries", throw=True)
except Failed as e: raise Failed(e) except Failed as e: raise Failed(e)
for lib in libs: for lib in libs:
util.seperator() util.separator()
params = {} params = {}
if "library_name" in libs[lib] and libs[lib]["library_name"]: if "library_name" in libs[lib] and libs[lib]["library_name"]:
params["name"] = str(libs[lib]["library_name"]) params["name"] = str(libs[lib]["library_name"])
@ -255,9 +253,8 @@ class Config:
else: else:
params["name"] = str(lib) params["name"] = str(lib)
logger.info("Connecting to {} Library...".format(params["name"])) logger.info("Connecting to {} Library...".format(params["name"]))
default_lib = os.path.join(default_dir, "{}.yml".format(lib))
params["asset_directory"] = check_for_attribute(libs[lib], "asset_directory", parent="settings", var_type="listpath", default=self.general["asset_directory"], default_is_none=True, save=False) params["asset_directory"] = check_for_attribute(libs[lib], "asset_directory", parent="settings", var_type="list_path", default=self.general["asset_directory"], default_is_none=True, save=False)
if params["asset_directory"] is None: if params["asset_directory"] is None:
logger.warning("Config Warning: Assets will not be used asset_directory attribute must be set under config or under this specific Library") logger.warning("Config Warning: Assets will not be used asset_directory attribute must be set under config or under this specific Library")
@ -292,7 +289,7 @@ class Config:
radarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False) radarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False)
radarr_params["add"] = check_for_attribute(libs[lib], "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False) radarr_params["add"] = check_for_attribute(libs[lib], "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False)
radarr_params["search"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False) radarr_params["search"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False)
radarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="lowerlist", default=self.general["radarr"]["tag"], default_is_none=True, save=False) radarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="radarr", var_type="lower_list", default=self.general["radarr"]["tag"], default_is_none=True, save=False)
library.add_Radarr(RadarrAPI(self.TMDb, radarr_params)) library.add_Radarr(RadarrAPI(self.TMDb, radarr_params))
except Failed as e: except Failed as e:
util.print_multiline(e) util.print_multiline(e)
@ -309,7 +306,7 @@ class Config:
sonarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False) sonarr_params["root_folder_path"] = check_for_attribute(libs[lib], "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False)
sonarr_params["add"] = check_for_attribute(libs[lib], "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False) sonarr_params["add"] = check_for_attribute(libs[lib], "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False)
sonarr_params["search"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False) sonarr_params["search"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False)
sonarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="lowerlist", default=self.general["sonarr"]["tag"], default_is_none=True, save=False) sonarr_params["tag"] = check_for_attribute(libs[lib], "search", parent="sonarr", var_type="lower_list", default=self.general["sonarr"]["tag"], default_is_none=True, save=False)
library.add_Sonarr(SonarrAPI(self.TVDb, sonarr_params, library.Plex.language)) library.add_Sonarr(SonarrAPI(self.TVDb, sonarr_params, library.Plex.language))
except Failed as e: except Failed as e:
util.print_multiline(e) util.print_multiline(e)
@ -328,28 +325,28 @@ class Config:
self.libraries.append(library) self.libraries.append(library)
util.seperator() util.separator()
if len(self.libraries) > 0: if len(self.libraries) > 0:
logger.info("{} Plex Library Connection{} Successful".format(len(self.libraries), "s" if len(self.libraries) > 1 else "")) logger.info("{} Plex Library Connection{} Successful".format(len(self.libraries), "s" if len(self.libraries) > 1 else ""))
else: else:
raise Failed("Plex Error: No Plex libraries were found") raise Failed("Plex Error: No Plex libraries were found")
util.seperator() util.separator()
def update_libraries(self, test, requested_collections): def update_libraries(self, test, requested_collections):
for library in self.libraries: for library in self.libraries:
os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout) os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout)
logger.info("") logger.info("")
util.seperator("{} Library".format(library.name)) util.separator("{} Library".format(library.name))
try: library.update_metadata(self.TMDb, test) try: library.update_metadata(self.TMDb, test)
except Failed as e: logger.error(e) except Failed as e: logger.error(e)
logger.info("") logger.info("")
util.seperator("{} Library {}Collections".format(library.name, "Test " if test else "")) util.separator("{} Library {}Collections".format(library.name, "Test " if test else ""))
collections = {c: library.collections[c] for c in util.get_list(requested_collections) if c in library.collections} if requested_collections else library.collections 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: if collections:
logger.info("") logger.info("")
util.seperator("Mapping {} Library".format(library.name)) util.separator("Mapping {} Library".format(library.name))
logger.info("") logger.info("")
movie_map, show_map = self.map_guids(library) movie_map, show_map = self.map_guids(library)
for c in collections: for c in collections:
@ -358,21 +355,21 @@ class Config:
if "template" in collections[c] and collections[c]["template"]: if "template" in collections[c] and collections[c]["template"]:
for data_template in util.get_list(collections[c]["template"], split=False): for data_template in util.get_list(collections[c]["template"], split=False):
if "name" in data_template \ if "name" in data_template \
and data_template["name"] \ and data_template["name"] \
and library.templates \ and library.templates \
and data_template["name"] in self.library.templates \ and data_template["name"] in library.templates \
and self.library.templates[data_template["name"]] \ and library.templates[data_template["name"]] \
and "test" in self.library.templates[data_template["name"]] \ and "test" in library.templates[data_template["name"]] \
and self.library.templates[data_template["name"]]["test"] == True: and library.templates[data_template["name"]]["test"] is True:
no_template_test = False no_template_test = False
if no_template_test: if no_template_test:
continue continue
try: try:
logger.info("") logger.info("")
util.seperator("{} Collection".format(c)) util.separator("{} Collection".format(c))
logger.info("") logger.info("")
map = {} rating_key_map = {}
try: try:
builder = CollectionBuilder(self, library, c, collections[c]) builder = CollectionBuilder(self, library, c, collections[c])
except Exception as e: except Exception as e:
@ -383,19 +380,19 @@ class Config:
try: try:
collection_obj = library.get_collection(c) collection_obj = library.get_collection(c)
collection_name = collection_obj.title collection_name = collection_obj.title
except Failed as e: except Failed:
collection_obj = None collection_obj = None
collection_name = c collection_name = c
if builder.schedule is not None: if builder.schedule is not None:
print_multiline(builder.schedule, info=True) util.print_multiline(builder.schedule, info=True)
logger.info("") logger.info("")
if builder.sync: if builder.sync:
logger.info("Sync Mode: sync") logger.info("Sync Mode: sync")
if collection_obj: if collection_obj:
for item in collection_obj.items(): for item in collection_obj.items():
map[item.ratingKey] = item rating_key_map[item.ratingKey] = item
else: else:
logger.info("Sync Mode: append") logger.info("Sync Mode: append")
@ -404,7 +401,7 @@ class Config:
logger.info("") logger.info("")
logger.info("Collection Filter {}: {}".format(f[0], f[1])) logger.info("Collection Filter {}: {}".format(f[0], f[1]))
builder.run_methods(collection_obj, collection_name, map, movie_map, show_map) builder.run_methods(collection_obj, collection_name, rating_key_map, movie_map, show_map)
try: try:
plex_collection = library.get_collection(collection_name) plex_collection = library.get_collection(collection_name)
@ -419,14 +416,14 @@ class Config:
logger.error("Unknown Error: {}".format(e)) logger.error("Unknown Error: {}".format(e))
if library.show_unmanaged is True and not test and not requested_collections: if library.show_unmanaged is True and not test and not requested_collections:
logger.info("") logger.info("")
util.seperator("Unmanaged Collections in {} Library".format(library.name)) util.separator("Unmanaged Collections in {} Library".format(library.name))
logger.info("") logger.info("")
unmanaged_count = 0 unmanaged_count = 0
collections_in_plex = [str(pcol) for pcol in collections] collections_in_plex = [str(plex_col) for plex_col in collections]
for col in library.get_all_collections(): for col in library.get_all_collections():
if col.title not in collections_in_plex: if col.title not in collections_in_plex:
logger.info(col.title) logger.info(col.title)
unmanaged_count += 1 unmanaged_count += 1
logger.info("{} Unmanaged Collections".format(unmanaged_count)) logger.info("{} Unmanaged Collections".format(unmanaged_count))
else: else:
logger.info("") logger.info("")
@ -436,7 +433,6 @@ class Config:
movie_map = {} movie_map = {}
show_map = {} show_map = {}
length = 0 length = 0
count = 0
logger.info("Mapping {} Library: {}".format("Movie" if library.is_movie else "Show", library.name)) logger.info("Mapping {} Library: {}".format("Movie" if library.is_movie else "Show", library.name))
items = library.Plex.all() items = library.Plex.all()
for i, item in enumerate(items, 1): for i, item in enumerate(items, 1):
@ -445,7 +441,7 @@ class Config:
id_type, main_id = self.get_id(item, library, length) id_type, main_id = self.get_id(item, library, length)
except BadRequest: except BadRequest:
util.print_stacktrace() util.print_stacktrace()
util.print_end(length, "{} {:<46} | {} for {}".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, error_message, item.title)) util.print_end(length, "{} | {} for {} not found".format("Cache | ! |" if self.Cache else "Mapping Error:", item.guid, item.title))
continue continue
if isinstance(main_id, list): if isinstance(main_id, list):
if id_type == "movie": if id_type == "movie":

@ -1,4 +1,4 @@
import logging, math, re, requests, time import logging, math, re, requests
from lxml import html from lxml import html
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
@ -33,8 +33,8 @@ class IMDbAPI:
except IndexError: raise Failed("IMDb Error: Failed to parse URL: {}".format(imdb_url)) except IndexError: raise Failed("IMDb Error: Failed to parse URL: {}".format(imdb_url))
try: total = int(re.findall("(\\d+) title", results)[0]) 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("IMDb Error: No Results at URL: {}".format(imdb_url))
if "&start=" in current_url: current_url = re.sub("&start=\d+", "", current_url) if "&start=" in current_url: current_url = re.sub("&start=\\d+", "", current_url)
if "&count=" in current_url: current_url = re.sub("&count=\d+", "", current_url) if "&count=" in current_url: current_url = re.sub("&count=\\d+", "", current_url)
if limit < 1 or total < limit: limit = total if limit < 1 or total < limit: limit = total
remainder = limit % 250 remainder = limit % 250
if remainder == 0: remainder = 250 if remainder == 0: remainder = 250
@ -66,7 +66,7 @@ class IMDbAPI:
if tvdb_id: show_ids.append(tvdb_id) if tvdb_id: show_ids.append(tvdb_id)
elif method == "imdb_list": elif method == "imdb_list":
if status_message: if status_message:
logger.info("Processing {}: {}".format(pretty,"{} Items at {}".format(data["limit"], data["url"]) if data["limit"] > 0 else data["url"])) logger.info("Processing {}: {}".format(pretty, "{} Items at {}".format(data["limit"], data["url"]) if data["limit"] > 0 else data["url"]))
imdb_ids = self.get_imdb_ids_from_url(data["url"], language, data["limit"]) imdb_ids = self.get_imdb_ids_from_url(data["url"], language, data["limit"])
total_ids = len(imdb_ids) total_ids = len(imdb_ids)
length = 0 length = 0
@ -86,6 +86,8 @@ class IMDbAPI:
return movie_ids, show_ids return movie_ids, show_ids
def convert_from_imdb(self, imdb_id, language): def convert_from_imdb(self, imdb_id, language):
update_tmdb = False
update_tvdb = False
if self.Cache: if self.Cache:
tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id) tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id)
update_tmdb = False update_tmdb = False
@ -121,7 +123,7 @@ class IMDbAPI:
try: try:
if tvdb_id and not from_cache: self.TVDb.get_series(language, tvdb_id=tvdb_id) if tvdb_id and not from_cache: self.TVDb.get_series(language, tvdb_id=tvdb_id)
except Failed: tvdb_id = None except Failed: tvdb_id = None
if not tmdb_id and not tvdb_id : raise Failed("IMDb Error: No TMDb ID or TVDb ID found for IMDb: {}".format(imdb_id)) if not tmdb_id and not tvdb_id: raise Failed("IMDb Error: No TMDb ID or TVDb ID found for IMDb: {}".format(imdb_id))
if self.Cache: if self.Cache:
if tmdb_id and update_tmdb is not False: if tmdb_id and update_tmdb is not False:
self.Cache.update_imdb("movie", update_tmdb, imdb_id, tmdb_id) self.Cache.update_imdb("movie", update_tmdb, imdb_id, tmdb_id)

@ -27,7 +27,7 @@ class MyAnimeListIDList:
raise Failed("MyAnimeList Error: MyAnimeList ID: {} not found".format(mal_id)) raise Failed("MyAnimeList Error: MyAnimeList ID: {} not found".format(mal_id))
class MyAnimeListAPI: class MyAnimeListAPI:
def __init__(self, params, MyAnimeListIDList, authorization=None): def __init__(self, params, MyAnimeListIDList_in, authorization=None):
self.urls = { self.urls = {
"oauth_token": "https://myanimelist.net/v1/oauth2/token", "oauth_token": "https://myanimelist.net/v1/oauth2/token",
"oauth_authorize": "https://myanimelist.net/v1/oauth2/authorize", "oauth_authorize": "https://myanimelist.net/v1/oauth2/authorize",
@ -40,7 +40,7 @@ class MyAnimeListAPI:
self.client_secret = params["client_secret"] self.client_secret = params["client_secret"]
self.config_path = params["config_path"] self.config_path = params["config_path"]
self.authorization = authorization self.authorization = authorization
self.MyAnimeListIDList = MyAnimeListIDList self.MyAnimeListIDList = MyAnimeListIDList_in
if not self.save_authorization(self.authorization): if not self.save_authorization(self.authorization):
if not self.refresh_authorization(): if not self.refresh_authorization():
self.get_authorization() self.get_authorization()

@ -1,5 +1,5 @@
import datetime, logging, os, requests import logging, os, re, requests
from lxml import html from datetime import datetime, timedelta
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
from plexapi.exceptions import BadRequest, NotFound, Unauthorized from plexapi.exceptions import BadRequest, NotFound, Unauthorized
@ -16,7 +16,7 @@ class PlexAPI:
try: self.PlexServer = PlexServer(params["plex"]["url"], params["plex"]["token"], timeout=params["plex"]["timeout"]) 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 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("Plex Error: {}".format(e))
except requests.exceptions.ConnectionError as e: except requests.exceptions.ConnectionError:
util.print_stacktrace() util.print_stacktrace()
raise Failed("Plex Error: Plex url is invalid") raise Failed("Plex Error: Plex url is invalid")
self.is_movie = params["library_type"] == "movie" self.is_movie = params["library_type"] == "movie"
@ -30,7 +30,7 @@ class PlexAPI:
if attribute in self.data: if attribute in self.data:
if self.data[attribute]: if self.data[attribute]:
if isinstance(self.data[attribute], dict): return self.data[attribute] if isinstance(self.data[attribute], dict): return self.data[attribute]
else: logger.waring("Config Warning: {} must be a dictionary".format(attribute)) else: logger.warning("Config Warning: {} must be a dictionary".format(attribute))
else: logger.warning("Config Warning: {} attribute is blank".format(attribute)) else: logger.warning("Config Warning: {} attribute is blank".format(attribute))
return None return None
@ -101,7 +101,7 @@ class PlexAPI:
try: valid_collections.append(self.get_collection(collection)) try: valid_collections.append(self.get_collection(collection))
except Failed as e: logger.error(e) except Failed as e: logger.error(e)
if len(valid_collections) == 0: if len(valid_collections) == 0:
raise Failed("Collection Error: No valid Plex Collections in {}".format(collections[c][m])) raise Failed("Collection Error: No valid Plex Collections in {}".format(collections))
return valid_collections return valid_collections
def add_missing(self, collection, items, is_movie): def add_missing(self, collection, items, is_movie):
@ -119,7 +119,7 @@ class PlexAPI:
except yaml.scanner.ScannerError as e: except yaml.scanner.ScannerError as e:
logger.error("YAML Error: {}".format(str(e).replace("\n", "\n|\t "))) logger.error("YAML Error: {}".format(str(e).replace("\n", "\n|\t ")))
def add_to_collection(self, collection, items, filters, show_filtered, map, movie_map, show_map): def add_to_collection(self, collection, items, filters, show_filtered, rating_key_map, movie_map, show_map):
name = collection.title if isinstance(collection, Collections) else collection name = collection.title if isinstance(collection, Collections) else collection
collection_items = collection.items() if isinstance(collection, Collections) else [] collection_items = collection.items() if isinstance(collection, Collections) else []
total = len(items) total = len(items)
@ -145,7 +145,6 @@ class PlexAPI:
break break
elif method == "original_language": elif method == "original_language":
terms = util.get_list(f[1], lower=True) terms = util.get_list(f[1], lower=True)
tmdb_id = None
movie = None movie = None
for key, value in movie_map.items(): for key, value in movie_map.items():
if current.ratingKey == value: if current.ratingKey == value:
@ -174,6 +173,7 @@ class PlexAPI:
break break
else: else:
terms = util.get_list(f[1]) terms = util.get_list(f[1])
attrs = []
if method in ["video_resolution", "audio_language", "subtitle_language"]: if method in ["video_resolution", "audio_language", "subtitle_language"]:
for media in current.media: for media in current.media:
if method == "video_resolution": attrs = [media.videoResolution] if method == "video_resolution": attrs = [media.videoResolution]
@ -189,20 +189,20 @@ class PlexAPI:
length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title)) length = util.print_return(length, "Filtering {}/{} {}".format((" " * (max_length - len(str(i)))) + str(i), total, current.title))
if match: if match:
util.print_end(length, "{} Collection | {} | {}".format(name, "=" if current in collection_items else "+", current.title)) util.print_end(length, "{} Collection | {} | {}".format(name, "=" if current in collection_items else "+", current.title))
if current in collection_items: map[current.ratingKey] = None if current in collection_items: rating_key_map[current.ratingKey] = None
else: current.addCollection(name) else: current.addCollection(name)
elif show_filtered is True: elif show_filtered is True:
logger.info("{} Collection | X | {}".format(name, current.title)) logger.info("{} Collection | X | {}".format(name, current.title))
media_type = "{}{}".format("Movie" if self.is_movie else "Show", "s" if total > 1 else "") media_type = "{}{}".format("Movie" if self.is_movie else "Show", "s" if total > 1 else "")
util.print_end(length, "{} {} Processed".format(total, media_type)) util.print_end(length, "{} {} Processed".format(total, media_type))
return map return rating_key_map
def search_item(self, data, year=None): def search_item(self, data, year=None):
return util.choose_from_list(self.search(data, year=year), "movie" if self.is_movie else "show", str(data), exact=True) return util.choose_from_list(self.search(data, year=year), "movie" if self.is_movie else "show", str(data), exact=True)
def update_metadata(self, TMDb, test): def update_metadata(self, TMDb, test):
logger.info("") logger.info("")
util.seperator("{} Library Metadata".format(self.name)) util.separator("{} Library Metadata".format(self.name))
logger.info("") logger.info("")
if not self.metadata: if not self.metadata:
raise Failed("No metadata to edit") raise Failed("No metadata to edit")
@ -210,11 +210,11 @@ class PlexAPI:
if test and ("test" not in self.metadata[m] or self.metadata[m]["test"] is not True): if test and ("test" not in self.metadata[m] or self.metadata[m]["test"] is not True):
continue continue
logger.info("") logger.info("")
util.seperator() util.separator()
logger.info("") logger.info("")
year = None year = None
if "year" in self.metadata[m]: if "year" in self.metadata[m]:
now = datetime.datetime.now() now = datetime.now()
if self.metadata[m]["year"] is None: logger.error("Metadata Error: year attribute is blank") 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 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("Metadata Error: year attribute must be between 1800-{}".format(now.year + 1))
@ -253,7 +253,6 @@ class PlexAPI:
except Failed as e: except Failed as e:
logger.error(e) logger.error(e)
originally_available = tmdb_item.first_air_date if tmdb_item else None originally_available = tmdb_item.first_air_date if tmdb_item else None
rating = tmdb_item.vote_average if tmdb_item else None rating = tmdb_item.vote_average if tmdb_item else None
original_title = tmdb_item.original_name if tmdb_item and tmdb_item.original_name != tmdb_item.name else None original_title = tmdb_item.original_name if tmdb_item and tmdb_item.original_name != tmdb_item.name else None
@ -325,10 +324,10 @@ class PlexAPI:
if self.metadata[m]["label_sync_mode"] is None: logger.error("Metadata Error: label_sync_mode attribute is blank defaulting to append") if self.metadata[m]["label_sync_mode"] is None: logger.error("Metadata Error: label_sync_mode attribute is blank defaulting to append")
elif self.metadata[m]["label_sync_mode"] not in ["append", "sync"]: logger.error("Metadata Error: label_sync_mode attribute must be either 'append' or 'sync' defaulting to append") elif self.metadata[m]["label_sync_mode"] not in ["append", "sync"]: logger.error("Metadata Error: label_sync_mode attribute must be either 'append' or 'sync' defaulting to append")
elif self.metadata[m]["label_sync_mode"] == "sync": elif self.metadata[m]["label_sync_mode"] == "sync":
for label in (l for l in item_labels if l not in labels): for label in (la for la in item_labels if la not in labels):
item.removeLabel(label) item.removeLabel(label)
logger.info("Detail: Label {} removed".format(label)) logger.info("Detail: Label {} removed".format(label))
for label in (l for l in labels if l not in item_labels): for label in (la for la in labels if la not in item_labels):
item.addLabel(label) item.addLabel(label)
logger.info("Detail: Label {} added".format(label)) logger.info("Detail: Label {} added".format(label))
else: else:
@ -381,7 +380,7 @@ class PlexAPI:
if self.metadata[m]["episodes"]: if self.metadata[m]["episodes"]:
for episode_str in self.metadata[m]["episodes"]: for episode_str in self.metadata[m]["episodes"]:
logger.info("") logger.info("")
match = re.search("[Ss]{1}\d+[Ee]{1}\d+", episode_str) match = re.search("[Ss]\\d+[Ee]\\d+", episode_str)
if match: if match:
output = match.group(0)[1:].split("E" if "E" in m.group(0) else "e") output = match.group(0)[1:].split("E" if "E" in m.group(0) else "e")
episode_id = int(output[0]) episode_id = int(output[0])
@ -421,6 +420,6 @@ class PlexAPI:
else: else:
logger.info("Season: {} Episode: {} Details Update Not Needed".format(season_id, episode_id)) logger.info("Season: {} Episode: {} Details Update Not Needed".format(season_id, episode_id))
else: else:
logger.error("Metadata Error: episode {} invlaid must have S##E## format".format(episode_str)) logger.error("Metadata Error: episode {} invalid must have S##E## format".format(episode_str))
else: else:
logger.error("Metadata Error: episodes attribute is blank") logger.error("Metadata Error: episodes attribute is blank")

@ -11,7 +11,7 @@ class RadarrAPI:
self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/")
try: try:
result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json()
except Exception as e: except Exception:
util.print_stacktrace() util.print_stacktrace()
raise Failed("Radarr Error: Could not connect to Radarr at {}".format(params["url"])) raise Failed("Radarr Error: Could not connect to Radarr at {}".format(params["url"]))
if "error" in result and result["error"] == "Unauthorized": if "error" in result and result["error"] == "Unauthorized":
@ -94,7 +94,7 @@ class RadarrAPI:
else: else:
try: try:
logger.error("Radarr Error: ({}) {}: ({}) {}".format(tmdb_id, movie.title, response.status_code, response.json()[0]["errorMessage"])) logger.error("Radarr Error: ({}) {}: ({}) {}".format(tmdb_id, movie.title, response.status_code, response.json()[0]["errorMessage"]))
except KeyError as e: except KeyError:
logger.debug(url_json) logger.debug(url_json)
logger.error("Radarr Error: {}".format(response.json())) logger.error("Radarr Error: {}".format(response.json()))
logger.info("{} Movie{} added to Radarr".format(add_count, "s" if add_count > 1 else "")) logger.info("{} Movie{} added to Radarr".format(add_count, "s" if add_count > 1 else ""))

@ -11,7 +11,7 @@ class SonarrAPI:
self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/") self.base_url = "{}/api{}".format(params["url"], "/v3/" if params["version"] == "v3" else "/")
try: try:
result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json() result = requests.get("{}system/status".format(self.base_url), params=self.url_params).json()
except Exception as e: except Exception:
util.print_stacktrace() util.print_stacktrace()
raise Failed("Sonarr Error: Could not connect to Sonarr at {}".format(params["url"])) raise Failed("Sonarr Error: Could not connect to Sonarr at {}".format(params["url"]))
if "error" in result and result["error"] == "Unauthorized": if "error" in result and result["error"] == "Unauthorized":
@ -72,7 +72,7 @@ class SonarrAPI:
"language": self.language, "language": self.language,
"monitored": True, "monitored": True,
"rootFolderPath": self.root_folder_path, "rootFolderPath": self.root_folder_path,
"seasons" : [], "seasons": [],
"images": [{"covertype": "poster", "url": show.poster_path}], "images": [{"covertype": "poster", "url": show.poster_path}],
"addOptions": {"searchForMissingEpisodes": self.search} "addOptions": {"searchForMissingEpisodes": self.search}
} }
@ -85,7 +85,7 @@ class SonarrAPI:
else: else:
try: try:
logger.error("Sonarr Error: ({}) {}: ({}) {}".format(tvdb_id, show.title, response.status_code, response.json()[0]["errorMessage"])) logger.error("Sonarr Error: ({}) {}: ({}) {}".format(tvdb_id, show.title, response.status_code, response.json()[0]["errorMessage"]))
except KeyError as e: except KeyError:
logger.debug(url_json) logger.debug(url_json)
logger.error("Sonarr Error: {}".format(response.json())) logger.error("Sonarr Error: {}".format(response.json()))
logger.info("{} Show{} added to Sonarr".format(add_count, "s" if add_count > 1 else "")) logger.info("{} Show{} added to Sonarr".format(add_count, "s" if add_count > 1 else ""))

@ -9,7 +9,7 @@ class TautulliAPI:
def __init__(self, params): def __init__(self, params):
try: try:
response = requests.get("{}/api/v2?apikey={}&cmd=get_library_names".format(params["url"], params["apikey"])).json() response = requests.get("{}/api/v2?apikey={}&cmd=get_library_names".format(params["url"], params["apikey"])).json()
except Exception as e: except Exception:
util.print_stacktrace() util.print_stacktrace()
raise Failed("Tautulli Error: Invalid url") raise Failed("Tautulli Error: Invalid url")
if response["response"]["result"] != "success": if response["response"]["result"] != "success":

@ -9,7 +9,7 @@ def run_tests(default_dir):
try: try:
config = Config(default_dir) config = Config(default_dir)
logger.info("") logger.info("")
util.seperator("Mapping Tests") util.separator("Mapping Tests")
for library in config.libraries: for library in config.libraries:
config.map_guids(library) config.map_guids(library)
anidb_tests(config) anidb_tests(config)
@ -19,13 +19,13 @@ def run_tests(default_dir):
tmdb_tests(config) tmdb_tests(config)
trakt_tests(config) trakt_tests(config)
tvdb_tests(config) tvdb_tests(config)
util.seperator("Finished All Plex Meta Manager Tests") util.separator("Finished All Plex Meta Manager Tests")
except KeyboardInterrupt: except KeyboardInterrupt:
util.seperator("Canceled Plex Meta Manager Tests") util.separator("Canceled Plex Meta Manager Tests")
def anidb_tests(config): def anidb_tests(config):
if config.AniDB: if config.AniDB:
util.seperator("AniDB Tests") util.separator("AniDB Tests")
try: try:
config.AniDB.convert_anidb_to_tvdb(69) config.AniDB.convert_anidb_to_tvdb(69)
@ -84,11 +84,11 @@ def anidb_tests(config):
logger.error("Failure | Validate AniDB List: {}".format(e)) logger.error("Failure | Validate AniDB List: {}".format(e))
else: else:
util.seperator("AniDB Not Configured") util.separator("AniDB Not Configured")
def imdb_tests(config): def imdb_tests(config):
if config.IMDb: if config.IMDb:
util.seperator("IMDb Tests") util.separator("IMDb Tests")
tmdb_ids, tvdb_ids = config.IMDb.get_items("imdb_list", {"url": "https://www.imdb.com/search/title/?groups=top_1000", "limit": 0}, "en", status_message=False) 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") if len(tmdb_ids) == 1000: logger.info("Success | IMDb URL get TMDb IDs")
@ -103,11 +103,11 @@ def imdb_tests(config):
else: logger.error("Failure | IMDb ID get TMDb IDs: {} Should be 1".format(len(tmdb_ids))) else: logger.error("Failure | IMDb ID get TMDb IDs: {} Should be 1".format(len(tmdb_ids)))
else: else:
util.seperator("IMDb Not Configured") util.separator("IMDb Not Configured")
def mal_tests(config): def mal_tests(config):
if config.MyAnimeListIDList: if config.MyAnimeListIDList:
util.seperator("MyAnimeListXML Tests") util.separator("MyAnimeListXML Tests")
try: try:
config.MyAnimeListIDList.convert_mal_to_tvdb(21) config.MyAnimeListIDList.convert_mal_to_tvdb(21)
@ -145,10 +145,10 @@ def mal_tests(config):
logger.error("Failure | Find MyAnimeList ID: {}".format(e)) logger.error("Failure | Find MyAnimeList ID: {}".format(e))
else: else:
util.seperator("MyAnimeListXML Not Configured") util.separator("MyAnimeListXML Not Configured")
if config.MyAnimeList: if config.MyAnimeList:
util.seperator("MyAnimeList Tests") util.separator("MyAnimeList Tests")
mal_list_tests = [ mal_list_tests = [
("mal_all", 10), ("mal_all", 10),
@ -173,11 +173,11 @@ def mal_tests(config):
util.print_stacktrace() util.print_stacktrace()
logger.error("Failure | Get Anime using {}: {}".format(util.pretty_names[mal_list_test[0]], e)) logger.error("Failure | Get Anime using {}: {}".format(util.pretty_names[mal_list_test[0]], e))
else: else:
util.seperator("MyAnimeList Not Configured") util.separator("MyAnimeList Not Configured")
def tautulli_tests(config): def tautulli_tests(config):
if config.libraries[0].Tautulli: if config.libraries[0].Tautulli:
util.seperator("Tautulli Tests") util.separator("Tautulli Tests")
try: try:
config.libraries[0].Tautulli.get_section_id(config.libraries[0].name) config.libraries[0].Tautulli.get_section_id(config.libraries[0].name)
@ -200,11 +200,11 @@ def tautulli_tests(config):
util.print_stacktrace() util.print_stacktrace()
logger.error("Failure | Get Top: {}".format(e)) logger.error("Failure | Get Top: {}".format(e))
else: else:
util.seperator("Tautulli Not Configured") util.separator("Tautulli Not Configured")
def tmdb_tests(config): def tmdb_tests(config):
if config.TMDb: if config.TMDb:
util.seperator("TMDb Tests") util.separator("TMDb Tests")
try: try:
config.TMDb.convert_imdb_to_tmdb("tt0076759") config.TMDb.convert_imdb_to_tmdb("tt0076759")
@ -284,11 +284,11 @@ def tmdb_tests(config):
util.print_stacktrace() 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("Failure | Get {} using {}: {}".format("Movies" if tmdb_list_test[2] else "Shows", util.pretty_names[tmdb_list_test[0]], e))
else: else:
util.seperator("TMDb Not Configured") util.separator("TMDb Not Configured")
def trakt_tests(config): def trakt_tests(config):
if config.Trakt: if config.Trakt:
util.seperator("Trakt Tests") util.separator("Trakt Tests")
try: try:
config.Trakt.convert_imdb_to_tmdb("tt0076759") config.Trakt.convert_imdb_to_tmdb("tt0076759")
@ -369,11 +369,11 @@ def trakt_tests(config):
util.print_stacktrace() 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("Failure | Get {} using {}: {}".format("Movies" if trakt_list_test[2] else "Shows", util.pretty_names[trakt_list_test[0]], e))
else: else:
util.seperator("Trakt Not Configured") util.separator("Trakt Not Configured")
def tvdb_tests(config): def tvdb_tests(config):
if config.TVDb: if config.TVDb:
util.seperator("TVDb Tests") util.separator("TVDb Tests")
tmdb_ids, tvdb_ids = config.TVDb.get_items("tvdb_list", "https://www.thetvdb.com/lists/arrowverse", "en", status_message=False) 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") if len(tvdb_ids) == 10 and len(tmdb_ids) == 0: logger.info("Success | TVDb URL get TVDb IDs and TMDb IDs")
@ -412,4 +412,4 @@ def tvdb_tests(config):
logger.error("Failure | TVDb ID get TVDb Movie ID: {}".format(e)) logger.error("Failure | TVDb ID get TVDb Movie ID: {}".format(e))
else: else:
util.seperator("TVDb Not Configured") util.separator("TVDb Not Configured")

@ -1,4 +1,4 @@
import logging, os, tmdbv3api import logging, tmdbv3api
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
from retrying import retry from retrying import retry
@ -30,9 +30,13 @@ class TMDbAPI:
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
def convert_from_tmdb(self, tmdb_id, convert_to, is_movie): def convert_from_tmdb(self, tmdb_id, convert_to, is_movie):
try: return self.Movie.external_ids(tmdb_id)[convert_to] if is_movie else self.TV.external_ids(tmdb_id)[convert_to] try:
except TMDbException: raise Failed("TMDb Error: No {} found for TMDb ID {}".format(convert_to.upper().replace("B_", "b "), tmdb_id)) id_to_return = self.Movie.external_ids(tmdb_id)[convert_to] if is_movie else self.TV.external_ids(tmdb_id)[convert_to]
if not id_to_return or (convert_to == "tvdb_id" and id_to_return == 0):
raise Failed("TMDb Error: No {} found for TMDb ID {}".format(convert_to.upper().replace("B_", "b "), tmdb_id))
return id_to_return
except TMDbException:
raise Failed("TMDb Error: {} TMDb ID: {} not found".format("Movie" if is_movie else "Show", tmdb_id))
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) @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): def convert_to_tmdb(self, external_id, external_source, is_movie):
@ -105,6 +109,7 @@ class TMDbAPI:
elif method == "tmdb_now_playing" and is_movie: tmdb_items = self.Movie.now_playing(x + 1) elif method == "tmdb_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_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) elif method == "tmdb_trending_weekly": tmdb_items = self.Trending.movie_week(x + 1) if is_movie else self.Trending.tv_week(x + 1)
else: raise Failed("TMDb Error: {} method not supported".format(method))
for tmdb_item in tmdb_items: for tmdb_item in tmdb_items:
try: try:
ids.append(tmdb_item.id if is_movie else self.convert_tmdb_to_tvdb(tmdb_item.id)) ids.append(tmdb_item.id if is_movie else self.convert_tmdb_to_tvdb(tmdb_item.id))
@ -143,6 +148,9 @@ class TMDbAPI:
movie_ids = [] movie_ids = []
show_ids = [] show_ids = []
if method in ["tmdb_discover", "tmdb_company", "tmdb_keyword"] or (method == "tmdb_network" and not is_movie): if method in ["tmdb_discover", "tmdb_company", "tmdb_keyword"] or (method == "tmdb_network" and not is_movie):
attrs = None
tmdb_id = ""
tmdb_name = ""
if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]: if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]:
tmdb_id = int(data) tmdb_id = int(data)
if method == "tmdb_company": if method == "tmdb_company":
@ -163,7 +171,7 @@ class TMDbAPI:
if status_message: if status_message:
if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]: 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("Processing {}: ({}) {} ({} {}{})".format(pretty, tmdb_id, tmdb_name, amount, media_type, "" if amount == 1 else "s"))
else: elif method == "tmdb_discover":
logger.info("Processing {}: {} {}{}".format(pretty, amount, media_type, "" if amount == 1 else "s")) logger.info("Processing {}: {} {}{}".format(pretty, amount, media_type, "" if amount == 1 else "s"))
for attr, value in attrs.items(): for attr, value in attrs.items():
logger.info(" {}: {}".format(attr, value)) logger.info(" {}: {}".format(attr, value))

@ -119,7 +119,7 @@ class TraktAPI:
except Failed as e: except Failed as e:
logger.error(e) logger.error(e)
if len(trakt_values) == 0: if len(trakt_values) == 0:
raise Failed("Trakt Error: No valid Trakt Lists in {}".format(value)) raise Failed("Trakt Error: No valid Trakt Lists in {}".format(values))
return trakt_values return trakt_values
def validate_trakt_watchlist(self, values, is_movie): def validate_trakt_watchlist(self, values, is_movie):
@ -131,7 +131,7 @@ class TraktAPI:
except Failed as e: except Failed as e:
logger.error(e) logger.error(e)
if len(trakt_values) == 0: if len(trakt_values) == 0:
raise Failed("Trakt Error: No valid Trakt Watchlists in {}".format(value)) raise Failed("Trakt Error: No valid Trakt Watchlists in {}".format(values))
return trakt_values return trakt_values
def get_items(self, method, data, is_movie, status_message=True): def get_items(self, method, data, is_movie, status_message=True):

@ -1,4 +1,4 @@
import logging, math, re, requests, time import logging, requests
from lxml import html from lxml import html
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
@ -20,6 +20,10 @@ class TVDbObj:
results = response.xpath("//*[text()='TheTVDB.com {} ID']/parent::node()/span/text()".format(self.media_type)) results = response.xpath("//*[text()='TheTVDB.com {} ID']/parent::node()/span/text()".format(self.media_type))
if len(results) > 0: if len(results) > 0:
self.id = int(results[0]) self.id = int(results[0])
elif tvdb_url.startswith(TVDb.movie_id_url):
raise Failed("TVDb Error: Could not find a TVDb Movie using TVDb Movie ID: {}".format(tvdb_url[len(TVDb.series_id_url):]))
elif tvdb_url.startswith(TVDb.series_id_url):
raise Failed("TVDb Error: Could not find a TVDb Series using TVDb Series ID: {}".format(tvdb_url[len(TVDb.series_id_url):]))
else: else:
raise Failed("TVDb Error: Could not find a TVDb {} ID at the URL {}".format(self.media_type, tvdb_url)) raise Failed("TVDb Error: Could not find a TVDb {} ID at the URL {}".format(self.media_type, tvdb_url))
@ -41,7 +45,7 @@ class TVDbObj:
if not tmdb_id: if not tmdb_id:
results = response.xpath("//*[text()='IMDB']/@href") results = response.xpath("//*[text()='IMDB']/@href")
if len(results) > 0: if len(results) > 0:
try: tmdb_id = TVDb.convert_from_imdb(util.get_id_from_imdb_url(results[0]), language) try: tmdb_id = TVDb.convert_from_imdb(util.get_id_from_imdb_url(results[0]))
except Failed as e: logger.error(e) except Failed as e: logger.error(e)
self.tmdb_id = tmdb_id self.tmdb_id = tmdb_id
self.tvdb_url = tvdb_url self.tvdb_url = tvdb_url
@ -67,7 +71,7 @@ class TVDbAPI:
def get_series(self, language, tvdb_url=None, tvdb_id=None): def get_series(self, language, tvdb_url=None, tvdb_id=None):
if not tvdb_url and not tvdb_id: if not tvdb_url and not tvdb_id:
raise Failed("TVDB Error: getget_seriesmove requires either tvdb_url or tvdb_id") raise Failed("TVDB Error: get_series requires either tvdb_url or tvdb_id")
elif not tvdb_url and tvdb_id: elif not tvdb_url and tvdb_id:
tvdb_url = "{}{}".format(self.series_id_url, tvdb_id) tvdb_url = "{}{}".format(self.series_id_url, tvdb_id)
return TVDbObj(tvdb_url, language, False, self) return TVDbObj(tvdb_url, language, False, self)
@ -104,7 +108,7 @@ class TVDbAPI:
if len(show_ids) > 0 or len(movie_ids) > 0: if len(show_ids) > 0 or len(movie_ids) > 0:
return movie_ids, show_ids return movie_ids, show_ids
raise Failed("TVDb Error: No TVDb IDs found at {}".format(tvdb_url)) raise Failed("TVDb Error: No TVDb IDs found at {}".format(tvdb_url))
except requests.exceptions.MissingSchema as e: except requests.exceptions.MissingSchema:
util.print_stacktrace() util.print_stacktrace()
raise Failed("TVDb Error: URL Lookup Failed for {}".format(tvdb_url)) raise Failed("TVDb Error: URL Lookup Failed for {}".format(tvdb_url))
else: else:
@ -137,10 +141,10 @@ class TVDbAPI:
logger.debug("TVDb IDs Found: {}".format(show_ids)) logger.debug("TVDb IDs Found: {}".format(show_ids))
return movie_ids, show_ids return movie_ids, show_ids
def convert_from_imdb(self, imdb_id, language): def convert_from_imdb(self, imdb_id):
update = False
if self.Cache: if self.Cache:
tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id) tmdb_id, tvdb_id = self.Cache.get_ids_from_imdb(imdb_id)
update = False
if not tmdb_id: if not tmdb_id:
tmdb_id, update = self.Cache.get_tmdb_from_imdb(imdb_id) tmdb_id, update = self.Cache.get_tmdb_from_imdb(imdb_id)
if update: if update:

@ -18,7 +18,7 @@ class Failed(Exception):
def retry_if_not_failed(exception): def retry_if_not_failed(exception):
return not isinstance(exception, Failed) return not isinstance(exception, Failed)
seperating_character = "=" separating_character = "="
screen_width = 100 screen_width = 100
method_alias = { method_alias = {
@ -524,7 +524,7 @@ def get_year_list(data, method):
final_years.append(i) final_years.append(i)
else: else:
year = re.search("(\\d+)", str(value)).group(1) year = re.search("(\\d+)", str(value)).group(1)
if int(start) < 1800 or int(start) > current_year: if int(year) < 1800 or int(year) > current_year:
logger.error("Collection Error: Skipping {} year {} must be between 1800 and {}".format(method, year, current_year)) logger.error("Collection Error: Skipping {} year {} must be between 1800 and {}".format(method, year, current_year))
else: else:
if len(str(year)) != len(str(value)): if len(str(year)) != len(str(value)):
@ -572,14 +572,14 @@ def windows_input(prompt, timeout=5):
start_time = time.time() start_time = time.time()
while True: while True:
if msvcrt.kbhit(): if msvcrt.kbhit():
chr = msvcrt.getwche() char = msvcrt.getwche()
if ord(chr) == 13: # enter_key if ord(char) == 13: # enter_key
out = "".join(result) out = "".join(result)
print("") print("")
logger.debug("{}: {}".format(prompt, out)) logger.debug("{}: {}".format(prompt, out))
return out return out
elif ord(chr) >= 32: #space_char elif ord(char) >= 32: #space_char
result.append(chr) result.append(char)
if (time.time() - start_time) > timeout: if (time.time() - start_time) > timeout:
print("") print("")
raise TimeoutExpired raise TimeoutExpired
@ -631,13 +631,13 @@ def get_centered_text(text):
side = int(space / 2) side = int(space / 2)
return "{}{}{}".format(" " * side, text, " " * side) return "{}{}{}".format(" " * side, text, " " * side)
def seperator(text=None): def separator(text=None):
logger.handlers[0].setFormatter(logging.Formatter("%(message)-{}s".format(screen_width - 2))) logger.handlers[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.handlers[1].setFormatter(logging.Formatter("[%(asctime)s] %(filename)-27s %(levelname)-10s %(message)-{}s".format(screen_width - 2)))
logger.info("|{}|".format(seperating_character * screen_width)) logger.info("|{}|".format(separating_character * screen_width))
if text: if text:
logger.info("| {} |".format(get_centered_text(text))) logger.info("| {} |".format(get_centered_text(text)))
logger.info("|{}|".format(seperating_character * screen_width)) logger.info("|{}|".format(separating_character * screen_width))
logger.handlers[0].setFormatter(logging.Formatter("| %(message)-{}s |".format(screen_width - 2))) logger.handlers[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.handlers[1].setFormatter(logging.Formatter("[%(asctime)s] %(filename)-27s %(levelname)-10s | %(message)-{}s |".format(screen_width - 2)))

@ -1,9 +1,9 @@
import argparse, logging, os, re, schedule, sys, time, traceback, datetime import argparse, logging, os, re, schedule, sys, time, datetime
from modules import tests, util from modules import tests, util
from modules.config import Config from modules.config import Config
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--mytests", dest="tests", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("--my-tests", dest="tests", help=argparse.SUPPRESS, action="store_true", default=False)
parser.add_argument("--debug", dest="debug", help=argparse.SUPPRESS, action="store_true", default=False) parser.add_argument("--debug", dest="debug", help=argparse.SUPPRESS, action="store_true", default=False)
parser.add_argument("-c", "--config", dest="config", help="Run with desired *.yml file", type=str) parser.add_argument("-c", "--config", dest="config", help="Run with desired *.yml file", type=str)
parser.add_argument("-t", "--time", dest="time", help="Time to update each day use format HH:MM (Default: 03:00)", default="03:00", type=str) parser.add_argument("-t", "--time", dest="time", help="Time to update each day use format HH:MM (Default: 03:00)", default="03:00", type=str)
@ -17,7 +17,7 @@ args = parser.parse_args()
if not re.match("^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$", args.time): 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("Argument Error: time argument invalid: {} must be in the HH:MM format".format(args.time))
util.seperating_character = args.divider[0] util.separating_character = args.divider[0]
if 90 <= args.width <= 300: if 90 <= args.width <= 300:
util.screen_width = args.width util.screen_width = args.width
else: else:
@ -51,27 +51,27 @@ logger.addHandler(file_handler)
sys.excepthook = util.my_except_hook sys.excepthook = util.my_except_hook
util.seperator() util.separator()
logger.info(util.get_centered_text(" ")) logger.info(util.get_centered_text(" "))
logger.info(util.get_centered_text(" ____ _ __ __ _ __ __ ")) logger.info(util.get_centered_text(" ____ _ __ __ _ __ __ "))
logger.info(util.get_centered_text("| _ \| | _____ __ | \/ | ___| |_ __ _ | \/ | __ _ _ __ __ _ __ _ ___ _ __ ")) logger.info(util.get_centered_text("| _ \\| | _____ __ | \\/ | ___| |_ __ _ | \\/ | __ _ _ __ __ _ __ _ ___ _ __ "))
logger.info(util.get_centered_text("| |_) | |/ _ \ \/ / | |\/| |/ _ \ __/ _` | | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|")) logger.info(util.get_centered_text("| |_) | |/ _ \\ \\/ / | |\\/| |/ _ \\ __/ _` | | |\\/| |/ _` | '_ \\ / _` |/ _` |/ _ \\ '__|"))
logger.info(util.get_centered_text("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | ")) logger.info(util.get_centered_text("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | "))
logger.info(util.get_centered_text("|_| |_|\___/_/\_\ |_| |_|\___|\__\__,_| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| ")) logger.info(util.get_centered_text("|_| |_|\\___/_/\\_\\ |_| |_|\\___|\\__\\__,_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| "))
logger.info(util.get_centered_text(" |___/ ")) logger.info(util.get_centered_text(" |___/ "))
logger.info(util.get_centered_text(" Version: 1.2.2 ")) logger.info(util.get_centered_text(" Version: 1.2.2 "))
util.seperator() util.separator()
if args.tests: if args.tests:
tests.run_tests(default_dir) tests.run_tests(default_dir)
sys.exit(0) sys.exit(0)
def start(config_path, test, daily, collections): def start(config_path, test, daily, collections):
if daily: type = "Daily " if daily: start_type = "Daily "
elif test: type = "Test " elif test: start_type = "Test "
elif collections: type = "Collections " elif collections: start_type = "Collections "
else: type = "" else: start_type = ""
util.seperator("Starting {}Run".format(type)) util.separator("Starting {}Run".format(start_type))
try: try:
config = Config(default_dir, config_path) config = Config(default_dir, config_path)
config.update_libraries(test, collections) config.update_libraries(test, collections)
@ -79,7 +79,7 @@ def start(config_path, test, daily, collections):
util.print_stacktrace() util.print_stacktrace()
logger.critical(e) logger.critical(e)
logger.info("") logger.info("")
util.seperator("Finished {}Run".format(type)) util.separator("Finished {}Run".format(start_type))
try: try:
if args.run or args.test or args.collections: if args.run or args.test or args.collections:
@ -101,4 +101,4 @@ try:
length = util.print_return(length, "Current Time: {} | {} until the daily run at {}".format(current, time_str, args.time)) length = util.print_return(length, "Current Time: {} | {} until the daily run at {}".format(current, time_str, args.time))
time.sleep(1) time.sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
util.seperator("Exiting Plex Meta Manager") util.separator("Exiting Plex Meta Manager")

Loading…
Cancel
Save