pull/353/head
meisnate12 3 years ago
parent a56fabc83d
commit 382c156b66

@ -72,7 +72,7 @@ class AniDB:
current_url = f"{base_url}{next_page_list[0]}"
return anidb_ids[:limit]
def get_items(self, method, data, language):
def get_anidb_ids(self, method, data, language):
anidb_ids = []
if method == "anidb_popular":
logger.info(f"Processing AniDB Popular: {data} Anime")
@ -88,9 +88,6 @@ class AniDB:
anidb_ids.extend(self._relations(data, language))
else:
raise Failed(f"AniDB Error: Method {method} not supported")
movie_ids, show_ids = self.config.Convert.anidb_to_ids(anidb_ids)
logger.debug("")
logger.debug(f"{len(anidb_ids)} AniDB IDs Found: {anidb_ids}")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids
return anidb_ids

@ -207,7 +207,7 @@ class AniList:
return anilist_values
raise Failed(f"AniList Error: No valid AniList IDs in {anilist_ids}")
def get_items(self, method, data):
def get_anilist_ids(self, method, data):
if method == "anilist_id":
logger.info(f"Processing AniList ID: {data}")
anilist_id, name = self._validate(data)
@ -235,9 +235,6 @@ class AniList:
logger.info(f"Processing AniList Relations: ({data}) {name} ({len(anilist_ids)} Anime)")
else:
raise Failed(f"AniList Error: Method {method} not supported")
movie_ids, show_ids = self.config.Convert.anilist_to_ids(anilist_ids)
logger.debug("")
logger.debug(f"{len(anilist_ids)} AniList IDs Found: {anilist_ids}")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids
return anilist_ids

@ -164,6 +164,7 @@ class CollectionBuilder:
self.filters = []
self.tmdb_filters = []
self.rating_keys = []
self.filtered_keys = []
self.run_again_movies = []
self.run_again_shows = []
self.posters = {}
@ -992,22 +993,99 @@ class CollectionBuilder:
else:
logger.error(message)
def collect_rating_keys(self):
filtered_keys = {}
def find_rating_keys(self):
for method, value in self.builders:
ids = []
rating_keys = []
logger.debug("")
logger.debug(f"Builder: {method}: {value}")
logger.info("")
if "plex" in method:
rating_keys = self.library.get_rating_keys(method, value)
elif "tautulli" in method:
rating_keys = self.library.Tautulli.get_rating_keys(self.library, value)
elif "anidb" in method:
anidb_ids = self.config.AniDB.get_anidb_ids(method, value, self.language)
ids = self.config.Convert.anidb_to_ids(anidb_ids)
elif "anilist" in method:
anilist_ids = self.config.AniList.get_anilist_ids(method, value)
ids = self.config.Convert.anilist_to_ids(anilist_ids)
elif "mal" in method:
mal_ids = self.config.MyAnimeList.get_mal_ids(method, value)
ids = self.config.Convert.myanimelist_to_ids(mal_ids)
elif "tvdb" in method:
ids = self.config.TVDb.get_tvdb_ids(method, value, self.language)
elif "imdb" in method:
ids = self.config.IMDb.get_imdb_ids(method, value, self.language)
elif "icheckmovies" in method:
ids = self.config.ICheckMovies.get_icheckmovies_ids(method, value, self.language)
elif "letterboxd" in method:
ids = self.config.Letterboxd.get_tmdb_ids(method, value, self.language)
elif "stevenlu" in method:
ids = self.config.StevenLu.get_stevenlu_ids(method)
elif "tmdb" in method:
ids = self.config.TMDb.get_tmdb_ids(method, value, self.library.is_movie)
elif "trakt" in method:
ids = self.config.Trakt.get_trakt_ids(method, value, self.library.is_movie)
else:
logger.error(f"Collection Error: {method} method not supported")
if len(ids) > 0:
logger.debug("")
logger.debug(f"{len(ids)} IDs Found: {ids}")
total_ids = len(ids)
if total_ids > 0:
for i, input_data in enumerate(ids, 1):
input_id, id_type = input_data
util.print_return(f"Parsing ID {i}/{total_ids}")
if id_type == "tmdb":
if input_id in self.library.movie_map:
rating_keys.append(self.library.movie_map[input_id][0])
elif input_id not in self.missing_movies:
self.missing_movies.append(input_id)
elif id_type in ["tvdb", "tmdb_show"]:
if id_type == "tmdb_show":
try:
input_id = self.config.Convert.tmdb_to_tvdb(input_id, fail=True)
except Failed as e:
logger.error(e)
continue
if input_id in self.library.show_map:
rating_keys.append(self.library.show_map[input_id][0])
elif input_id not in self.missing_shows:
self.missing_shows.append(input_id)
elif id_type == "imdb":
if input_id in self.library.imdb_map:
rating_keys.append(self.library.imdb_map[input_id][0])
else:
try:
tmdb_id, tmdb_type = self.config.Convert.imdb_to_tmdb(input_id)
if tmdb_type == "movie":
if tmdb_id not in self.missing_movies:
self.missing_movies.append(tmdb_id)
else:
tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id)
if tvdb_id not in self.missing_shows:
self.missing_shows.append(tvdb_id)
except Failed as e:
logger.error(e)
continue
util.print_end()
if len(rating_keys) > 0:
name = self.obj.title if self.obj else self.name
def add_rating_keys(keys):
if not isinstance(keys, list):
keys = [keys]
total = len(keys)
if not isinstance(rating_keys, list):
rating_keys = [rating_keys]
total = len(rating_keys)
max_length = len(str(total))
if self.filters and self.details["show_filtered"] is True:
logger.info("")
logger.info("Filtering Builder:")
for i, key in enumerate(keys, 1):
for i, key in enumerate(rating_keys, 1):
if key not in self.rating_keys:
if key in filtered_keys:
if key in self.filtered_keys:
if self.details["show_filtered"] is True:
logger.info(f"{name} Collection | X | {filtered_keys[key]}")
logger.info(f"{name} Collection | X | {self.filtered_keys[key]}")
else:
try:
current = self.fetch_item(key)
@ -1018,87 +1096,9 @@ class CollectionBuilder:
if self.check_filters(current, f"{(' ' * (max_length - len(str(i))))}{i}/{total}"):
self.rating_keys.append(key)
else:
if key not in filtered_keys:
filtered_keys[key] = current_title
self.filtered_keys[key] = current_title
if self.details["show_filtered"] is True:
logger.info(f"{name} Collection | X | {current_title}")
def check_map(input_ids):
movie_ids, show_ids = input_ids
items_found_inside = 0
if len(movie_ids) > 0:
items_found_inside += len(movie_ids)
movie_rating_keys = []
for movie_id in movie_ids:
if movie_id in self.library.movie_map:
movie_rating_keys.append(self.library.movie_map[movie_id][0])
elif movie_id not in self.missing_movies:
self.missing_movies.append(movie_id)
add_rating_keys(movie_rating_keys)
if len(show_ids) > 0:
items_found_inside += len(show_ids)
show_rating_keys = []
for show_id in show_ids:
if show_id in self.library.show_map:
show_rating_keys.append(self.library.show_map[show_id][0])
elif show_id not in self.missing_shows:
self.missing_shows.append(show_id)
add_rating_keys(show_rating_keys)
return items_found_inside
for method, value in self.builders:
logger.debug("")
logger.debug(f"Builder: {method}: {value}")
logger.info("")
if "plex" in method: add_rating_keys(self.library.get_items(method, value))
elif "tautulli" in method: add_rating_keys(self.library.Tautulli.get_items(self.library, value))
elif "anidb" in method: check_map(self.config.AniDB.get_items(method, value, self.language))
elif "anilist" in method: check_map(self.config.AniList.get_items(method, value))
elif "mal" in method: check_map(self.config.MyAnimeList.get_items(method, value))
elif "tvdb" in method: check_map(self.config.TVDb.get_items(method, value, self.language))
elif "imdb" in method: check_map(self.config.IMDb.get_items(method, value, self.language, self.library.is_movie))
elif "icheckmovies" in method: check_map(self.config.ICheckMovies.get_items(method, value, self.language))
elif "letterboxd" in method: check_map(self.config.Letterboxd.get_items(method, value, self.language))
elif "stevenlu" in method: check_map(self.config.StevenLu.get_items(method))
elif "tmdb" in method: check_map(self.config.TMDb.get_items(method, value, self.library.is_movie))
elif "trakt" in method: check_map(self.config.Trakt.get_items(method, value, self.library.is_movie))
else: logger.error(f"Collection Error: {method} method not supported")
def check_tmdb_filter(self, item_id, is_movie, item=None, check_released=False):
if self.tmdb_filters or check_released:
try:
if item is None:
item = self.config.TMDb.get_movie(item_id) if is_movie else self.config.TMDb.get_show(self.config.Convert.tvdb_to_tmdb(item_id))
if check_released:
if util.validate_date(item.release_date if is_movie else item.first_air_date, "") > self.current_time:
return False
for filter_method, filter_data in self.tmdb_filters:
filter_attr, modifier, filter_final = self._split(filter_method)
if filter_attr == "original_language":
if (modifier == ".not" and item.original_language in filter_data) \
or (modifier == "" and item.original_language not in filter_data):
return False
elif filter_attr in ["first_episode_aired", "last_episode_aired"]:
tmdb_date = None
if filter_attr == "first_episode_aired":
tmdb_date = item.first_air_date
elif filter_attr == "last_episode_aired":
tmdb_date = item.last_air_date
if not util.date_filter(tmdb_date, modifier, filter_data, filter_final, self.current_time):
return False
elif modifier in [".gt", ".gte", ".lt", ".lte"]:
attr = None
if filter_attr == "tmdb_vote_count":
attr = item.vote_count
elif filter_attr == "year" and is_movie:
attr = item.year
elif filter_attr == "year" and not is_movie:
air_date = item.first_air_date
if air_date:
attr = util.validate_date(air_date, "Year Filter").year
if util.number_filter(attr, modifier, filter_data):
return False
except Failed:
return False
return True
def build_filter(self, method, plex_filter, smart=False):
if smart:
@ -1385,6 +1385,44 @@ class CollectionBuilder:
logger.info("")
logger.info(f"{total} {media_type} Processed")
def check_tmdb_filter(self, item_id, is_movie, item=None, check_released=False):
if self.tmdb_filters or check_released:
try:
if item is None:
item = self.config.TMDb.get_movie(item_id) if is_movie else self.config.TMDb.get_show(self.config.Convert.tvdb_to_tmdb(item_id))
if check_released:
if util.validate_date(item.release_date if is_movie else item.first_air_date, "") > self.current_time:
return False
for filter_method, filter_data in self.tmdb_filters:
filter_attr, modifier, filter_final = self._split(filter_method)
if filter_attr == "original_language":
if (modifier == ".not" and item.original_language in filter_data) \
or (modifier == "" and item.original_language not in filter_data):
return False
elif filter_attr in ["first_episode_aired", "last_episode_aired"]:
tmdb_date = None
if filter_attr == "first_episode_aired":
tmdb_date = util.validate_date(item.first_air_date, "TMDB First Air Date")
elif filter_attr == "last_episode_aired":
tmdb_date = util.validate_date(item.last_air_date, "TMDB Last Air Date")
if util.is_date_filter(tmdb_date, modifier, filter_data, filter_final, self.current_time):
return False
elif modifier in [".gt", ".gte", ".lt", ".lte"]:
attr = None
if filter_attr == "tmdb_vote_count":
attr = item.vote_count
elif filter_attr == "year" and is_movie:
attr = item.year
elif filter_attr == "year" and not is_movie:
air_date = item.first_air_date
if air_date:
attr = util.validate_date(air_date, "Year Filter").year
if util.is_number_filter(attr, modifier, filter_data):
return False
except Failed:
return False
return True
def check_filters(self, current, display):
if self.filters:
util.print_return(f"Filtering {display} {current.title}")
@ -1406,41 +1444,19 @@ class CollectionBuilder:
if not self.check_tmdb_filter(t_id, current.ratingKey in self.library.movie_rating_key_map):
return False
elif filter_attr in ["release", "added", "last_played"]:
if not util.date_filter(getattr(current, filter_actual), modifier, filter_data, filter_final, self.current_time):
if util.is_date_filter(getattr(current, filter_actual), modifier, filter_data, filter_final, self.current_time):
return False
elif filter_attr == "audio_track_title":
jailbreak = False
elif filter_attr in ["audio_track_title", "filepath", "title", "studio"]:
values = []
if filter_attr == "audio_track_title":
for media in current.media:
for part in media.parts:
for audio in part.audioStreams():
for check_title in filter_data:
title = audio.title if audio.title else ""
if util.string_filter(title, modifier, check_title):
jailbreak = True
break
if jailbreak: break
if jailbreak: break
if jailbreak: break
if (jailbreak and modifier == ".not") or (not jailbreak and modifier in ["", ".begins", ".ends", ".regex"]):
return False
values.extend([a.title for a in part.audioStreams() if a.title])
elif filter_attr == "filepath":
jailbreak = False
for location in current.locations:
for check_text in filter_data:
if util.string_filter(location, modifier, check_text):
jailbreak = True
break
if jailbreak: break
if (jailbreak and modifier == ".not") or (not jailbreak and modifier in ["", ".begins", ".ends", ".regex"]):
return False
values = [loc for loc in current.locations]
elif filter_attr in ["title", "studio"]:
jailbreak = False
current_data = getattr(current, filter_actual)
for check_data in filter_data:
if util.string_filter(current_data, modifier, check_data):
jailbreak = True
break
if (jailbreak and modifier == ".not") or (not jailbreak and modifier in ["", ".begins", ".ends", ".regex"]):
values = [getattr(current, filter_actual)]
if util.is_string_filter(values, modifier, filter_data):
return False
elif filter_attr == "history":
item_date = current.originallyAvailableAt
@ -1461,11 +1477,8 @@ class CollectionBuilder:
if date_match is False:
return False
elif modifier in [".gt", ".gte", ".lt", ".lte"]:
if filter_attr == "duration":
attr = getattr(current, filter_actual) / 60000
else:
attr = getattr(current, filter_actual)
if util.number_filter(attr, modifier, filter_data):
divider = 60000 if filter_attr == "duration" else 1
if util.is_number_filter(getattr(current, filter_actual) / divider, modifier, filter_data):
return False
else:
attrs = []

@ -18,14 +18,16 @@ class Cache:
else:
logger.info(f"Using cache database at {self.cache_path}")
cursor.execute("DROP TABLE IF EXISTS guids")
cursor.execute("DROP TABLE IF EXISTS guid_map")
cursor.execute("DROP TABLE IF EXISTS imdb_to_tvdb_map")
cursor.execute("DROP TABLE IF EXISTS tmdb_to_tvdb_map")
cursor.execute("DROP TABLE IF EXISTS imdb_map")
cursor.execute(
"""CREATE TABLE IF NOT EXISTS guid_map (
"""CREATE TABLE IF NOT EXISTS guids_map (
key INTEGER PRIMARY KEY,
plex_guid TEXT UNIQUE,
t_id TEXT,
imdb_id TEXT,
media_type TEXT,
expiration_date TEXT)"""
)
@ -100,27 +102,39 @@ class Cache:
def query_guid_map(self, plex_guid):
id_to_return = None
imdb_id = None
media_type = None
expired = None
with sqlite3.connect(self.cache_path) as connection:
connection.row_factory = sqlite3.Row
with closing(connection.cursor()) as cursor:
cursor.execute(f"SELECT * FROM guid_map WHERE plex_guid = ?", (plex_guid,))
cursor.execute(f"SELECT * FROM guids_map WHERE plex_guid = ?", (plex_guid,))
row = cursor.fetchone()
if row:
time_between_insertion = datetime.now() - datetime.strptime(row["expiration_date"], "%Y-%m-%d")
id_to_return = util.get_list(row["t_id"], int_list=True)
imdb_id = util.get_list(row["imdb_id"])
media_type = row["media_type"]
expired = time_between_insertion.days > self.expiration
return id_to_return, media_type, expired
return id_to_return, imdb_id, media_type, expired
def update_guid_map(self, media_type, plex_guid, t_id, expired):
self._update_map("guid_map", "plex_guid", plex_guid, "t_id", t_id, expired, media_type=media_type)
def update_guid_map(self, plex_guid, t_id, imdb_id, expired, media_type):
expiration_date = datetime.now() if expired is True else (datetime.now() - timedelta(days=random.randint(1, self.expiration)))
with sqlite3.connect(self.cache_path) as connection:
connection.row_factory = sqlite3.Row
with closing(connection.cursor()) as cursor:
cursor.execute(f"INSERT OR IGNORE INTO guids_map(plex_guid) VALUES(?)", (plex_guid,))
if media_type is None:
sql = f"UPDATE guids_map SET t_id = ?, imdb_id = ?, expiration_date = ? WHERE plex_guid = ?"
cursor.execute(sql, (t_id, imdb_id, expiration_date.strftime("%Y-%m-%d"), plex_guid))
else:
sql = f"UPDATE guids_map SET t_id = ?, imdb_id = ?, expiration_date = ?, media_type = ? WHERE plex_guid = ?"
cursor.execute(sql, (t_id, imdb_id, expiration_date.strftime("%Y-%m-%d"), media_type, plex_guid))
def query_imdb_to_tmdb_map(self, media_type, _id, imdb=True):
def query_imdb_to_tmdb_map(self, _id, imdb=True, media_type=None, return_type=False):
from_id = "imdb_id" if imdb else "tmdb_id"
to_id = "tmdb_id" if imdb else "imdb_id"
return self._query_map("imdb_to_tmdb_map", _id, from_id, to_id, media_type=media_type)
return self._query_map("imdb_to_tmdb_map", _id, from_id, to_id, media_type=media_type, return_type=return_type)
def update_imdb_to_tmdb_map(self, media_type, expired, imdb_id, tmdb_id):
self._update_map("imdb_to_tmdb_map", "imdb_id", imdb_id, "tmdb_id", tmdb_id, expired, media_type=media_type)
@ -147,9 +161,10 @@ class Cache:
def update_letterboxd_map(self, expired, letterboxd_id, tmdb_id):
self._update_map("letterboxd_map", "letterboxd_id", letterboxd_id, "tmdb_id", tmdb_id, expired)
def _query_map(self, map_name, _id, from_id, to_id, media_type=None):
def _query_map(self, map_name, _id, from_id, to_id, media_type=None, return_type=False):
id_to_return = None
expired = None
out_type = None
with sqlite3.connect(self.cache_path) as connection:
connection.row_factory = sqlite3.Row
with closing(connection.cursor()) as cursor:
@ -163,6 +178,10 @@ class Cache:
time_between_insertion = datetime.now() - datetime_object
id_to_return = row[to_id] if to_id == "imdb_id" else int(row[to_id])
expired = time_between_insertion.days > self.expiration
out_type = row["media_type"] if return_type else None
if out_type:
return id_to_return, out_type, expired
else:
return id_to_return, expired
def _update_map(self, map_name, val1_name, val1, val2_name, val2, expired, media_type=None):

@ -21,8 +21,8 @@ class Convert:
imdb_ids = util.get_list(imdbid[0])
tmdb_ids = []
for imdb in imdb_ids:
tmdb_id = self.imdb_to_tmdb(imdb)
if tmdb_id:
tmdb_id, tmdb_type = self.imdb_to_tmdb(imdb)
if tmdb_id and tmdb_type == "movie":
tmdb_ids.append(tmdb_id)
if tmdb_ids:
return None, imdb_ids, tmdb_ids
@ -78,18 +78,17 @@ class Convert:
return converted_ids
def anidb_to_ids(self, anidb_list):
show_ids = []
movie_ids = []
ids = []
for anidb_id in anidb_list:
try:
tvdb_id, _, tmdb_ids = self._anidb(anidb_id, fail=True)
if tvdb_id:
show_ids.append(tvdb_id)
ids.append((tvdb_id, "tvdb"))
if tmdb_ids:
movie_ids.extend(tmdb_ids)
ids.extend((tmdb_ids, "tmdb"))
except Failed as e:
logger.error(e)
return movie_ids, show_ids
return ids
def anilist_to_ids(self, anilist_ids):
anidb_ids = []
@ -113,43 +112,40 @@ class Convert:
media_type = "movie" if is_movie else "show"
expired = False
if self.config.Cache and is_movie:
cache_id, expired = self.config.Cache.query_imdb_to_tmdb_map(media_type, tmdb_id, imdb=False)
cache_id, expired = self.config.Cache.query_imdb_to_tmdb_map(tmdb_id, imdb=False, media_type=media_type)
if cache_id and not expired:
return cache_id
imdb_id = None
try:
imdb_id = self.config.TMDb.convert_from(tmdb_id, "imdb_id", is_movie)
except Failed:
pass
if imdb_id:
if self.config.Cache:
self.config.Cache.update_imdb_to_tmdb_map(media_type, expired, imdb_id, tmdb_id)
return imdb_id
elif fail:
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No IMDb ID Found for TMDb ID: {tmdb_id}")
else:
return None
def imdb_to_tmdb(self, imdb_id, is_movie=True, fail=False):
media_type = "movie" if is_movie else "show"
def imdb_to_tmdb(self, imdb_id, fail=False):
expired = False
if self.config.Cache and is_movie:
cache_id, expired = self.config.Cache.query_imdb_to_tmdb_map(media_type, imdb_id, imdb=True)
if self.config.Cache:
cache_id, cache_type, expired = self.config.Cache.query_imdb_to_tmdb_map(imdb_id, imdb=True, return_type=True)
if cache_id and not expired:
return cache_id
tmdb_id = None
return cache_id, cache_type
try:
tmdb_id = self.config.TMDb.convert_to(imdb_id, "imdb_id", is_movie)
except Failed:
pass
tmdb_id, tmdb_type = self.config.TMDb.convert_imdb_to(imdb_id)
if tmdb_id:
if self.config.Cache:
self.config.Cache.update_imdb_to_tmdb_map(media_type, expired, imdb_id, tmdb_id)
return tmdb_id
elif fail:
self.config.Cache.update_imdb_to_tmdb_map(tmdb_type, expired, imdb_id, tmdb_id)
return tmdb_id, tmdb_type
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No TMDb ID Found for IMDb ID: {imdb_id}")
else:
return None
return None, None
def tmdb_to_tvdb(self, tmdb_id, fail=False):
expired = False
@ -157,16 +153,15 @@ class Convert:
cache_id, expired = self.config.Cache.query_tmdb_to_tvdb_map(tmdb_id, tmdb=True)
if cache_id and not expired:
return cache_id
tvdb_id = None
try:
tvdb_id = self.config.TMDb.convert_from(tmdb_id, "tvdb_id", False)
except Failed:
pass
if tvdb_id:
if self.config.Cache:
self.config.Cache.update_tmdb_to_tvdb_map(expired, tmdb_id, tvdb_id)
return tvdb_id
elif fail:
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No TVDb ID Found for TMDb ID: {tmdb_id}")
else:
return None
@ -177,16 +172,15 @@ class Convert:
cache_id, expired = self.config.Cache.query_tmdb_to_tvdb_map(tvdb_id, tmdb=False)
if cache_id and not expired:
return cache_id
tmdb_id = None
try:
tmdb_id = self.config.TMDb.convert_to(tvdb_id, "tvdb_id", False)
except Failed:
pass
tmdb_id = self.config.TMDb.convert_tvdb_to(tvdb_id)
if tmdb_id:
if self.config.Cache:
self.config.Cache.update_tmdb_to_tvdb_map(expired, tmdb_id, tvdb_id)
return tmdb_id
elif fail:
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No TMDb ID Found for TVDb ID: {tvdb_id}")
else:
return None
@ -197,16 +191,15 @@ class Convert:
cache_id, expired = self.config.Cache.query_imdb_to_tvdb_map(tvdb_id, imdb=False)
if cache_id and not expired:
return cache_id
imdb_id = None
try:
imdb_id = self.tmdb_to_imdb(self.tvdb_to_tmdb(tvdb_id, fail=True), is_movie=False, fail=True)
except Failed:
pass
if imdb_id:
if self.config.Cache:
self.config.Cache.update_imdb_to_tvdb_map(expired, imdb_id, tvdb_id)
return imdb_id
elif fail:
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No IMDb ID Found for TVDb ID: {tvdb_id}")
else:
return None
@ -217,16 +210,17 @@ class Convert:
cache_id, expired = self.config.Cache.query_imdb_to_tvdb_map(imdb_id, imdb=True)
if cache_id and not expired:
return cache_id
tvdb_id = None
try:
tvdb_id = self.tmdb_to_tvdb(self.imdb_to_tmdb(imdb_id, is_movie=False, fail=True), fail=True)
except Failed:
pass
tmdb_id, tmdb_type = self.imdb_to_tmdb(imdb_id, fail=True)
if tmdb_type == "show":
tvdb_id = self.tmdb_to_tvdb(tmdb_id, fail=True)
if tvdb_id:
if self.config.Cache:
self.config.Cache.update_imdb_to_tvdb_map(expired, imdb_id, tvdb_id)
return tvdb_id
elif fail:
except Failed:
pass
if fail:
raise Failed(f"Convert Error: No TVDb ID Found for IMDb ID: {imdb_id}")
else:
return None
@ -238,10 +232,10 @@ class Convert:
imdb_id = []
anidb_id = None
if self.config.Cache:
cache_id, media_type, expired = self.config.Cache.query_guid_map(item.guid)
cache_id, imdb_check, media_type, expired = self.config.Cache.query_guid_map(item.guid)
if cache_id and not expired:
media_id_type = "movie" if "movie" in media_type else "show"
return media_id_type, cache_id
return media_id_type, cache_id, imdb_check
try:
guid = requests.utils.urlparse(item.guid)
item_type = guid.scheme.split(".")[-1]
@ -286,10 +280,16 @@ class Convert:
else:
if not tmdb_id and imdb_id:
for imdb in imdb_id:
tmdb = self.imdb_to_tmdb(imdb, is_movie=library.is_movie)
if tmdb:
tmdb, tmdb_type = self.imdb_to_tmdb(imdb)
if tmdb and ((tmdb_type == "movie" and library.is_movie) or (tmdb_type == "show" and library.is_show)):
tmdb_id.append(tmdb)
if not imdb_id and tmdb_id and library.is_movie:
for tmdb in tmdb_id:
imdb = self.tmdb_to_imdb(tmdb)
if imdb:
imdb_id.append(imdb)
if not tvdb_id and tmdb_id and library.is_show:
for tmdb in tmdb_id:
tvdb = self.tmdb_to_tvdb(tmdb)
@ -298,22 +298,29 @@ class Convert:
if not tvdb_id:
raise Failed(f"Unable to convert TMDb ID: {util.compile_list(tmdb_id)} to TVDb ID")
if not imdb_id and tvdb_id:
for tvdb in tvdb_id:
imdb = self.tvdb_to_imdb(tvdb)
if imdb:
imdb_id.append(imdb)
def update_cache(cache_ids, id_type, guid_type):
def update_cache(cache_ids, id_type, imdb_in, guid_type):
if self.config.Cache:
cache_ids = util.compile_list(cache_ids)
logger.info(util.adjust_space(f" Cache | {'^' if expired else '+'} | {item.guid:<46} | {id_type} ID: {cache_ids:<6} | {item.title}"))
self.config.Cache.update_guid_map(guid_type, item.guid, cache_ids, expired)
imdb_in = util.compile_list(imdb_in) if imdb_in else None
ids = f"{item.guid:<46} | {id_type} ID: {cache_ids:<7} | IMDb ID: {str(imdb_in):<10}"
logger.info(util.adjust_space(f" Cache | {'^' if expired else '+'} | {ids} | {item.title}"))
self.config.Cache.update_guid_map(item.guid, cache_ids, imdb_in, expired, guid_type)
if tmdb_id and library.is_movie:
update_cache(tmdb_id, "TMDb", "movie")
return "movie", tmdb_id
update_cache(tmdb_id, "TMDb", imdb_id, "movie")
return "movie", tmdb_id, imdb_id
elif tvdb_id and library.is_show:
update_cache(tvdb_id, "TVDb", "show")
return "show", tvdb_id
update_cache(tvdb_id, "TVDb", imdb_id, "show")
return "show", tvdb_id, imdb_id
elif anidb_id and tmdb_id and library.is_show:
update_cache(tmdb_id, "TMDb", "show_movie")
return "movie", tmdb_id
update_cache(tmdb_id, "TMDb", imdb_id, "show_movie")
return "movie", tmdb_id, imdb_id
else:
logger.debug(f"TMDb: {tmdb_id}, IMDb: {imdb_id}, TVDb: {tvdb_id}")
raise Failed(f"No ID to convert")
@ -322,4 +329,4 @@ class Convert:
except BadRequest:
util.print_stacktrace()
logger.info(util.adjust_space(f"Mapping Error | {item.guid:<46} | Bad Request for {item.title}"))
return None, None
return None, None, None

@ -16,7 +16,7 @@ class ICheckMovies:
def _parse_list(self, list_url, language):
imdb_urls = self._request(list_url, language, "//a[@class='optionIcon optionIMDB external']/@href")
return [t[t.find("/tt") + 1:-1] for t in imdb_urls]
return [(t[t.find("/tt") + 1:-1], "imdb") for t in imdb_urls]
def get_list_description(self, list_url, language):
descriptions = self._request(list_url, language, "//div[@class='span-19 last']/p/em/text()")
@ -34,21 +34,9 @@ class ICheckMovies:
raise Failed(f"ICheckMovies Error: {list_url} failed to parse")
return valid_lists
def get_items(self, method, data, language):
movie_ids = []
def get_icheckmovies_ids(self, method, data, language):
if method == "icheckmovies_list":
logger.info(f"Processing ICheckMovies List: {data}")
imdb_ids = self._parse_list(data, language)
total_ids = len(imdb_ids)
for i, imdb_id in enumerate(imdb_ids, 1):
try:
util.print_return(f"Converting IMDb ID {i}/{total_ids}")
movie_ids.append(self.config.Convert.imdb_to_tmdb(imdb_id))
except Failed as e:
logger.error(e)
logger.info(util.adjust_space(f"Processed {total_ids} IMDb IDs"))
return self._parse_list(data, language)
else:
raise Failed(f"ICheckMovies Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
return movie_ids, []

@ -95,35 +95,13 @@ class IMDb:
return imdb_ids
raise ValueError(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
def get_items(self, method, data, language, is_movie):
show_ids = []
movie_ids = []
fail_ids = []
def run_convert(imdb_id):
tvdb_id = self.config.Convert.imdb_to_tvdb(imdb_id) if not is_movie else None
tmdb_id = self.config.Convert.imdb_to_tmdb(imdb_id) if tvdb_id is None else None
if tmdb_id: movie_ids.append(tmdb_id)
elif tvdb_id: show_ids.append(tvdb_id)
else:
logger.error(f"Convert Error: No {'' if is_movie else 'TVDb ID or '}TMDb ID found for IMDb: {imdb_id}")
fail_ids.append(imdb_id)
def get_imdb_ids(self, method, data, language):
if method == "imdb_id":
logger.info(f"Processing IMDb ID: {data}")
run_convert(data)
return [(data, "imdb")]
elif method == "imdb_list":
status = f"{data['limit']} Items at " if data['limit'] > 0 else ''
logger.info(f"Processing IMDb List: {status}{data['url']}")
imdb_ids = self._ids_from_url(data["url"], language, data["limit"])
total_ids = len(imdb_ids)
for i, imdb in enumerate(imdb_ids, 1):
util.print_return(f"Converting IMDb ID {i}/{total_ids}")
run_convert(imdb)
logger.info(util.adjust_space(f"Processed {total_ids} IMDb IDs"))
return [(i, "imdb") for i in self._ids_from_url(data["url"], language, data["limit"])]
else:
raise Failed(f"IMDb Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(fail_ids)} IMDb IDs Failed to Convert: {fail_ids}")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids

@ -50,13 +50,13 @@ class Letterboxd:
raise Failed(f"Letterboxd Error: {list_url} failed to parse")
return valid_lists
def get_items(self, method, data, language):
movie_ids = []
def get_tmdb_ids(self, method, data, language):
if method == "letterboxd_list":
logger.info(f"Processing Letterboxd List: {data}")
items = self._parse_list(data, language)
total_items = len(items)
if total_items > 0:
ids = []
for i, item in enumerate(items, 1):
letterboxd_id, slug = item
util.print_return(f"Finding TMDb ID {i}/{total_items}")
@ -72,12 +72,10 @@ class Letterboxd:
continue
if self.config.Cache:
self.config.Cache.update_letterboxd_map(expired, letterboxd_id, tmdb_id)
movie_ids.append(tmdb_id)
ids.append((tmdb_id, "tmdb"))
logger.info(util.adjust_space(f"Processed {total_items} TMDb IDs"))
return ids
else:
logger.error(f"Letterboxd Error: No List Items found in {data}")
raise Failed(f"Letterboxd Error: No List Items found in {data}")
else:
raise Failed(f"Letterboxd Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
return movie_ids, []

@ -155,7 +155,7 @@ class MyAnimeList:
url = f"{urls['user']}/{username}/animelist?{final_status}sort={sort_by}&limit={limit}"
return self._parse_request(url)
def get_items(self, method, data):
def get_mal_ids(self, method, data):
if method == "mal_id":
logger.info(f"Processing MyAnimeList ID: {data}")
mal_ids = [data]
@ -173,9 +173,6 @@ class MyAnimeList:
mal_ids = self._userlist(data["username"], data["status"], data["sort_by"], data["limit"])
else:
raise Failed(f"MyAnimeList Error: Method {method} not supported")
movie_ids, show_ids = self.config.Convert.myanimelist_to_ids(mal_ids)
logger.debug("")
logger.debug(f"{len(mal_ids)} MyAnimeList IDs Found: {mal_ids}")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids
return mal_ids

@ -132,10 +132,6 @@ show_only_searches = [
"episode_user_rating.gt", "episode_user_rating.gte", "episode_user_rating.lt", "episode_user_rating.lte",
"episode_year", "episode_year.not", "episode_year.gt", "episode_year.gte", "episode_year.lt", "episode_year.lte"
]
number_attributes = [
"plays", "episode_plays", "duration", "tmdb_vote_count", "first_episode_aired", "last_episode_aired",
"added", "episode_added", "release", "episode_air_date", "last_played", "episode_last_played"
]
float_attributes = ["user_rating", "episode_user_rating", "critic_rating", "audience_rating"]
boolean_attributes = [
"hdr", "unmatched", "duplicate", "unplayed", "progress", "trash",
@ -143,6 +139,7 @@ boolean_attributes = [
]
tmdb_attributes = ["actor", "director", "producer", "writer"]
date_attributes = ["added", "episode_added", "release", "episode_air_date", "last_played", "episode_last_played", "first_episode_aired", "last_episode_aired"]
number_attributes = ["plays", "episode_plays", "duration", "tmdb_vote_count"] + date_attributes
search_display = {"added": "Date Added", "release": "Release Date", "hdr": "HDR", "progress": "In Progress", "episode_progress": "Episode In Progress"}
sorts = {
None: None,
@ -299,6 +296,7 @@ class Plex:
self.missing = {}
self.movie_map = {}
self.show_map = {}
self.imdb_map = {}
self.movie_rating_key_map = {}
self.show_rating_key_map = {}
self.run_again = []
@ -585,7 +583,7 @@ class Plex:
raise Failed(f"Collection Error: No valid Plex Collections in {collections}")
return valid_collections
def get_items(self, method, data):
def get_rating_keys(self, method, data):
media_type = "Movie" if self.is_movie else "Show"
items = []
if method == "plex_all":
@ -634,7 +632,10 @@ class Plex:
else:
raise Failed(f"Plex Error: Method {method} not supported")
if len(items) > 0:
return [item.ratingKey for item in items]
ids = [item.ratingKey for item in items]
logger.debug("")
logger.debug(f"{len(ids)} Keys Found: {ids}")
return ids
else:
raise Failed("Plex Error: No Items found in Plex")
@ -679,7 +680,7 @@ class Plex:
for i, item in enumerate(items, 1):
util.print_return(f"Processing: {i}/{len(items)} {item.title}")
if item.ratingKey not in self.movie_rating_key_map and item.ratingKey not in self.show_rating_key_map:
id_type, main_id = self.config.Convert.get_id(item, self)
id_type, main_id, imdb_id = self.config.Convert.get_id(item, self)
if main_id:
if id_type == "movie":
self.movie_rating_key_map[item.ratingKey] = main_id[0]
@ -687,6 +688,8 @@ class Plex:
elif id_type == "show":
self.show_rating_key_map[item.ratingKey] = main_id[0]
util.add_dict_list(main_id, item.ratingKey, self.show_map)
if imdb_id:
util.add_dict_list(imdb_id, item.ratingKey, self.imdb_map)
logger.info("")
logger.info(util.adjust_space(f"Processed {len(items)} {'Movies' if self.is_movie else 'Shows'}"))
return items

@ -10,21 +10,9 @@ class StevenLu:
def __init__(self, config):
self.config = config
def get_items(self, method):
movie_ids = []
fail_ids = []
def get_stevenlu_ids(self, method):
if method == "stevenlu_popular":
logger.info(f"Processing StevenLu Popular Movies")
for i in self.config.get_json(base_url):
tmdb_id = self.config.Convert.imdb_to_tmdb(i["imdb_id"])
if tmdb_id:
movie_ids.append(tmdb_id)
else:
logger.error(f"Convert Error: No TMDb ID found for IMDb: {i['imdb_id']}")
fail_ids.append(i["imdb_id"])
return [(i["imdb_id"], "imdb") for i in self.config.get_json(base_url)]
else:
raise Failed(f"StevenLu Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(fail_ids)} IMDb IDs Failed to Convert: {fail_ids}")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
return movie_ids, []

@ -20,7 +20,7 @@ class Tautulli:
if response["response"]["result"] != "success":
raise Failed(f"Tautulli Error: {response['response']['message']}")
def get_items(self, library, params):
def get_rating_keys(self, library, params):
query_size = int(params["list_size"]) + int(params["list_buffer"])
logger.info(f"Processing Tautulli Most {params['list_type'].capitalize()}: {params['list_size']} {'Movies' if library.is_movie else 'Shows'}")
response = self._request(f"{self.url}/api/v2?apikey={self.apikey}&cmd=get_home_stats&time_range={params['list_days']}&stats_count={query_size}")
@ -50,6 +50,8 @@ class Tautulli:
logger.error(f"Plex Error: Item {item} not found")
continue
count += 1
logger.debug("")
logger.debug(f"{len(rating_keys)} Keys Found: {rating_keys}")
return rating_keys
def _section_id(self, library_name):

@ -80,11 +80,24 @@ class TMDb:
raise Failed(f"TMDb Error: TMDb {'Movie' if is_movie else 'Show'} ID: {tmdb_id} not found")
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed)
def convert_to(self, external_id, external_source, is_movie):
search_results = self.Movie.external(external_id=external_id, external_source=external_source)
search = search_results["movie_results" if is_movie else "tv_results"]
if len(search) == 1: return int(search[0]["id"])
else: raise Failed(f"TMDb Error: No TMDb ID found for {external_source.upper().replace('B_', 'b ')} {external_id}")
def convert_to(self, external_id, external_source):
return self.Movie.external(external_id=external_id, external_source=external_source)
def convert_tvdb_to(self, tvdb_id):
search = self.convert_to(tvdb_id, "tvdb_id")
if len(search["tv_results"]) == 1:
return int(search["tv_results"][0]["id"])
else:
raise Failed(f"TMDb Error: No TMDb ID found for TVDb ID {tvdb_id}")
def convert_imdb_to(self, imdb_id):
search = self.convert_to(imdb_id, "imdb_id")
if len(search["movie_results"]) > 0:
return int(search["movie_results"][0]["id"]), "movie"
elif len(search["tv_results"]) > 0:
return int(search["tv_results"][0]["id"]), "show"
else:
raise Failed(f"TMDb Error: No TMDb ID found for IMDb ID {imdb_id}")
def get_movie_show_or_collection(self, tmdb_id, is_movie):
if is_movie:
@ -140,35 +153,27 @@ class TMDb:
except TMDbException as e: raise Failed(f"TMDb Error: No List found for TMDb ID {tmdb_id}: {e}")
def _credits(self, tmdb_id, actor=False, crew=False, director=False, producer=False, writer=False):
movie_ids = []
show_ids = []
ids = []
actor_credits = self._person_credits(tmdb_id)
if actor:
for credit in actor_credits.cast:
if credit.media_type == "movie":
movie_ids.append(credit.id)
ids.append((credit.id, "tmdb"))
elif credit.media_type == "tv":
try:
show_ids.append(self.config.Convert.tmdb_to_tvdb(credit.id, fail=True))
except Failed as e:
logger.warning(e)
ids.append((credit.id, "tmdb_show"))
for credit in actor_credits.crew:
if crew or \
(director and credit.department == "Directing") or \
(producer and credit.department == "Production") or \
(writer and credit.department == "Writing"):
if credit.media_type == "movie":
movie_ids.append(credit.id)
ids.append((credit.id, "tmdb"))
elif credit.media_type == "tv":
try:
show_ids.append(self.config.Convert.tmdb_to_tvdb(credit.id, fail=True))
except Failed as e:
logger.warning(e)
return movie_ids, show_ids
ids.append((credit.id, "tmdb_show"))
return ids
def _pagenation(self, method, amount, is_movie):
ids = []
count = 0
for x in range(int(amount / 20) + 1):
if method == "tmdb_popular": tmdb_items = self.Movie.popular(x + 1) if is_movie else self.TV.popular(x + 1)
elif method == "tmdb_top_rated": tmdb_items = self.Movie.top_rated(x + 1) if is_movie else self.TV.top_rated(x + 1)
@ -178,18 +183,15 @@ class TMDb:
else: raise Failed(f"TMDb Error: {method} method not supported")
for tmdb_item in tmdb_items:
try:
ids.append(tmdb_item.id if is_movie else self.config.Convert.tmdb_to_tvdb(tmdb_item.id, fail=True))
count += 1
ids.append((tmdb_item.id, "tmdb" if is_movie else "tmdb_show"))
except Failed as e:
logger.error(e)
pass
if count == amount: break
if count == amount: break
if len(ids) == amount: break
if len(ids) == amount: break
return ids
def _discover(self, attrs, amount, is_movie):
ids = []
count = 0
for date_attr in discover_dates:
if date_attr in attrs:
attrs[date_attr] = util.validate_date(attrs[date_attr], f"tmdb_discover attribute {date_attr}", return_as="%Y-%m-%d")
@ -202,13 +204,11 @@ class TMDb:
tmdb_items = self.Discover.discover_movies(attrs) if is_movie else self.Discover.discover_tv_shows(attrs)
for tmdb_item in tmdb_items:
try:
ids.append(tmdb_item.id if is_movie else self.config.Convert.tmdb_to_tvdb(tmdb_item.id, fail=True))
count += 1
ids.append((tmdb_item.id, "tmdb" if is_movie else "tmdb_show"))
except Failed as e:
logger.error(e)
pass
if count == amount: break
if count == amount: break
if len(ids) == amount: break
if len(ids) == amount: break
return ids, amount
def validate_tmdb_ids(self, tmdb_ids, tmdb_method):
@ -231,11 +231,10 @@ class TMDb:
elif tmdb_type == "List": self.get_list(tmdb_id)
return tmdb_id
def get_items(self, method, data, is_movie):
def get_tmdb_ids(self, method, data, is_movie):
pretty = method.replace("_", " ").title().replace("Tmdb", "TMDb")
media_type = "Movie" if is_movie else "Show"
movie_ids = []
show_ids = []
ids = []
if method in ["tmdb_discover", "tmdb_company", "tmdb_keyword"] or (method == "tmdb_network" and not is_movie):
attrs = None
tmdb_id = ""
@ -255,8 +254,7 @@ class TMDb:
else:
attrs = data.copy()
limit = int(attrs.pop("limit"))
if is_movie: movie_ids, amount = self._discover(attrs, limit, is_movie)
else: show_ids, amount = self._discover(attrs, limit, is_movie)
ids, amount = self._discover(attrs, limit, is_movie)
if method in ["tmdb_company", "tmdb_network", "tmdb_keyword"]:
logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({amount} {media_type}{'' if amount == 1 else 's'})")
elif method == "tmdb_discover":
@ -264,8 +262,7 @@ class TMDb:
for attr, value in attrs.items():
logger.info(f" {attr}: {value}")
elif method in ["tmdb_popular", "tmdb_top_rated", "tmdb_now_playing", "tmdb_trending_daily", "tmdb_trending_weekly"]:
if is_movie: movie_ids = self._pagenation(method, data, is_movie)
else: show_ids = self._pagenation(method, data, is_movie)
ids = self._pagenation(method, data, is_movie)
logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}")
else:
tmdb_id = int(data)
@ -274,34 +271,31 @@ class TMDb:
tmdb_name = tmdb_list.name
for tmdb_item in tmdb_list.items:
if tmdb_item.media_type == "movie":
movie_ids.append(tmdb_item.id)
ids.append((tmdb_item.id, "tmdb"))
elif tmdb_item.media_type == "tv":
try: show_ids.append(self.config.Convert.tmdb_to_tvdb(tmdb_item.id, fail=True))
except Failed: pass
try:
ids.append((tmdb_item.id, "tmdb_show"))
except Failed:
pass
elif method == "tmdb_movie":
tmdb_name = str(self.get_movie(tmdb_id).title)
movie_ids.append(tmdb_id)
ids.append((tmdb_id, "tmdb"))
elif method == "tmdb_collection":
tmdb_items = self.get_collection(tmdb_id)
tmdb_name = str(tmdb_items.name)
for tmdb_item in tmdb_items.parts:
movie_ids.append(tmdb_item["id"])
ids.append((tmdb_item["id"], "tmdb"))
elif method == "tmdb_show":
tmdb_name = str(self.get_show(tmdb_id).name)
show_ids.append(self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True))
ids.append((tmdb_id, "tmdb_show"))
else:
tmdb_name = str(self.get_person(tmdb_id).name)
if method == "tmdb_actor": movie_ids, show_ids = self._credits(tmdb_id, actor=True)
elif method == "tmdb_director": movie_ids, show_ids = self._credits(tmdb_id, director=True)
elif method == "tmdb_producer": movie_ids, show_ids = self._credits(tmdb_id, producer=True)
elif method == "tmdb_writer": movie_ids, show_ids = self._credits(tmdb_id, writer=True)
elif method == "tmdb_crew": movie_ids, show_ids = self._credits(tmdb_id, crew=True)
if method == "tmdb_actor": ids = self._credits(tmdb_id, actor=True)
elif method == "tmdb_director": ids = self._credits(tmdb_id, director=True)
elif method == "tmdb_producer": ids = self._credits(tmdb_id, producer=True)
elif method == "tmdb_writer": ids = self._credits(tmdb_id, writer=True)
elif method == "tmdb_crew": ids = self._credits(tmdb_id, crew=True)
else: raise Failed(f"TMDb Error: Method {method} not supported")
if len(movie_ids) > 0:
logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({len(movie_ids)} Movie{'' if len(movie_ids) == 1 else 's'})")
if not is_movie and len(show_ids) > 0:
logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({len(show_ids)} Show{'' if len(show_ids) == 1 else 's'})")
logger.debug("")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids
if len(ids) > 0:
logger.info(f"Processing {pretty}: ({tmdb_id}) {tmdb_name} ({len(ids)} Item{'' if len(ids) == 1 else 's'})")
return ids

@ -142,39 +142,59 @@ class Trakt:
except Failed:
raise Failed(f"Trakt Error: List {data} not found")
def _parse(self, items, top=True, is_movie=True):
def _parse(self, items, top=True, item_type=None):
ids = []
for item in items:
data = item["movie" if is_movie else "show"] if top else item
if data["ids"]["tmdb" if is_movie else "tvdb"]:
ids.append(data["ids"]["tmdb" if is_movie else "tvdb"])
if top:
if item_type:
data = item[item_type]
elif item["type"] in ["movie", "show"]:
data = item[item["type"]]
else:
logger.error(f"Trakt Error: No {'TMDb' if is_movie else 'TVDb'} ID found for {data['title']} ({data['year']})")
return (ids, []) if is_movie else ([], ids)
continue
else:
data = item
if item_type:
id_type = "TMDb" if item_type == "movie" else "TVDb"
else:
id_type = "TMDb" if item["type"] == "movie" else "TVDb"
if data["ids"][id_type.lower()]:
ids.append((data["ids"][id_type.lower()], id_type.lower()))
else:
logger.error(f"Trakt Error: No {id_type} ID found for {data['title']} ({data['year']})")
return ids
def _user_list(self, list_type, data, is_movie):
path = f"{requests.utils.urlparse(data).path}/items" if list_type == "list" else f"/users/{data}/{list_type}"
def _user_list(self, data):
try:
items = self._request(f"{path}/{'movies' if is_movie else 'shows'}")
items = self._request(f"{requests.utils.urlparse(data).path}/items")
except Failed:
raise Failed(f"Trakt Error: {'List' if list_type == 'list' else 'User'} {data} not found")
raise Failed(f"Trakt Error: List {data} not found")
if len(items) == 0:
if list_type == "list":
raise Failed(f"Trakt Error: List {data} is empty")
else:
return self._parse(items)
def _user_items(self, list_type, data, is_movie):
try:
items = self._request(f"/users/{data}/{list_type}/{'movies' if is_movie else 'shows'}")
except Failed:
raise Failed(f"Trakt Error: User {data} not found")
if len(items) == 0:
raise Failed(f"Trakt Error: {data}'s {list_type.capitalize()} is empty")
return self._parse(items, is_movie=is_movie)
return self._parse(items, item_type="movie" if is_movie else "show")
def _pagenation(self, pagenation, amount, is_movie):
items = self._request(f"/{'movies' if is_movie else 'shows'}/{pagenation}?limit={amount}")
return self._parse(items, top=pagenation != "popular", is_movie=is_movie)
return self._parse(items, top=pagenation != "popular", item_type="movie" if is_movie else "show")
def validate_trakt(self, trakt_lists, is_movie, trakt_type="list"):
values = util.get_list(trakt_lists, split=False)
trakt_values = []
for value in values:
try:
self._user_list(trakt_type, value, is_movie)
if trakt_type == "list":
self._user_list(value)
else:
self._user_items(trakt_type, value, is_movie)
trakt_values.append(value)
except Failed as e:
logger.error(e)
@ -187,21 +207,17 @@ class Trakt:
raise Failed(f"Trakt Error: No valid Trakt Lists in {values}")
return trakt_values
def get_items(self, method, data, is_movie):
def get_trakt_ids(self, method, data, is_movie):
pretty = method.replace("_", " ").title()
media_type = "Movie" if is_movie else "Show"
if method in ["trakt_trending", "trakt_popular", "trakt_recommended", "trakt_watched", "trakt_collected"]:
logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}")
movie_ids, show_ids = self._pagenation(method[6:], data, is_movie)
return self._pagenation(method[6:], data, is_movie)
elif method in ["trakt_collection", "trakt_watchlist"]:
logger.info(f"Processing {pretty} {media_type}s for {data}")
movie_ids, show_ids = self._user_list(method[6:], data, is_movie)
return self._user_items(method[6:], data, is_movie)
elif method == "trakt_list":
logger.info(f"Processing {pretty}: {data}")
movie_ids, show_ids = self._user_list(method[6:], data, is_movie)
return self._user_list(data)
else:
raise Failed(f"Trakt Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids

@ -54,6 +54,7 @@ class TVDbObj:
self.summary = results[0] if len(results) > 0 and len(results[0]) > 0 else None
tmdb_id = None
imdb_id = None
if self.is_movie:
results = response.xpath("//*[text()='TheMovieDB.com']/@href")
if len(results) > 0:
@ -61,16 +62,16 @@ class TVDbObj:
tmdb_id = util.regex_first_int(results[0], "TMDb ID")
except Failed:
pass
if tmdb_id is None:
results = response.xpath("//*[text()='IMDB']/@href")
if len(results) > 0:
try:
tmdb_id = self.config.Convert.imdb_to_tmdb(util.get_id_from_imdb_url(results[0]), fail=True)
imdb_id = util.get_id_from_imdb_url(results[0])
except Failed:
pass
if tmdb_id is None:
raise Failed(f"TVDB Error: No TMDb ID found for {self.title}")
if tmdb_id is None and imdb_id is None:
raise Failed(f"TVDB Error: No TMDb ID or IMDb ID found for {self.title}")
self.tmdb_id = tmdb_id
self.imdb_id = imdb_id
class TVDb:
def __init__(self, config):
@ -99,8 +100,7 @@ class TVDb:
return description[0] if len(description) > 0 and len(description[0]) > 0 else ""
def _ids_from_url(self, tvdb_url, language):
show_ids = []
movie_ids = []
ids = []
tvdb_url = tvdb_url.strip()
if tvdb_url.startswith((urls["list"], urls["alt_list"])):
try:
@ -111,23 +111,23 @@ class TVDb:
item_url = item.xpath(".//div[@class='col-xs-12 col-sm-9 mt-2']//a/@href")[0]
if item_url.startswith("/series/"):
try:
show_ids.append(self.get_series(language, f"{base_url}{item_url}").id)
ids.append((self.get_series(language, f"{base_url}{item_url}").id, "tvdb"))
except Failed as e:
logger.error(f"{e} for series {title}")
elif item_url.startswith("/movies/"):
try:
tmdb_id = self.get_movie(language, f"{base_url}{item_url}").tmdb_id
if tmdb_id:
movie_ids.append(tmdb_id)
else:
raise Failed(f"TVDb Error: TMDb ID not found from TVDb URL: {tvdb_url}")
movie = self.get_movie(language, f"{base_url}{item_url}")
if movie.tmdb_id:
ids.append((movie.tmdb_id, "tmdb"))
elif movie.imdb_id:
ids.append((movie.imdb_id, "imdb"))
except Failed as e:
logger.error(f"{e} for series {title}")
logger.error(e)
else:
logger.error(f"TVDb Error: Skipping Movie: {title}")
time.sleep(2)
if len(show_ids) > 0 or len(movie_ids) > 0:
return movie_ids, show_ids
if len(ids) > 0:
return ids
raise Failed(f"TVDb Error: No TVDb IDs found at {tvdb_url}")
except requests.exceptions.MissingSchema:
util.print_stacktrace()
@ -135,21 +135,19 @@ class TVDb:
else:
raise Failed(f"TVDb Error: {tvdb_url} must begin with {urls['list']}")
def get_items(self, method, data, language):
show_ids = []
movie_ids = []
def get_tvdb_ids(self, method, data, language):
if method == "tvdb_show":
logger.info(f"Processing TVDb Show: {data}")
show_ids.append(self.get_series(language, data).id)
return [(self.get_series(language, data).id, "tvdb")]
elif method == "tvdb_movie":
logger.info(f"Processing TVDb Movie: {data}")
movie_ids.append(self.get_movie(language, data).tmdb_id)
movie = self.get_movie(language, data)
if movie.tmdb_id:
return [(movie.tmdb_id, "tmdb")]
elif movie.imdb_id:
return [(movie.imdb_id, "imdb")]
elif method == "tvdb_list":
logger.info(f"Processing TVDb List: {data}")
movie_ids, show_ids = self._ids_from_url(data, language)
return self._ids_from_url(data, language)
else:
raise Failed(f"TVDb Error: Method {method} not supported")
logger.debug("")
logger.debug(f"{len(movie_ids)} TMDb IDs Found: {movie_ids}")
logger.debug(f"{len(show_ids)} TVDb IDs Found: {show_ids}")
return movie_ids, show_ids

@ -246,39 +246,46 @@ def is_locked(filepath):
file_object.close()
return locked
def date_filter(current, modifier, data, final, current_time):
if current is None:
return False
def is_date_filter(value, modifier, data, final, current_time):
if value is None:
return True
if modifier in ["", ".not"]:
threshold_date = current_time - timedelta(days=data)
if (modifier == "" and (current is None or current < threshold_date)) \
or (modifier == ".not" and current and current >= threshold_date):
return False
if (modifier == "" and (value is None or value < threshold_date)) \
or (modifier == ".not" and value and value >= threshold_date):
return True
elif modifier in [".before", ".after"]:
filter_date = validate_date(data, final)
if (modifier == ".before" and current >= filter_date) or (modifier == ".after" and current <= filter_date):
return False
if (modifier == ".before" and value >= filter_date) or (modifier == ".after" and value <= filter_date):
return True
elif modifier == ".regex":
jailbreak = False
jailbreak = True
for check_data in data:
if re.compile(check_data).match(current.strftime("%m/%d/%Y")):
if re.compile(check_data).match(value.strftime("%m/%d/%Y")):
jailbreak = True
break
if not jailbreak:
return False
return True
return False
def is_number_filter(value, modifier, data):
return value is None or (modifier == ".gt" and value <= data) \
or (modifier == ".gte" and value < data) \
or (modifier == ".lt" and value >= data) \
or (modifier == ".lte" and value > data)
def number_filter(current, modifier, data):
return current is None or (modifier == ".gt" and current <= data) \
or (modifier == ".gte" and current < data) \
or (modifier == ".lt" and current >= data) \
or (modifier == ".lte" and current > data)
def string_filter(current, modifier, data):
return (modifier in ["", ".not"] and data.lower() in current.lower()) \
or (modifier == ".begins" and current.lower().startswith(data.lower())) \
or (modifier == ".ends" and current.lower().endswith(data.lower())) \
or (modifier == ".regex" and re.compile(data).match(current))
def is_string_filter(values, modifier, data):
jailbreak = False
for value in values:
for check_value in data:
if (modifier in ["", ".not"] and check_value.lower() in value.lower()) \
or (modifier == ".begins" and value.lower().startswith(check_value.lower())) \
or (modifier == ".ends" and value.lower().endswith(check_value.lower())) \
or (modifier == ".regex" and re.compile(check_value).match(value)):
jailbreak = True
break
if jailbreak: break
return (jailbreak and modifier == ".not") or (not jailbreak and modifier in ["", ".begins", ".ends", ".regex"])
def parse(attribute, data, datatype=None, methods=None, parent=None, default=None, options=None, translation=None, minimum=1, maximum=None):
display = f"{parent + ' ' if parent else ''}{attribute} attribute"

@ -265,12 +265,14 @@ def mass_metadata(config, library, items):
tvdb_id = None
imdb_id = None
if config.Cache:
t_id, guid_media_type, _ = config.Cache.query_guid_map(item.guid)
t_id, i_id, guid_media_type, _ = config.Cache.query_guid_map(item.guid)
if t_id:
if "movie" in guid_media_type:
tmdb_id = t_id[0]
else:
tvdb_id = t_id[0]
if i_id:
imdb_id = i_id[0]
if not tmdb_id and not tvdb_id:
tmdb_id = library.get_tmdb_from_map(item)
if not tmdb_id and not tvdb_id and library.is_show:
@ -469,7 +471,7 @@ def run_collection(config, library, metadata, requested_collections):
for filter_key, filter_value in builder.filters:
logger.info(f"Collection Filter {filter_key}: {filter_value}")
builder.collect_rating_keys()
builder.find_rating_keys()
if len(builder.rating_keys) > 0 and builder.build_collection:
logger.info("")

Loading…
Cancel
Save