fix imdb to prep for custom sort

pull/351/head
meisnate12 3 years ago
parent 4b3056f22d
commit ba977e6a37

@ -73,7 +73,7 @@ poster_details = ["url_poster", "tmdb_poster", "tmdb_profile", "tvdb_poster", "f
background_details = ["url_background", "tmdb_background", "tvdb_background", "file_background"] background_details = ["url_background", "tmdb_background", "tvdb_background", "file_background"]
boolean_details = ["visible_library", "visible_home", "visible_shared", "show_filtered", "show_missing", "save_missing", "item_assets"] boolean_details = ["visible_library", "visible_home", "visible_shared", "show_filtered", "show_missing", "save_missing", "item_assets"]
string_details = ["sort_title", "content_rating", "name_mapping"] string_details = ["sort_title", "content_rating", "name_mapping"]
ignored_details = ["smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "tmdb_person", "build_collection"] ignored_details = ["smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "tmdb_person", "build_collection", "collection_order"]
details = ["collection_mode", "collection_order", "label"] + boolean_details + string_details details = ["collection_mode", "collection_order", "label"] + boolean_details + string_details
collectionless_details = ["collection_order", "plex_collectionless", "label", "label_sync_mode", "test"] + \ collectionless_details = ["collection_order", "plex_collectionless", "label", "label_sync_mode", "test"] + \
poster_details + background_details + summary_details + string_details poster_details + background_details + summary_details + string_details
@ -123,6 +123,17 @@ movie_only_filters = [
show_only_filters = ["network"] show_only_filters = ["network"]
smart_invalid = ["collection_order"] smart_invalid = ["collection_order"]
smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_missing", "smart_label"] + radarr_details + sonarr_details smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_missing", "smart_label"] + radarr_details + sonarr_details
custom_sort_builders = [
"tmdb_collection", "tmdb_list", "tmdb_popular", "tmdb_now_playing", "tmdb_top_rated",
"tmdb_trending_daily", "tmdb_trending_weekly", "tmdb_discover",
"tvdb_list", "imdb_list",
"trakt_list", "trakt_trending", "trakt_popular", "trakt_recommended", "trakt_watched", "trakt_collected",
"tautulli_popular", "tautulli_watched", "letterboxd_list", "icheckmovies_list",
"anidb_popular",
"anilist_top_rated", "anilist_popular", "anilist_season", "anilist_studio", "anilist_genre", "anilist_tag",
"mal_all", "mal_airing", "mal_upcoming", "mal_tv", "mal_movie", "mal_ova", "mal_special",
"mal_popular", "mal_favorite", "mal_suggested", "mal_userlist", "mal_season"
]
class CollectionBuilder: class CollectionBuilder:
def __init__(self, config, library, metadata, name, data): def __init__(self, config, library, metadata, name, data):
@ -143,7 +154,7 @@ class CollectionBuilder:
self.sonarr_options = {} self.sonarr_options = {}
self.missing_movies = [] self.missing_movies = []
self.missing_shows = [] self.missing_shows = []
self.methods = [] self.builders = []
self.filters = [] self.filters = []
self.rating_keys = [] self.rating_keys = []
self.run_again_movies = [] self.run_again_movies = []
@ -374,6 +385,19 @@ class CollectionBuilder:
logger.debug(f"Value: {self.data[methods['build_collection']]}") logger.debug(f"Value: {self.data[methods['build_collection']]}")
self.build_collection = util.parse_bool("build_collection", self.data[methods["build_collection"]]) self.build_collection = util.parse_bool("build_collection", self.data[methods["build_collection"]])
self.sort_collection = False
if "collection_order" in methods:
logger.info("")
logger.info("Validating Method: collection_order")
if self.data[methods["collection_order"]] is None:
raise Failed(f"Collection Warning: collection_order attribute is blank")
elif self.data[methods["collection_order"]].lower() in plex.collection_order_options:
self.details["collection_order"] = self.data[methods["collection_order"]].lower()
if self.data[methods["collection_order"]].lower() == "custom":
self.sort_collection = True
else:
raise Failed(f"Collection Error: {self.data[methods['collection_order']]} collection_order invalid\n\trelease (Order Collection by release dates)\n\talpha (Order Collection Alphabetically)\n\tcustom (Custom Order Collection)")
if "tmdb_person" in methods: if "tmdb_person" in methods:
logger.info("") logger.info("")
logger.info("Validating Method: tmdb_person") logger.info("Validating Method: tmdb_person")
@ -487,6 +511,12 @@ class CollectionBuilder:
elif method_name == "filters": self._filters(method_name, method_data) elif method_name == "filters": self._filters(method_name, method_data)
else: raise Failed(f"Collection Error: {method_final} attribute not supported") else: raise Failed(f"Collection Error: {method_final} attribute not supported")
if self.sort_collection and len(self.builders) > 1:
raise Failed("Collection Error: collection_order: custom can only be used with a single builder per collection")
if self.sort_collection and self.builders[0][0] not in custom_sort_builders:
raise Failed(f"Collection Error: collection_order: custom cannot be used with {self.builders[0][0]}")
if self.add_to_radarr is None: if self.add_to_radarr is None:
self.add_to_radarr = self.library.Radarr.add if self.library.Radarr else False self.add_to_radarr = self.library.Radarr.add if self.library.Radarr else False
if self.add_to_sonarr is None: if self.add_to_sonarr is None:
@ -581,11 +611,6 @@ class CollectionBuilder:
self.details[method_name] = plex.collection_mode_options[str(method_data).lower()] self.details[method_name] = plex.collection_mode_options[str(method_data).lower()]
else: else:
raise Failed(f"Collection Error: {method_data} 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)") raise Failed(f"Collection Error: {method_data} 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 str(method_data).lower() in plex.collection_order_options:
self.details[method_name] = plex.collection_order_options[str(method_data).lower()]
else:
raise Failed(f"Collection Error: {method_data} collection_order invalid\n\trelease (Order Collection by release dates)\n\talpha (Order Collection Alphabetically)")
elif method_name == "label": elif method_name == "label":
if "label" in methods and "label.sync" in methods: if "label" in methods and "label.sync" in methods:
raise Failed("Collection Error: Cannot use label and label.sync together") raise Failed("Collection Error: Cannot use label and label.sync together")
@ -682,10 +707,10 @@ class CollectionBuilder:
def _anidb(self, method_name, method_data): def _anidb(self, method_name, method_data):
if method_name == "anidb_popular": if method_name == "anidb_popular":
self.methods.append((method_name, util.parse_int(method_name, method_data, default=30, maximum=30))) self.builders.append((method_name, util.parse_int(method_name, method_data, default=30, maximum=30)))
elif method_name in ["anidb_id", "anidb_relation"]: elif method_name in ["anidb_id", "anidb_relation"]:
for anidb_id in self.config.AniDB.validate_anidb_ids(method_data, self.language): for anidb_id in self.config.AniDB.validate_anidb_ids(method_data, self.language):
self.methods.append((method_name, anidb_id)) self.builders.append((method_name, anidb_id))
elif method_name == "anidb_tag": elif method_name == "anidb_tag":
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
new_dictionary = {} new_dictionary = {}
@ -696,14 +721,14 @@ class CollectionBuilder:
else: else:
new_dictionary["tag"] = util.regex_first_int(dict_data[dict_methods["username"]], "AniDB Tag ID") new_dictionary["tag"] = util.regex_first_int(dict_data[dict_methods["username"]], "AniDB Tag ID")
new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 0, minimum=0) new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 0, minimum=0)
self.methods.append((method_name, new_dictionary)) self.builders.append((method_name, new_dictionary))
def _anilist(self, method_name, method_data): def _anilist(self, method_name, method_data):
if method_name in ["anilist_id", "anilist_relations", "anilist_studio"]: if method_name in ["anilist_id", "anilist_relations", "anilist_studio"]:
for anilist_id in self.config.AniList.validate_anilist_ids(method_data, studio=method_name == "anilist_studio"): for anilist_id in self.config.AniList.validate_anilist_ids(method_data, studio=method_name == "anilist_studio"):
self.methods.append((method_name, anilist_id)) self.builders.append((method_name, anilist_id))
elif method_name in ["anilist_popular", "anilist_top_rated"]: elif method_name in ["anilist_popular", "anilist_top_rated"]:
self.methods.append((method_name, util.parse_int(method_name, method_data))) self.builders.append((method_name, util.parse_int(method_name, method_data)))
elif method_name in ["anilist_season", "anilist_genre", "anilist_tag"]: elif method_name in ["anilist_season", "anilist_genre", "anilist_tag"]:
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
new_dictionary = {} new_dictionary = {}
@ -720,13 +745,13 @@ class CollectionBuilder:
new_dictionary["tag"] = self.config.AniList.validate_tag(util.parse_from_dict(method_name, "tag", dict_data, dict_methods)) new_dictionary["tag"] = self.config.AniList.validate_tag(util.parse_from_dict(method_name, "tag", dict_data, dict_methods))
new_dictionary["sort_by"] = util.parse_from_dict(method_name, "sort_by", dict_data, dict_methods, default="score", options=["score", "popular"]) new_dictionary["sort_by"] = util.parse_from_dict(method_name, "sort_by", dict_data, dict_methods, default="score", options=["score", "popular"])
new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 0, maximum=500) new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 0, maximum=500)
self.methods.append((method_name, new_dictionary)) self.builders.append((method_name, new_dictionary))
def _icheckmovies(self, method_name, method_data): def _icheckmovies(self, method_name, method_data):
if method_name.startswith("icheckmovies_list"): if method_name.startswith("icheckmovies_list"):
icheckmovies_lists = self.config.ICheckMovies.validate_icheckmovies_lists(method_data, self.language) icheckmovies_lists = self.config.ICheckMovies.validate_icheckmovies_lists(method_data, self.language)
for icheckmovies_list in icheckmovies_lists: for icheckmovies_list in icheckmovies_lists:
self.methods.append(("icheckmovies_list", icheckmovies_list)) self.builders.append(("icheckmovies_list", icheckmovies_list))
if method_name.endswith("_details"): if method_name.endswith("_details"):
self.summaries[method_name] = self.config.ICheckMovies.get_list_description(icheckmovies_lists[0], self.language) self.summaries[method_name] = self.config.ICheckMovies.get_list_description(icheckmovies_lists[0], self.language)
@ -734,27 +759,27 @@ class CollectionBuilder:
if method_name == "imdb_id": if method_name == "imdb_id":
for value in util.get_list(method_data): for value in util.get_list(method_data):
if str(value).startswith("tt"): if str(value).startswith("tt"):
self.methods.append((method_name, value)) self.builders.append((method_name, value))
else: else:
raise Failed(f"Collection Error: imdb_id {value} must begin with tt") raise Failed(f"Collection Error: imdb_id {value} must begin with tt")
elif method_name == "imdb_list": elif method_name == "imdb_list":
for imdb_dict in self.config.IMDb.validate_imdb_lists(method_data, self.language): for imdb_dict in self.config.IMDb.validate_imdb_lists(method_data, self.language):
self.methods.append((method_name, imdb_dict)) self.builders.append((method_name, imdb_dict))
def _letterboxd(self, method_name, method_data): def _letterboxd(self, method_name, method_data):
if method_name.startswith("letterboxd_list"): if method_name.startswith("letterboxd_list"):
letterboxd_lists = self.config.Letterboxd.validate_letterboxd_lists(method_data, self.language) letterboxd_lists = self.config.Letterboxd.validate_letterboxd_lists(method_data, self.language)
for letterboxd_list in letterboxd_lists: for letterboxd_list in letterboxd_lists:
self.methods.append(("letterboxd_list", letterboxd_list)) self.builders.append(("letterboxd_list", letterboxd_list))
if method_name.endswith("_details"): if method_name.endswith("_details"):
self.summaries[method_name] = self.config.Letterboxd.get_list_description(letterboxd_lists[0], self.language) self.summaries[method_name] = self.config.Letterboxd.get_list_description(letterboxd_lists[0], self.language)
def _mal(self, method_name, method_data): def _mal(self, method_name, method_data):
if method_name == "mal_id": if method_name == "mal_id":
for mal_id in util.get_int_list(method_data, "MyAnimeList ID"): for mal_id in util.get_int_list(method_data, "MyAnimeList ID"):
self.methods.append((method_name, mal_id)) self.builders.append((method_name, mal_id))
elif method_name in ["mal_all", "mal_airing", "mal_upcoming", "mal_tv", "mal_ova", "mal_movie", "mal_special", "mal_popular", "mal_favorite", "mal_suggested"]: elif method_name in ["mal_all", "mal_airing", "mal_upcoming", "mal_tv", "mal_ova", "mal_movie", "mal_special", "mal_popular", "mal_favorite", "mal_suggested"]:
self.methods.append((method_name, util.parse_int(method_name, method_data))) self.builders.append((method_name, util.parse_int(method_name, method_data)))
elif method_name in ["mal_season", "mal_userlist"]: elif method_name in ["mal_season", "mal_userlist"]:
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
new_dictionary = {} new_dictionary = {}
@ -772,11 +797,11 @@ class CollectionBuilder:
new_dictionary["status"] = util.parse_from_dict(method_name, "status", dict_data, dict_methods, default="all", options=mal.userlist_status) new_dictionary["status"] = util.parse_from_dict(method_name, "status", dict_data, dict_methods, default="all", options=mal.userlist_status)
new_dictionary["sort_by"] = util.parse_from_dict(method_name, "sort_by", dict_data, dict_methods, default="score", options=mal.userlist_sort_options, translation=mal.userlist_sort_translation) new_dictionary["sort_by"] = util.parse_from_dict(method_name, "sort_by", dict_data, dict_methods, default="score", options=mal.userlist_sort_options, translation=mal.userlist_sort_translation)
new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 100, maximum=1000) new_dictionary["limit"] = util.parse_int_from_dict(method_name, "limit", dict_data, dict_methods, 100, maximum=1000)
self.methods.append((method_name, new_dictionary)) self.builders.append((method_name, new_dictionary))
def _plex(self, method_name, method_data): def _plex(self, method_name, method_data):
if method_name == "plex_all": if method_name == "plex_all":
self.methods.append((method_name, True)) self.builders.append((method_name, True))
elif method_name in ["plex_search", "plex_collectionless"]: elif method_name in ["plex_search", "plex_collectionless"]:
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
new_dictionary = {} new_dictionary = {}
@ -790,13 +815,13 @@ class CollectionBuilder:
exact_list.append(self.name) exact_list.append(self.name)
new_dictionary["exclude_prefix"] = prefix_list new_dictionary["exclude_prefix"] = prefix_list
new_dictionary["exclude"] = exact_list new_dictionary["exclude"] = exact_list
self.methods.append((method_name, new_dictionary)) self.builders.append((method_name, new_dictionary))
else: else:
self.methods.append(("plex_search", self.build_filter("plex_search", {"any": {method_name: method_data}}))) self.builders.append(("plex_search", self.build_filter("plex_search", {"any": {method_name: method_data}})))
def _tautulli(self, method_name, method_data): def _tautulli(self, method_name, method_data):
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
self.methods.append((method_name, { self.builders.append((method_name, {
"list_type": "popular" if method_name == "tautulli_popular" else "watched", "list_type": "popular" if method_name == "tautulli_popular" else "watched",
"list_days": util.parse_int_from_dict(method_name, "list_days", dict_data, dict_methods, 30), "list_days": util.parse_int_from_dict(method_name, "list_days", dict_data, dict_methods, 30),
"list_size": util.parse_int_from_dict(method_name, "list_size", dict_data, dict_methods, 10), "list_size": util.parse_int_from_dict(method_name, "list_size", dict_data, dict_methods, 10),
@ -859,11 +884,11 @@ class CollectionBuilder:
else: else:
raise Failed(f"Collection Error: {method_name} parameter {discover_final} is blank") raise Failed(f"Collection Error: {method_name} parameter {discover_final} is blank")
if len(new_dictionary) > 1: if len(new_dictionary) > 1:
self.methods.append((method_name, new_dictionary)) self.builders.append((method_name, new_dictionary))
else: else:
raise Failed(f"Collection Error: {method_name} had no valid fields") raise Failed(f"Collection Error: {method_name} had no valid fields")
elif method_name in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]: elif method_name in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]:
self.methods.append((method_name, util.parse_int(method_name, method_data))) self.builders.append((method_name, util.parse_int(method_name, method_data)))
else: else:
values = self.config.TMDb.validate_tmdb_ids(method_data, method_name) values = self.config.TMDb.validate_tmdb_ids(method_data, method_name)
if method_name.endswith("_details"): if method_name.endswith("_details"):
@ -886,20 +911,20 @@ class CollectionBuilder:
if hasattr(item, "description") and item.description: if hasattr(item, "description") and item.description:
self.summaries[method_name] = item.description self.summaries[method_name] = item.description
for value in values: for value in values:
self.methods.append((method_name[:-8] if method_name.endswith("_details") else method_name, value)) self.builders.append((method_name[:-8] if method_name.endswith("_details") else method_name, value))
def _trakt(self, method_name, method_data): def _trakt(self, method_name, method_data):
if method_name.startswith("trakt_list"): if method_name.startswith("trakt_list"):
trakt_lists = self.config.Trakt.validate_trakt(method_data, self.library.is_movie) trakt_lists = self.config.Trakt.validate_trakt(method_data, self.library.is_movie)
for trakt_list in trakt_lists: for trakt_list in trakt_lists:
self.methods.append(("trakt_list", trakt_list)) self.builders.append(("trakt_list", trakt_list))
if method_name.endswith("_details"): if method_name.endswith("_details"):
self.summaries[method_name] = self.config.Trakt.list_description(trakt_lists[0]) self.summaries[method_name] = self.config.Trakt.list_description(trakt_lists[0])
elif method_name in ["trakt_trending", "trakt_popular", "trakt_recommended", "trakt_watched", "trakt_collected"]: elif method_name in ["trakt_trending", "trakt_popular", "trakt_recommended", "trakt_watched", "trakt_collected"]:
self.methods.append((method_name, util.parse_int(method_name, method_data))) self.builders.append((method_name, util.parse_int(method_name, method_data)))
elif method_name in ["trakt_watchlist", "trakt_collection"]: elif method_name in ["trakt_watchlist", "trakt_collection"]:
for trakt_list in self.config.Trakt.validate_trakt(method_data, self.library.is_movie, trakt_type=method_name[6:]): for trakt_list in self.config.Trakt.validate_trakt(method_data, self.library.is_movie, trakt_type=method_name[6:]):
self.methods.append((method_name, trakt_list)) self.builders.append((method_name, trakt_list))
def _tvdb(self, method_name, method_data): def _tvdb(self, method_name, method_data):
values = util.get_list(method_data) values = util.get_list(method_data)
@ -915,7 +940,7 @@ class CollectionBuilder:
elif method_name.startswith("tvdb_list"): elif method_name.startswith("tvdb_list"):
self.summaries[method_name] = self.config.TVDb.get_list_description(values[0], self.language) self.summaries[method_name] = self.config.TVDb.get_list_description(values[0], self.language)
for value in values: for value in values:
self.methods.append((method_name[:-8] if method_name.endswith("_details") else method_name, value)) self.builders.append((method_name[:-8] if method_name.endswith("_details") else method_name, value))
def _filters(self, method_name, method_data): def _filters(self, method_name, method_data):
for dict_data, dict_methods in util.validate_dict_list(method_name, method_data): for dict_data, dict_methods in util.validate_dict_list(method_name, method_data):
@ -987,7 +1012,7 @@ class CollectionBuilder:
elif show_id not in self.missing_shows: elif show_id not in self.missing_shows:
self.missing_shows.append(show_id) self.missing_shows.append(show_id)
return items_found_inside return items_found_inside
for method, value in self.methods: for method, value in self.builders:
logger.debug("") logger.debug("")
logger.debug(f"Builder: {method}: {value}") logger.debug(f"Builder: {method}: {value}")
logger.info("") logger.info("")

@ -540,8 +540,8 @@ class Config:
util.separator() util.separator()
def get_html(self, url, headers=None): def get_html(self, url, headers=None, params=None):
return html.fromstring(self.get(url, headers=headers).content) return html.fromstring(self.get(url, headers=headers, params=params).content)
def get_json(self, url, headers=None): def get_json(self, url, headers=None):
return self.get(url, headers=headers).json() return self.get(url, headers=headers).json()

@ -1,6 +1,7 @@
import logging, math, re, time import logging, math, re, time
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
from urllib.parse import urlparse, parse_qs
logger = logging.getLogger("Plex Meta Manager") logger = logging.getLogger("Plex Meta Manager")
@ -8,102 +9,90 @@ builders = ["imdb_list", "imdb_id"]
base_url = "https://www.imdb.com" base_url = "https://www.imdb.com"
urls = { urls = {
"list": f"{base_url}/list/ls", "list": f"{base_url}/list/ls",
"search": f"{base_url}/search/title/?", "search": f"{base_url}/search/title/",
"keyword": f"{base_url}/search/keyword/?" "keyword": f"{base_url}/search/keyword/"
} }
xpath = {
"imdb_id": "//div[contains(@class, 'lister-item-image')]//@data-tconst",
"list": "//div[@class='desc lister-total-num-results']/text()",
"search": "//div[@class='desc']/span/text()",
"keyword": "//div[@class='desc']/text()"
}
item_counts = {"list": 100, "search": 250, "keyword": 50}
class IMDb: class IMDb:
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
def _validate_url(self, imdb_url, language):
imdb_url = imdb_url.strip()
if not imdb_url.startswith(urls["list"]) and not imdb_url.startswith(urls["search"]) and not imdb_url.startswith(urls["keyword"]):
raise Failed(f"IMDb Error: {imdb_url} must begin with either:\n{urls['list']} (For Lists)\n{urls['search']} (For Searches)\n{urls['keyword']} (For Keyword Searches)")
total, _ = self._total(self._fix_url(imdb_url), language)
if total > 0:
return imdb_url
raise Failed(f"IMDb Error: {imdb_url} failed to parse")
def validate_imdb_lists(self, imdb_lists, language): def validate_imdb_lists(self, imdb_lists, language):
valid_lists = [] valid_lists = []
for imdb_list in util.get_list(imdb_lists, split=False): for imdb_dict in util.get_list(imdb_lists, split=False):
if isinstance(imdb_list, dict): if not isinstance(imdb_dict, dict):
dict_methods = {dm.lower(): dm for dm in imdb_list} imdb_dict = {"url": imdb_dict}
if "url" in dict_methods and imdb_list[dict_methods["url"]]: dict_methods = {dm.lower(): dm for dm in imdb_dict}
imdb_url = self._validate_url(imdb_list[dict_methods["url"]], language) imdb_url = util.parse_from_dict("imdb_list", "url", imdb_dict, dict_methods).strip()
else: if not imdb_url.startswith((urls["list"], urls["search"], urls["keyword"])):
raise Failed("Collection Error: imdb_list attribute url is required") raise Failed(f"IMDb Error: {imdb_url} must begin with either:\n{urls['list']} (For Lists)\n{urls['search']} (For Searches)\n{urls['keyword']} (For Keyword Searches)")
if "limit" in dict_methods and imdb_list[dict_methods["limit"]]: self._total(imdb_url, language)
list_count = util.regex_first_int(imdb_list[dict_methods["limit"]], "List Limit", default=0) list_count = util.parse_int_from_dict("imdb_list", "limit", imdb_dict, dict_methods, 0, minimum=0) if "limit" in dict_methods else 0
else:
list_count = 0
else:
imdb_url = self._validate_url(str(imdb_list), language)
list_count = 0
valid_lists.append({"url": imdb_url, "limit": list_count}) valid_lists.append({"url": imdb_url, "limit": list_count})
return valid_lists return valid_lists
def _fix_url(self, imdb_url):
if imdb_url.startswith(urls["list"]):
try: list_id = re.search("(\\d+)", str(imdb_url)).group(1)
except AttributeError: raise Failed(f"IMDb Error: Failed to parse List ID from {imdb_url}")
return f"{urls['search']}lists=ls{list_id}"
elif imdb_url.endswith("/"):
return imdb_url[:-1]
else:
return imdb_url
def _total(self, imdb_url, language): def _total(self, imdb_url, language):
headers = util.header(language) headers = util.header(language)
if imdb_url.startswith(urls["keyword"]): if imdb_url.startswith(urls["keyword"]):
results = self.config.get_html(imdb_url, headers=headers).xpath("//div[@class='desc']/text()") page_type = "keyword"
total = None elif imdb_url.startswith(urls["list"]):
for result in results: page_type = "list"
if "title" in result:
try:
total = int(re.findall("(\\d+) title", result)[0])
break
except IndexError:
pass
if total is None:
raise Failed(f"IMDb Error: No Results at URL: {imdb_url}")
return total, 50
else: else:
try: results = self.config.get_html(imdb_url, headers=headers).xpath("//div[@class='desc']/span/text()")[0].replace(",", "") page_type = "search"
except IndexError: raise Failed(f"IMDb Error: Failed to parse URL: {imdb_url}") results = self.config.get_html(imdb_url, headers=headers).xpath(xpath[page_type])
try: total = int(re.findall("(\\d+) title", results)[0]) total = 0
except IndexError: raise Failed(f"IMDb Error: No Results at URL: {imdb_url}") for result in results:
return total, 250 if "title" in result:
try:
total = int(re.findall("(\\d+) title", result.replace(",", ""))[0])
break
except IndexError:
pass
if total > 0:
return total, item_counts[page_type]
raise ValueError(f"IMDb Error: Failed to parse URL: {imdb_url}")
def _ids_from_url(self, imdb_url, language, limit): def _ids_from_url(self, imdb_url, language, limit):
current_url = self._fix_url(imdb_url) total, item_count = self._total(imdb_url, language)
total, item_count = self._total(current_url, language)
headers = util.header(language) headers = util.header(language)
imdb_ids = [] imdb_ids = []
if "&start=" in current_url: current_url = re.sub("&start=\\d+", "", current_url) parsed_url = urlparse(imdb_url)
if "&count=" in current_url: current_url = re.sub("&count=\\d+", "", current_url) params = parse_qs(parsed_url.query)
if "&page=" in current_url: current_url = re.sub("&page=\\d+", "", current_url) imdb_base = parsed_url._replace(query=None).geturl()
if limit < 1 or total < limit: limit = total params.pop("start", None)
params.pop("count", None)
params.pop("page", None)
if limit < 1 or total < limit:
limit = total
remainder = limit % item_count remainder = limit % item_count
if remainder == 0: remainder = item_count if remainder == 0:
remainder = item_count
num_of_pages = math.ceil(int(limit) / item_count) num_of_pages = math.ceil(int(limit) / item_count)
for i in range(1, num_of_pages + 1): for i in range(1, num_of_pages + 1):
start_num = (i - 1) * item_count + 1 start_num = (i - 1) * item_count + 1
util.print_return(f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}") util.print_return(f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}")
if imdb_url.startswith(urls["keyword"]): if imdb_base.startswith((urls["list"], urls["keyword"])):
response = self.config.get_html(f"{current_url}&page={i}", headers=headers) params["page"] = i
else:
response = self.config.get_html(f"{current_url}&count={remainder if i == num_of_pages else item_count}&start={start_num}", headers=headers)
if imdb_url.startswith(urls["keyword"]) and i == num_of_pages:
imdb_ids.extend(response.xpath("//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst")[:remainder])
else: else:
imdb_ids.extend(response.xpath("//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst")) params["count"] = remainder if i == num_of_pages else item_count
params["start"] = start_num
ids_found = self.config.get_html(imdb_base, headers=headers, params=params).xpath(xpath["imdb_id"])
if imdb_base.startswith((urls["list"], urls["keyword"])) and i == num_of_pages:
ids_found = ids_found[:remainder]
imdb_ids.extend(ids_found)
time.sleep(2) time.sleep(2)
util.print_end() util.print_end()
if imdb_ids: return imdb_ids if len(imdb_ids) > 0:
else: raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}") return imdb_ids
raise ValueError(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
def get_items(self, method, data, language, is_movie): def get_items(self, method, data, language, is_movie):
pretty = util.pretty_names[method] if method in util.pretty_names else method pretty = util.pretty_names[method] if method in util.pretty_names else method

@ -60,7 +60,7 @@ collection_mode_options = {
"hide_items": "hideItems", "hideitems": "hideItems", "hide_items": "hideItems", "hideitems": "hideItems",
"show_items": "showItems", "showitems": "showItems" "show_items": "showItems", "showitems": "showItems"
} }
collection_order_options = {"release": "release", "alpha": "alpha"} collection_order_options = ["release", "alpha", "custom"]
collection_mode_keys = {-1: "default", 0: "hide", 1: "hideItems", 2: "showItems"} collection_mode_keys = {-1: "default", 0: "hide", 1: "hideItems", 2: "showItems"}
collection_order_keys = {0: "release", 1: "alpha", 2: "custom"} collection_order_keys = {0: "release", 1: "alpha", 2: "custom"}
item_advance_keys = { item_advance_keys = {

@ -349,7 +349,6 @@ def parse_int(method, data, default=10, minimum=1, maximum=None):
return default return default
def parse_from_dict(parent, method, data, methods, default=None, options=None, translation=None): def parse_from_dict(parent, method, data, methods, default=None, options=None, translation=None):
message = ""
if options is None and translation is not None: if options is None and translation is not None:
options = [o for o in translation] options = [o for o in translation]
if method not in methods: if method not in methods:

@ -504,7 +504,7 @@ try:
minutes = int((seconds % 3600) // 60) minutes = int((seconds % 3600) // 60)
time_str = f"{hours} Hour{'s' if hours > 1 else ''} and " if hours > 0 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 ''}" time_str += f"{minutes} Minute{'s' if minutes > 1 else ''}"
util.print_return(f"Current Time: {current} | {time_str} until the next run at {og_time_str} {times_to_run}") util.print_return(f"Current Time: {current} | {time_str} until the next run at {og_time_str} {util.compile_list(times_to_run)}")
time.sleep(60) time.sleep(60)
except KeyboardInterrupt: except KeyboardInterrupt:
util.separator("Exiting Plex Meta Manager") util.separator("Exiting Plex Meta Manager")

Loading…
Cancel
Save