mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
reorganize methods
This commit is contained in:
parent
f8e10315d5
commit
6cf158fc10
5 changed files with 393 additions and 402 deletions
|
@ -3,6 +3,7 @@ from datetime import datetime, timedelta
|
|||
from modules import anidb, anilist, imdb, letterboxd, mal, plex, radarr, sonarr, tautulli, tmdb, trakttv, tvdb, util
|
||||
from modules.util import Failed
|
||||
from plexapi.exceptions import BadRequest, NotFound
|
||||
from plexapi.video import Movie, Show
|
||||
from urllib.parse import quote
|
||||
|
||||
logger = logging.getLogger("Plex Meta Manager")
|
||||
|
@ -26,6 +27,20 @@ method_alias = {
|
|||
"writers": "writer",
|
||||
"years": "year"
|
||||
}
|
||||
filter_alias = {
|
||||
"actor": "actors",
|
||||
"audience_rating": "audienceRating",
|
||||
"collection": "collections",
|
||||
"content_rating": "contentRating",
|
||||
"country": "countries",
|
||||
"critic_rating": "rating",
|
||||
"director": "directors",
|
||||
"genre": "genres",
|
||||
"originally_available": "originallyAvailableAt",
|
||||
"tmdb_vote_count": "vote_count",
|
||||
"user_rating": "userRating",
|
||||
"writer": "writers"
|
||||
}
|
||||
modifier_alias = {".greater": ".gt", ".less": ".lt"}
|
||||
all_builders = anidb.builders + anilist.builders + imdb.builders + letterboxd.builders + mal.builders + plex.builders + tautulli.builders + tmdb.builders + trakttv.builders + tvdb.builders
|
||||
dictionary_builders = [
|
||||
|
@ -191,6 +206,9 @@ class CollectionBuilder:
|
|||
self.missing_shows = []
|
||||
self.methods = []
|
||||
self.filters = []
|
||||
self.rating_keys = []
|
||||
self.missing_movies = []
|
||||
self.missing_shows = []
|
||||
self.posters = {}
|
||||
self.backgrounds = {}
|
||||
self.summaries = {}
|
||||
|
@ -1207,16 +1225,32 @@ class CollectionBuilder:
|
|||
self.details["collection_mode"] = "hide"
|
||||
self.sync = True
|
||||
|
||||
def run_methods(self, collection_obj, collection_name, rating_key_map, movie_map, show_map):
|
||||
items_found = 0
|
||||
try:
|
||||
self.obj = library.get_collection(self.name)
|
||||
collection_smart = library.smart(self.obj)
|
||||
if (self.smart and not collection_smart) or (not self.smart and collection_smart):
|
||||
logger.info("")
|
||||
logger.error(f"Collection Error: Converting {self.obj.title} to a {'smart' if self.smart else 'normal'} collection")
|
||||
library.query(self.obj.delete)
|
||||
self.obj = None
|
||||
except Failed:
|
||||
self.obj = None
|
||||
|
||||
self.plex_map = {}
|
||||
if self.sync and self.obj:
|
||||
for item in library.get_collection_items(self.obj, self.smart_label_collection):
|
||||
self.plex_map[item.ratingKey] = item
|
||||
|
||||
def collect_rating_keys(self, movie_map, show_map):
|
||||
def add_rating_keys(keys):
|
||||
if not isinstance(keys, list):
|
||||
keys = [keys]
|
||||
self.rating_keys.extend([key for key in keys if key not in self.rating_keys])
|
||||
for method, values in self.methods:
|
||||
logger.debug("")
|
||||
logger.debug(f"Method: {method}")
|
||||
logger.debug(f"Values: {values}")
|
||||
for value in values:
|
||||
items = []
|
||||
missing_movies = []
|
||||
missing_shows = []
|
||||
def check_map(input_ids):
|
||||
movie_ids, show_ids = input_ids
|
||||
items_found_inside = 0
|
||||
|
@ -1224,134 +1258,252 @@ class CollectionBuilder:
|
|||
items_found_inside += len(movie_ids)
|
||||
for movie_id in movie_ids:
|
||||
if movie_id in movie_map:
|
||||
items.extend(movie_map[movie_id])
|
||||
else:
|
||||
missing_movies.append(movie_id)
|
||||
add_rating_keys(movie_map[movie_id])
|
||||
elif movie_id not in self.missing_movies:
|
||||
self.missing_movies.append(movie_id)
|
||||
if len(show_ids) > 0:
|
||||
items_found_inside += len(show_ids)
|
||||
for show_id in show_ids:
|
||||
if show_id in show_map:
|
||||
items.extend(show_map[show_id])
|
||||
else:
|
||||
missing_shows.append(show_id)
|
||||
add_rating_keys(show_map[show_id])
|
||||
elif show_id not in self.missing_shows:
|
||||
self.missing_shows.append(show_id)
|
||||
return items_found_inside
|
||||
logger.debug("")
|
||||
logger.debug(f"Value: {value}")
|
||||
logger.info("")
|
||||
if "plex" in method:
|
||||
items = self.library.get_items(method, value)
|
||||
items_found += len(items)
|
||||
elif "tautulli" in method:
|
||||
items = self.library.Tautulli.get_items(self.library, time_range=value["list_days"], stats_count=value["list_size"], list_type=value["list_type"], stats_count_buffer=value["list_buffer"])
|
||||
items_found += len(items)
|
||||
elif "anidb" in method: items_found += check_map(self.config.AniDB.get_items(method, value, self.library.Plex.language))
|
||||
elif "anilist" in method: items_found += check_map(self.config.AniList.get_items(method, value))
|
||||
elif "mal" in method: items_found += check_map(self.config.MyAnimeList.get_items(method, value))
|
||||
elif "tvdb" in method: items_found += check_map(self.config.TVDb.get_items(method, value, self.library.Plex.language))
|
||||
elif "imdb" in method: items_found += check_map(self.config.IMDb.get_items(method, value, self.library.Plex.language))
|
||||
elif "letterboxd" in method: items_found += check_map(self.config.Letterboxd.get_items(method, value, self.library.Plex.language))
|
||||
elif "tmdb" in method: items_found += check_map(self.config.TMDb.get_items(method, value, self.library.is_movie))
|
||||
elif "trakt" in method: items_found += check_map(self.config.Trakt.get_items(method, value, self.library.is_movie))
|
||||
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.library.Plex.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.library.Plex.language))
|
||||
elif "imdb" in method: check_map(self.config.IMDb.get_items(method, value, self.library.Plex.language))
|
||||
elif "letterboxd" in method: check_map(self.config.Letterboxd.get_items(method, value, self.library.Plex.language))
|
||||
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")
|
||||
|
||||
logger.info("")
|
||||
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"], self.smart_label_collection, rating_key_map, movie_map, show_map)
|
||||
else:
|
||||
logger.error("No items found to add to this collection ")
|
||||
|
||||
if len(missing_movies) > 0 or len(missing_shows) > 0:
|
||||
logger.info("")
|
||||
arr_filters = []
|
||||
for filter_method, filter_data in self.filters:
|
||||
if (filter_method.startswith("original_language") and self.library.is_movie) or filter_method.startswith("tmdb_vote_count"):
|
||||
arr_filters.append((filter_method, filter_data))
|
||||
if len(missing_movies) > 0:
|
||||
missing_movies_with_names = []
|
||||
for missing_id in missing_movies:
|
||||
try:
|
||||
movie = self.config.TMDb.get_movie(missing_id)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
continue
|
||||
match = True
|
||||
for filter_method, filter_data in arr_filters:
|
||||
if (filter_method == "original_language" and movie.original_language not in filter_data) \
|
||||
or (filter_method == "original_language.not" and movie.original_language in filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.gte" and movie.vote_count < filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.lte" and movie.vote_count > filter_data):
|
||||
match = False
|
||||
def add_to_collection(self, movie_map, show_map):
|
||||
name, collection_items = self.library.get_collection_name_and_items(self.obj if self.obj else self.name, self.smart_label_collection)
|
||||
total = len(self.rating_keys)
|
||||
max_length = len(str(total))
|
||||
length = 0
|
||||
for i, item in enumerate(self.rating_keys, 1):
|
||||
try:
|
||||
current = self.library.fetchItem(item.ratingKey if isinstance(item, (Movie, Show)) else int(item))
|
||||
if not isinstance(current, (Movie, Show)):
|
||||
raise NotFound
|
||||
except (BadRequest, NotFound):
|
||||
logger.error(f"Plex Error: Item {item} not found")
|
||||
continue
|
||||
match = True
|
||||
if self.filters:
|
||||
length = util.print_return(length, f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}")
|
||||
for filter_method, filter_data in self.filters:
|
||||
modifier = filter_method[-4:]
|
||||
method = filter_method[:-4] if modifier in [".not", ".lte", ".gte"] else filter_method
|
||||
method_name = filter_alias[method] if method in filter_alias else method
|
||||
if method_name == "max_age":
|
||||
threshold_date = datetime.now() - timedelta(days=filter_data)
|
||||
if current.originallyAvailableAt is None or current.originallyAvailableAt < threshold_date:
|
||||
match = False
|
||||
break
|
||||
elif method_name == "original_language":
|
||||
movie = None
|
||||
for key, value in movie_map.items():
|
||||
if current.ratingKey in value:
|
||||
try:
|
||||
movie = self.config.TMDb.get_movie(key)
|
||||
break
|
||||
if match:
|
||||
missing_movies_with_names.append((movie.title, missing_id))
|
||||
if self.details["show_missing"] is True:
|
||||
logger.info(f"{collection_name} Collection | ? | {movie.title} (TMDb: {missing_id})")
|
||||
elif self.details["show_filtered"] is True:
|
||||
logger.info(f"{collection_name} Collection | X | {movie.title} (TMDb: {missing_id})")
|
||||
logger.info(f"{len(missing_movies_with_names)} Movie{'s' if len(missing_movies_with_names) > 1 else ''} Missing")
|
||||
if self.details["save_missing"] is True:
|
||||
self.library.add_missing(collection_name, missing_movies_with_names, True)
|
||||
if (self.add_to_radarr and self.library.Radarr) or self.run_again:
|
||||
missing_tmdb_ids = [missing_id for title, missing_id in missing_movies_with_names]
|
||||
if self.add_to_radarr and self.library.Radarr:
|
||||
try:
|
||||
self.library.Radarr.add_tmdb(missing_tmdb_ids, **self.radarr_options)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
if self.run_again:
|
||||
self.missing_movies.extend(missing_tmdb_ids)
|
||||
if len(missing_shows) > 0 and self.library.is_show:
|
||||
missing_shows_with_names = []
|
||||
for missing_id in missing_shows:
|
||||
try:
|
||||
title = str(self.config.TVDb.get_series(self.library.Plex.language, missing_id).title.encode("ascii", "replace").decode())
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
continue
|
||||
match = True
|
||||
if arr_filters:
|
||||
show = self.config.TMDb.get_show(self.config.Convert.tvdb_to_tmdb(missing_id))
|
||||
for filter_method, filter_data in arr_filters:
|
||||
if (filter_method == "tmdb_vote_count.gte" and show.vote_count < filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.lte" and show.vote_count > filter_data):
|
||||
match = False
|
||||
except Failed:
|
||||
pass
|
||||
if movie is None:
|
||||
logger.warning(f"Filter Error: No TMDb ID found for {current.title}")
|
||||
continue
|
||||
if (modifier == ".not" and movie.original_language in filter_data) or (
|
||||
modifier != ".not" and movie.original_language not in filter_data):
|
||||
match = False
|
||||
break
|
||||
elif method_name == "audio_track_title":
|
||||
jailbreak = False
|
||||
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 check_title.lower() in title.lower():
|
||||
jailbreak = True
|
||||
break
|
||||
if jailbreak: break
|
||||
if jailbreak: break
|
||||
if jailbreak: break
|
||||
if (jailbreak and modifier == ".not") or (not jailbreak and modifier != ".not"):
|
||||
match = False
|
||||
break
|
||||
elif method_name == "filepath":
|
||||
jailbreak = False
|
||||
for location in current.locations:
|
||||
for check_text in filter_data:
|
||||
if check_text.lower() in location.lower():
|
||||
jailbreak = True
|
||||
break
|
||||
if jailbreak: break
|
||||
if (jailbreak and modifier == ".not") or (not jailbreak and modifier != ".not"):
|
||||
match = False
|
||||
break
|
||||
elif modifier in [".gte", ".lte"]:
|
||||
if method_name == "vote_count":
|
||||
tmdb_item = None
|
||||
for key, value in movie_map.items():
|
||||
if current.ratingKey in value:
|
||||
try:
|
||||
tmdb_item = self.config.TMDb.get_movie(key) if self.library.is_movie else self.config.TMDb.get_show(key)
|
||||
break
|
||||
if match:
|
||||
missing_shows_with_names.append((title, missing_id))
|
||||
if self.details["show_missing"] is True:
|
||||
logger.info(f"{collection_name} Collection | ? | {title} (TVDB: {missing_id})")
|
||||
elif self.details["show_filtered"] is True:
|
||||
logger.info(f"{collection_name} Collection | X | {title} (TVDb: {missing_id})")
|
||||
logger.info(f"{len(missing_shows_with_names)} Show{'s' if len(missing_shows_with_names) > 1 else ''} Missing")
|
||||
if self.details["save_missing"] is True:
|
||||
self.library.add_missing(collection_name, missing_shows_with_names, False)
|
||||
if (self.add_to_sonarr and self.library.Sonarr) or self.run_again:
|
||||
missing_tvdb_ids = [missing_id for title, missing_id in missing_shows_with_names]
|
||||
if self.add_to_sonarr and self.library.Sonarr:
|
||||
try:
|
||||
self.library.Sonarr.add_tvdb(missing_tvdb_ids, **self.sonarr_options)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
if self.run_again:
|
||||
self.missing_shows.extend(missing_tvdb_ids)
|
||||
|
||||
if self.sync and items_found > 0:
|
||||
logger.info("")
|
||||
count_removed = 0
|
||||
for ratingKey, item in rating_key_map.items():
|
||||
if item is not None:
|
||||
logger.info(f"{collection_name} Collection | - | {item.title}")
|
||||
if self.smart_label_collection:
|
||||
self.library.query_data(item.removeLabel, collection_name)
|
||||
except Failed:
|
||||
pass
|
||||
if tmdb_item is None:
|
||||
logger.warning(f"Filter Error: No TMDb ID found for {current.title}")
|
||||
continue
|
||||
attr = tmdb_item.vote_count
|
||||
else:
|
||||
attr = getattr(current, method_name) / 60000 if method_name == "duration" else getattr(current, method_name)
|
||||
if attr is None or (modifier == ".lte" and attr > filter_data) or (modifier == ".gte" and attr < filter_data):
|
||||
match = False
|
||||
break
|
||||
else:
|
||||
self.library.query_data(item.removeCollection, collection_name)
|
||||
count_removed += 1
|
||||
logger.info(f"{count_removed} {'Movie' if self.library.is_movie else 'Show'}{'s' if count_removed == 1 else ''} Removed")
|
||||
logger.info("")
|
||||
attrs = []
|
||||
if method_name in ["video_resolution", "audio_language", "subtitle_language"]:
|
||||
for media in current.media:
|
||||
if method_name == "video_resolution":
|
||||
attrs.extend([media.videoResolution])
|
||||
for part in media.parts:
|
||||
if method_name == "audio_language":
|
||||
attrs.extend([a.language for a in part.audioStreams()])
|
||||
if method_name == "subtitle_language":
|
||||
attrs.extend([s.language for s in part.subtitleStreams()])
|
||||
elif method_name in ["contentRating", "studio", "year", "rating", "originallyAvailableAt"]:
|
||||
attrs = [str(getattr(current, method_name))]
|
||||
elif method_name in ["actors", "countries", "directors", "genres", "writers", "collections"]:
|
||||
attrs = [getattr(x, "tag") for x in getattr(current, method_name)]
|
||||
else:
|
||||
raise Failed(f"Filter Error: filter: {method_name} not supported")
|
||||
|
||||
def update_details(self, collection):
|
||||
if self.smart_url and self.smart_url != self.library.smart_filter(collection):
|
||||
self.library.update_smart_collection(collection, self.smart_url)
|
||||
if (not list(set(filter_data) & set(attrs)) and modifier != ".not")\
|
||||
or (list(set(filter_data) & set(attrs)) and modifier == ".not"):
|
||||
match = False
|
||||
break
|
||||
length = util.print_return(length, f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}")
|
||||
if match:
|
||||
util.print_end(length, f"{name} Collection | {'=' if current in collection_items else '+'} | {current.title}")
|
||||
if current in collection_items:
|
||||
self.plex_map[current.ratingKey] = None
|
||||
elif self.smart_label_collection:
|
||||
self.library.query_data(current.addLabel, name)
|
||||
else:
|
||||
self.library.query_data(current.addCollection, name)
|
||||
elif self.details["show_filtered"] is True:
|
||||
logger.info(f"{name} Collection | X | {current.title}")
|
||||
media_type = f"{'Movie' if self.library.is_movie else 'Show'}{'s' if total > 1 else ''}"
|
||||
util.print_end(length, f"{total} {media_type} Processed")
|
||||
|
||||
def run_missing(self, missing_movies, missing_shows):
|
||||
logger.info("")
|
||||
arr_filters = []
|
||||
for filter_method, filter_data in self.filters:
|
||||
if (filter_method.startswith("original_language") and self.library.is_movie) or filter_method.startswith("tmdb_vote_count"):
|
||||
arr_filters.append((filter_method, filter_data))
|
||||
if len(missing_movies) > 0:
|
||||
missing_movies_with_names = []
|
||||
for missing_id in missing_movies:
|
||||
try:
|
||||
movie = self.config.TMDb.get_movie(missing_id)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
continue
|
||||
match = True
|
||||
for filter_method, filter_data in arr_filters:
|
||||
if (filter_method == "original_language" and movie.original_language not in filter_data) \
|
||||
or (filter_method == "original_language.not" and movie.original_language in filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.gte" and movie.vote_count < filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.lte" and movie.vote_count > filter_data):
|
||||
match = False
|
||||
break
|
||||
if match:
|
||||
missing_movies_with_names.append((movie.title, missing_id))
|
||||
if self.details["show_missing"] is True:
|
||||
logger.info(f"{self.name} Collection | ? | {movie.title} (TMDb: {missing_id})")
|
||||
elif self.details["show_filtered"] is True:
|
||||
logger.info(f"{self.name} Collection | X | {movie.title} (TMDb: {missing_id})")
|
||||
logger.info(f"{len(missing_movies_with_names)} Movie{'s' if len(missing_movies_with_names) > 1 else ''} Missing")
|
||||
if self.details["save_missing"] is True:
|
||||
self.library.add_missing(self.name, missing_movies_with_names, True)
|
||||
if (self.add_to_radarr and self.library.Radarr) or self.run_again:
|
||||
missing_tmdb_ids = [missing_id for title, missing_id in missing_movies_with_names]
|
||||
if self.add_to_radarr and self.library.Radarr:
|
||||
try:
|
||||
self.library.Radarr.add_tmdb(missing_tmdb_ids, **self.radarr_options)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
if self.run_again:
|
||||
self.missing_movies.extend(missing_tmdb_ids)
|
||||
if len(missing_shows) > 0 and self.library.is_show:
|
||||
missing_shows_with_names = []
|
||||
for missing_id in missing_shows:
|
||||
try:
|
||||
title = str(self.config.TVDb.get_series(self.library.Plex.language, missing_id).title.encode("ascii", "replace").decode())
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
continue
|
||||
match = True
|
||||
if arr_filters:
|
||||
show = self.config.TMDb.get_show(self.config.Convert.tvdb_to_tmdb(missing_id))
|
||||
for filter_method, filter_data in arr_filters:
|
||||
if (filter_method == "tmdb_vote_count.gte" and show.vote_count < filter_data) \
|
||||
or (filter_method == "tmdb_vote_count.lte" and show.vote_count > filter_data):
|
||||
match = False
|
||||
break
|
||||
if match:
|
||||
missing_shows_with_names.append((title, missing_id))
|
||||
if self.details["show_missing"] is True:
|
||||
logger.info(f"{self.name} Collection | ? | {title} (TVDB: {missing_id})")
|
||||
elif self.details["show_filtered"] is True:
|
||||
logger.info(f"{self.name} Collection | X | {title} (TVDb: {missing_id})")
|
||||
logger.info(f"{len(missing_shows_with_names)} Show{'s' if len(missing_shows_with_names) > 1 else ''} Missing")
|
||||
if self.details["save_missing"] is True:
|
||||
self.library.add_missing(self.name, missing_shows_with_names, False)
|
||||
if (self.add_to_sonarr and self.library.Sonarr) or self.run_again:
|
||||
missing_tvdb_ids = [missing_id for title, missing_id in missing_shows_with_names]
|
||||
if self.add_to_sonarr and self.library.Sonarr:
|
||||
try:
|
||||
self.library.Sonarr.add_tvdb(missing_tvdb_ids, **self.sonarr_options)
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
if self.run_again:
|
||||
self.missing_shows.extend(missing_tvdb_ids)
|
||||
|
||||
def sync_collection(self):
|
||||
logger.info("")
|
||||
count_removed = 0
|
||||
for ratingKey, item in self.rating_key_map.items():
|
||||
if item is not None:
|
||||
logger.info(f"{self.name} Collection | - | {item.title}")
|
||||
if self.smart_label_collection:
|
||||
self.library.query_data(item.removeLabel, self.name)
|
||||
else:
|
||||
self.library.query_data(item.removeCollection, self.name)
|
||||
count_removed += 1
|
||||
logger.info(f"{count_removed} {'Movie' if self.library.is_movie else 'Show'}{'s' if count_removed == 1 else ''} Removed")
|
||||
|
||||
def update_details(self):
|
||||
if not self.obj and self.smart_url:
|
||||
self.library.create_smart_collection(self.name, self.smart_type_key, self.smart_url)
|
||||
elif not self.obj and self.smart_label_collection:
|
||||
self.library.create_smart_labels(self.name, sort=self.smart_sort)
|
||||
self.obj = self.library.get_collection(self.name)
|
||||
|
||||
if self.smart_url and self.smart_url != self.library.smart_filter(self.obj):
|
||||
self.library.update_smart_collection(self.obj, self.smart_url)
|
||||
logger.info(f"Detail: Smart Filter updated to {self.smart_url}")
|
||||
|
||||
edits = {}
|
||||
|
@ -1379,50 +1531,50 @@ class CollectionBuilder:
|
|||
elif "tmdb_show_details" in self.summaries: summary = get_summary("tmdb_show_details", self.summaries)
|
||||
else: summary = None
|
||||
if summary:
|
||||
if str(summary) != str(collection.summary):
|
||||
if str(summary) != str(self.obj.summary):
|
||||
edits["summary.value"] = summary
|
||||
edits["summary.locked"] = 1
|
||||
|
||||
if "sort_title" in self.details:
|
||||
if str(self.details["sort_title"]) != str(collection.titleSort):
|
||||
if str(self.details["sort_title"]) != str(self.obj.titleSort):
|
||||
edits["titleSort.value"] = self.details["sort_title"]
|
||||
edits["titleSort.locked"] = 1
|
||||
logger.info(f"Detail: sort_title updated Collection Sort Title to {self.details['sort_title']}")
|
||||
|
||||
if "content_rating" in self.details:
|
||||
if str(self.details["content_rating"]) != str(collection.contentRating):
|
||||
if str(self.details["content_rating"]) != str(self.obj.contentRating):
|
||||
edits["contentRating.value"] = self.details["content_rating"]
|
||||
edits["contentRating.locked"] = 1
|
||||
logger.info(f"Detail: content_rating updated Collection Content Rating to {self.details['content_rating']}")
|
||||
|
||||
if "collection_mode" in self.details:
|
||||
if int(collection.collectionMode) not in plex.collection_mode_keys\
|
||||
or plex.collection_mode_keys[int(collection.collectionMode)] != self.details["collection_mode"]:
|
||||
self.library.collection_mode_query(collection, self.details["collection_mode"])
|
||||
if int(self.obj.collectionMode) not in plex.collection_mode_keys\
|
||||
or plex.collection_mode_keys[int(self.obj.collectionMode)] != self.details["collection_mode"]:
|
||||
self.library.collection_mode_query(self.obj, self.details["collection_mode"])
|
||||
logger.info(f"Detail: collection_mode updated Collection Mode to {self.details['collection_mode']}")
|
||||
|
||||
if "collection_order" in self.details:
|
||||
if int(collection.collectionSort) not in plex.collection_order_keys\
|
||||
or plex.collection_order_keys[int(collection.collectionSort)] != self.details["collection_order"]:
|
||||
self.library.collection_order_query(collection, self.details["collection_order"])
|
||||
if int(self.obj.collectionSort) not in plex.collection_order_keys\
|
||||
or plex.collection_order_keys[int(self.obj.collectionSort)] != self.details["collection_order"]:
|
||||
self.library.collection_order_query(self.obj, self.details["collection_order"])
|
||||
logger.info(f"Detail: collection_order updated Collection Order to {self.details['collection_order']}")
|
||||
|
||||
if "label" in self.details or "label.sync" in self.details:
|
||||
item_labels = [label.tag for label in collection.labels]
|
||||
item_labels = [label.tag for label in self.obj.labels]
|
||||
labels = util.get_list(self.details["label" if "label" in self.details else "label.sync"])
|
||||
if "label.sync" in self.details:
|
||||
for label in (la for la in item_labels if la not in labels):
|
||||
self.library.query_data(collection.removeLabel, label)
|
||||
self.library.query_data(self.obj.removeLabel, label)
|
||||
logger.info(f"Detail: Label {label} removed")
|
||||
for label in (la for la in labels if la not in item_labels):
|
||||
self.library.query_data(collection.addLabel, label)
|
||||
self.library.query_data(self.obj.addLabel, label)
|
||||
logger.info(f"Detail: Label {label} added")
|
||||
|
||||
if len(self.item_details) > 0:
|
||||
labels = None
|
||||
if "item_label" in self.item_details or "item_label.sync" in self.item_details:
|
||||
labels = util.get_list(self.item_details["item_label" if "item_label" in self.item_details else "item_label.sync"])
|
||||
for item in self.library.get_collection_items(collection, self.smart_label_collection):
|
||||
for item in self.library.get_collection_items(self.obj, self.smart_label_collection):
|
||||
if labels is not None:
|
||||
item_labels = [label.tag for label in item.labels]
|
||||
if "item_label.sync" in self.item_details:
|
||||
|
@ -1442,7 +1594,7 @@ class CollectionBuilder:
|
|||
|
||||
if len(edits) > 0:
|
||||
logger.debug(edits)
|
||||
self.library.edit_query(collection, edits)
|
||||
self.library.edit_query(self.obj, edits)
|
||||
logger.info("Details: have been updated")
|
||||
|
||||
if self.library.asset_directory:
|
||||
|
@ -1466,13 +1618,13 @@ class CollectionBuilder:
|
|||
matches = glob.glob(background_filter)
|
||||
if len(matches) > 0:
|
||||
self.backgrounds["asset_directory"] = os.path.abspath(matches[0])
|
||||
for item in self.library.query(collection.items):
|
||||
for item in self.library.query(self.obj.items):
|
||||
self.library.update_item_from_assets(item, dirs=[path])
|
||||
|
||||
def set_image(image_method, images, is_background=False):
|
||||
message = f"{'background' if is_background else 'poster'} to [{'File' if image_method in image_file_details else 'URL'}] {images[image_method]}"
|
||||
try:
|
||||
self.library.upload_image(collection, images[image_method], poster=not is_background, url=image_method not in image_file_details)
|
||||
self.library.upload_image(self.obj, images[image_method], poster=not is_background, url=image_method not in image_file_details)
|
||||
logger.info(f"Detail: {image_method} updated collection {message}")
|
||||
except BadRequest:
|
||||
logger.error(f"Detail: {image_method} failed to update {message}")
|
||||
|
@ -1524,8 +1676,9 @@ class CollectionBuilder:
|
|||
elif "tmdb_show_details" in self.backgrounds: set_image("tmdb_show_details", self.backgrounds, is_background=True)
|
||||
else: logger.info("No background to update")
|
||||
|
||||
def run_collections_again(self, collection_obj, movie_map, show_map):
|
||||
name, collection_items = self.library.get_collection_name_and_items(collection_obj, self.smart_label_collection)
|
||||
def run_collections_again(self, movie_map, show_map):
|
||||
self.obj = self.library.get_collection(self.name)
|
||||
name, collection_items = self.library.get_collection_name_and_items(self.obj, self.smart_label_collection)
|
||||
rating_keys = []
|
||||
for mm in self.missing_movies:
|
||||
if mm in movie_map:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import logging, os, random, sqlite3
|
||||
from contextlib import closing
|
||||
from datetime import datetime, timedelta
|
||||
from modules.util import Failed
|
||||
|
||||
logger = logging.getLogger("Plex Meta Manager")
|
||||
|
||||
|
|
142
modules/plex.py
142
modules/plex.py
|
@ -7,7 +7,6 @@ from plexapi import utils
|
|||
from plexapi.exceptions import BadRequest, NotFound, Unauthorized
|
||||
from plexapi.collection import Collections
|
||||
from plexapi.server import PlexServer
|
||||
from plexapi.video import Movie, Show
|
||||
from retrying import retry
|
||||
from ruamel import yaml
|
||||
from urllib import parse
|
||||
|
@ -77,20 +76,6 @@ item_advance_keys = {
|
|||
"item_use_original_title": ("useOriginalTitle", use_original_title_options)
|
||||
}
|
||||
new_plex_agents = ["tv.plex.agents.movie", "tv.plex.agents.series"]
|
||||
filter_alias = {
|
||||
"actor": "actors",
|
||||
"audience_rating": "audienceRating",
|
||||
"collection": "collections",
|
||||
"content_rating": "contentRating",
|
||||
"country": "countries",
|
||||
"critic_rating": "rating",
|
||||
"director": "directors",
|
||||
"genre": "genres",
|
||||
"originally_available": "originallyAvailableAt",
|
||||
"tmdb_vote_count": "vote_count",
|
||||
"user_rating": "userRating",
|
||||
"writer": "writers"
|
||||
}
|
||||
searches = [
|
||||
"title", "title.and", "title.not", "title.begins", "title.ends",
|
||||
"studio", "studio.and", "studio.not", "studio.begins", "studio.ends",
|
||||
|
@ -367,8 +352,12 @@ class PlexAPI:
|
|||
return self.Plex.search(title=title, sort=sort, maxresults=maxresults, libtype=libtype, **kwargs)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
def exact_search(self, title, libtype=None):
|
||||
return self.Plex.search(libtype=libtype, **{"title=": title})
|
||||
def exact_search(self, title, libtype=None, year=None):
|
||||
if year:
|
||||
terms = {"title=": title, "year": year}
|
||||
else:
|
||||
terms = {"title=": title}
|
||||
return self.Plex.search(libtype=libtype, **terms)
|
||||
|
||||
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
|
||||
def get_labeled_items(self, label):
|
||||
|
@ -652,125 +641,6 @@ class PlexAPI:
|
|||
name = collection.title if isinstance(collection, Collections) else str(collection)
|
||||
return name, self.get_collection_items(collection, smart_label_collection)
|
||||
|
||||
def add_to_collection(self, collection, items, filters, show_filtered, smart, rating_key_map, movie_map, show_map):
|
||||
name, collection_items = self.get_collection_name_and_items(collection, smart)
|
||||
total = len(items)
|
||||
max_length = len(str(total))
|
||||
length = 0
|
||||
for i, item in enumerate(items, 1):
|
||||
try:
|
||||
current = self.fetchItem(item.ratingKey if isinstance(item, (Movie, Show)) else int(item))
|
||||
if not isinstance(current, (Movie, Show)):
|
||||
raise NotFound
|
||||
except (BadRequest, NotFound):
|
||||
logger.error(f"Plex Error: Item {item} not found")
|
||||
continue
|
||||
match = True
|
||||
if filters:
|
||||
length = util.print_return(length, f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}")
|
||||
for filter_method, filter_data in filters:
|
||||
modifier = filter_method[-4:]
|
||||
method = filter_method[:-4] if modifier in [".not", ".lte", ".gte"] else filter_method
|
||||
method_name = filter_alias[method] if method in filter_alias else method
|
||||
if method_name == "max_age":
|
||||
threshold_date = datetime.now() - timedelta(days=filter_data)
|
||||
if current.originallyAvailableAt is None or current.originallyAvailableAt < threshold_date:
|
||||
match = False
|
||||
break
|
||||
elif method_name == "original_language":
|
||||
movie = None
|
||||
for key, value in movie_map.items():
|
||||
if current.ratingKey in value:
|
||||
try:
|
||||
movie = self.TMDb.get_movie(key)
|
||||
break
|
||||
except Failed:
|
||||
pass
|
||||
if movie is None:
|
||||
logger.warning(f"Filter Error: No TMDb ID found for {current.title}")
|
||||
continue
|
||||
if (modifier == ".not" and movie.original_language in filter_data) or (modifier != ".not" and movie.original_language not in filter_data):
|
||||
match = False
|
||||
break
|
||||
elif method_name == "audio_track_title":
|
||||
jailbreak = False
|
||||
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 check_title.lower() in title.lower():
|
||||
jailbreak = True
|
||||
break
|
||||
if jailbreak: break
|
||||
if jailbreak: break
|
||||
if jailbreak: break
|
||||
if (jailbreak and modifier == ".not") or (not jailbreak and modifier != ".not"):
|
||||
match = False
|
||||
break
|
||||
elif method_name == "filepath":
|
||||
jailbreak = False
|
||||
for location in current.locations:
|
||||
for check_text in filter_data:
|
||||
if check_text.lower() in location.lower():
|
||||
jailbreak = True
|
||||
break
|
||||
if jailbreak: break
|
||||
if (jailbreak and modifier == ".not") or (not jailbreak and modifier != ".not"):
|
||||
match = False
|
||||
break
|
||||
elif modifier in [".gte", ".lte"]:
|
||||
if method_name == "vote_count":
|
||||
tmdb_item = None
|
||||
for key, value in movie_map.items():
|
||||
if current.ratingKey in value:
|
||||
try:
|
||||
tmdb_item = self.TMDb.get_movie(key) if self.is_movie else self.TMDb.get_show(key)
|
||||
break
|
||||
except Failed:
|
||||
pass
|
||||
if tmdb_item is None:
|
||||
logger.warning(f"Filter Error: No TMDb ID found for {current.title}")
|
||||
continue
|
||||
attr = tmdb_item.vote_count
|
||||
else:
|
||||
attr = getattr(current, method_name) / 60000 if method_name == "duration" else getattr(current, method_name)
|
||||
if attr is None or (modifier == ".lte" and attr > filter_data) or (modifier == ".gte" and attr < filter_data):
|
||||
match = False
|
||||
break
|
||||
else:
|
||||
attrs = []
|
||||
if method_name in ["video_resolution", "audio_language", "subtitle_language"]:
|
||||
for media in current.media:
|
||||
if method_name == "video_resolution":
|
||||
attrs.extend([media.videoResolution])
|
||||
for part in media.parts:
|
||||
if method_name == "audio_language":
|
||||
attrs.extend([a.language for a in part.audioStreams()])
|
||||
if method_name == "subtitle_language":
|
||||
attrs.extend([s.language for s in part.subtitleStreams()])
|
||||
elif method_name in ["contentRating", "studio", "year", "rating", "originallyAvailableAt"]:
|
||||
attrs = [str(getattr(current, method_name))]
|
||||
elif method_name in ["actors", "countries", "directors", "genres", "writers", "collections"]:
|
||||
attrs = [getattr(x, "tag") for x in getattr(current, method_name)]
|
||||
else:
|
||||
raise Failed(f"Filter Error: filter: {method_name} not supported")
|
||||
|
||||
if (not list(set(filter_data) & set(attrs)) and modifier != ".not") or (list(set(filter_data) & set(attrs)) and modifier == ".not"):
|
||||
match = False
|
||||
break
|
||||
length = util.print_return(length, f"Filtering {(' ' * (max_length - len(str(i)))) + str(i)}/{total} {current.title}")
|
||||
if match:
|
||||
util.print_end(length, f"{name} Collection | {'=' if current in collection_items else '+'} | {current.title}")
|
||||
if current in collection_items: rating_key_map[current.ratingKey] = None
|
||||
elif smart: self.query_data(current.addLabel, name)
|
||||
else: self.query_data(current.addCollection, name)
|
||||
elif show_filtered is True:
|
||||
logger.info(f"{name} Collection | X | {current.title}")
|
||||
media_type = f"{'Movie' if self.is_movie else 'Show'}{'s' if total > 1 else ''}"
|
||||
util.print_end(length, f"{total} {media_type} Processed")
|
||||
return rating_key_map
|
||||
|
||||
def search_item(self, data, year=None):
|
||||
kwargs = {}
|
||||
if year is not None:
|
||||
|
|
|
@ -21,10 +21,11 @@ class TautulliAPI:
|
|||
self.url = params["url"]
|
||||
self.apikey = params["apikey"]
|
||||
|
||||
def get_items(self, library, time_range=30, stats_count=20, list_type="popular", stats_count_buffer=20):
|
||||
logger.info(f"Processing Tautulli Most {'Popular' if list_type == 'popular' else 'Watched'}: {stats_count} {'Movies' if library.is_movie else 'Shows'}")
|
||||
response = self._request(f"{self.url}/api/v2?apikey={self.apikey}&cmd=get_home_stats&time_range={time_range}&stats_count={int(stats_count) + int(stats_count_buffer)}")
|
||||
stat_id = f"{'popular' if list_type == 'popular' else 'top'}_{'movies' if library.is_movie else 'tv'}"
|
||||
def get_items(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}")
|
||||
stat_id = f"{'popular' if params['list_type'] == 'popular' else 'top'}_{'movies' if library.is_movie else 'tv'}"
|
||||
|
||||
items = None
|
||||
for entry in response["response"]["data"]:
|
||||
|
@ -38,19 +39,17 @@ class TautulliAPI:
|
|||
rating_keys = []
|
||||
count = 0
|
||||
for item in items:
|
||||
if item["section_id"] == section_id and count < int(stats_count):
|
||||
rk = None
|
||||
if item["section_id"] == section_id and count < int(params['list_size']):
|
||||
try:
|
||||
library.fetchItem(int(item["rating_key"]))
|
||||
rk = item["rating_key"]
|
||||
rating_keys.append(item["rating_key"])
|
||||
except (BadRequest, NotFound):
|
||||
new_item = library.exact_search(item["title"])
|
||||
new_item = library.exact_search(item["title"], year=item["year"])
|
||||
if new_item:
|
||||
rk = new_item[0].ratingKey
|
||||
rating_keys.append(new_item[0].ratingKey)
|
||||
else:
|
||||
logger.error(f"Plex Error: Item {item} not found")
|
||||
continue
|
||||
rating_keys.append(rk)
|
||||
count += 1
|
||||
return rating_keys
|
||||
|
||||
|
|
|
@ -186,105 +186,37 @@ def update_libraries(config, is_test, requested_collections, resume_from):
|
|||
util.separator(f"{builder.name} Collection")
|
||||
logger.info("")
|
||||
try:
|
||||
collection_obj = library.get_collection(builder.name)
|
||||
builder.run_collections_again(movie_map, show_map)
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
continue
|
||||
builder.run_collections_again(collection_obj, movie_map, show_map)
|
||||
|
||||
def run_collection(config, library, metadata, requested_collections, is_test, resume_from, movie_map, show_map):
|
||||
for mapping_name, collection_attrs in requested_collections.items():
|
||||
if is_test and ("test" not in collection_attrs or collection_attrs["test"] is not True):
|
||||
no_template_test = True
|
||||
if "template" in collection_attrs and collection_attrs["template"]:
|
||||
for data_template in util.get_list(collection_attrs["template"], split=False):
|
||||
if "name" in data_template \
|
||||
and data_template["name"] \
|
||||
and metadata.templates \
|
||||
and data_template["name"] in metadata.templates \
|
||||
and metadata.templates[data_template["name"]] \
|
||||
and "test" in metadata.templates[data_template["name"]] \
|
||||
and metadata.templates[data_template["name"]]["test"] is True:
|
||||
no_template_test = False
|
||||
if no_template_test:
|
||||
continue
|
||||
def map_guids(config, library):
|
||||
movie_map = {}
|
||||
show_map = {}
|
||||
length = 0
|
||||
logger.info(f"Mapping {'Movie' if library.is_movie else 'Show'} Library: {library.name}")
|
||||
items = library.Plex.all()
|
||||
for i, item in enumerate(items, 1):
|
||||
length = util.print_return(length, f"Processing: {i}/{len(items)} {item.title}")
|
||||
try:
|
||||
if resume_from and resume_from != mapping_name:
|
||||
continue
|
||||
elif resume_from == mapping_name:
|
||||
resume_from = None
|
||||
logger.info("")
|
||||
util.separator(f"Resuming Collections")
|
||||
|
||||
logger.info("")
|
||||
util.separator(f"{mapping_name} Collection")
|
||||
logger.info("")
|
||||
|
||||
try:
|
||||
builder = CollectionBuilder(config, library, metadata, mapping_name, collection_attrs)
|
||||
except Failed as f:
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(f, error=True)
|
||||
continue
|
||||
except Exception as e:
|
||||
util.print_stacktrace()
|
||||
logger.error(e)
|
||||
continue
|
||||
|
||||
try:
|
||||
collection_obj = library.get_collection(mapping_name)
|
||||
collection_name = collection_obj.title
|
||||
collection_smart = library.smart(collection_obj)
|
||||
if (builder.smart and not collection_smart) or (not builder.smart and collection_smart):
|
||||
logger.info("")
|
||||
logger.error(f"Collection Error: Converting {collection_obj.title} to a {'smart' if builder.smart else 'normal'} collection")
|
||||
library.query(collection_obj.delete)
|
||||
collection_obj = None
|
||||
except Failed:
|
||||
collection_obj = None
|
||||
collection_name = mapping_name
|
||||
|
||||
if len(builder.schedule) > 0:
|
||||
util.print_multiline(builder.schedule, info=True)
|
||||
|
||||
rating_key_map = {}
|
||||
logger.info("")
|
||||
if builder.sync:
|
||||
logger.info("Sync Mode: sync")
|
||||
if collection_obj:
|
||||
for item in library.get_collection_items(collection_obj, builder.smart_label_collection):
|
||||
rating_key_map[item.ratingKey] = item
|
||||
else:
|
||||
logger.info("Sync Mode: append")
|
||||
|
||||
for i, f in enumerate(builder.filters):
|
||||
if i == 0:
|
||||
logger.info("")
|
||||
logger.info(f"Collection Filter {f[0]}: {f[1]}")
|
||||
|
||||
if not builder.smart_url:
|
||||
builder.run_methods(collection_obj, collection_name, rating_key_map, movie_map, show_map)
|
||||
|
||||
try:
|
||||
if not collection_obj and builder.smart_url:
|
||||
library.create_smart_collection(collection_name, builder.smart_type_key, builder.smart_url)
|
||||
elif not collection_obj and builder.smart_label_collection:
|
||||
library.create_smart_labels(collection_name, sort=builder.smart_sort)
|
||||
plex_collection = library.get_collection(collection_name)
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
logger.error(e)
|
||||
continue
|
||||
|
||||
builder.update_details(plex_collection)
|
||||
|
||||
if builder.run_again and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
|
||||
library.run_again.append(builder)
|
||||
|
||||
except Exception as e:
|
||||
id_type, main_id = config.Convert.get_id(item, library, length)
|
||||
except BadRequest:
|
||||
util.print_stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
return resume_from
|
||||
util.print_end(length, f"{'Cache | ! |' if config.Cache else 'Mapping Error:'} | {item.guid} for {item.title} not found")
|
||||
continue
|
||||
if not isinstance(main_id, list):
|
||||
main_id = [main_id]
|
||||
if id_type == "movie":
|
||||
for m in main_id:
|
||||
if m in movie_map: movie_map[m].append(item.ratingKey)
|
||||
else: movie_map[m] = [item.ratingKey]
|
||||
elif id_type == "show":
|
||||
for m in main_id:
|
||||
if m in show_map: show_map[m].append(item.ratingKey)
|
||||
else: show_map[m] = [item.ratingKey]
|
||||
util.print_end(length, f"Processed {len(items)} {'Movies' if library.is_movie else 'Shows'}")
|
||||
return movie_map, show_map
|
||||
|
||||
def mass_metadata(config, library, movie_map, show_map):
|
||||
length = 0
|
||||
|
@ -380,32 +312,70 @@ def mass_metadata(config, library, movie_map, show_map):
|
|||
except Failed:
|
||||
pass
|
||||
|
||||
def map_guids(config, library):
|
||||
movie_map = {}
|
||||
show_map = {}
|
||||
length = 0
|
||||
logger.info(f"Mapping {'Movie' if library.is_movie else 'Show'} Library: {library.name}")
|
||||
items = library.Plex.all()
|
||||
for i, item in enumerate(items, 1):
|
||||
length = util.print_return(length, f"Processing: {i}/{len(items)} {item.title}")
|
||||
def run_collection(config, library, metadata, requested_collections, is_test, resume_from, movie_map, show_map):
|
||||
for mapping_name, collection_attrs in requested_collections.items():
|
||||
if is_test and ("test" not in collection_attrs or collection_attrs["test"] is not True):
|
||||
no_template_test = True
|
||||
if "template" in collection_attrs and collection_attrs["template"]:
|
||||
for data_template in util.get_list(collection_attrs["template"], split=False):
|
||||
if "name" in data_template \
|
||||
and data_template["name"] \
|
||||
and metadata.templates \
|
||||
and data_template["name"] in metadata.templates \
|
||||
and metadata.templates[data_template["name"]] \
|
||||
and "test" in metadata.templates[data_template["name"]] \
|
||||
and metadata.templates[data_template["name"]]["test"] is True:
|
||||
no_template_test = False
|
||||
if no_template_test:
|
||||
continue
|
||||
try:
|
||||
id_type, main_id = config.Convert.get_id(item, library, length)
|
||||
except BadRequest:
|
||||
if resume_from and resume_from != mapping_name:
|
||||
continue
|
||||
elif resume_from == mapping_name:
|
||||
resume_from = None
|
||||
logger.info("")
|
||||
util.separator(f"Resuming Collections")
|
||||
|
||||
logger.info("")
|
||||
util.separator(f"{mapping_name} Collection")
|
||||
logger.info("")
|
||||
|
||||
builder = CollectionBuilder(config, library, metadata, mapping_name, collection_attrs)
|
||||
|
||||
if len(builder.schedule) > 0:
|
||||
util.print_multiline(builder.schedule, info=True)
|
||||
|
||||
logger.info("")
|
||||
logger.info(f"Sync Mode: {'sync' if builder.sync else 'append'}")
|
||||
|
||||
if len(builder.filters) > 0:
|
||||
logger.info("")
|
||||
for filter_key, filter_value in builder.filters:
|
||||
logger.info(f"Collection Filter {filter_key}: {filter_value}")
|
||||
|
||||
if not builder.smart_url:
|
||||
builder.collect_rating_keys(movie_map, show_map)
|
||||
logger.info("")
|
||||
if len(builder.rating_keys) > 0:
|
||||
builder.add_to_collection(movie_map, show_map)
|
||||
if len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0:
|
||||
builder.run_missing(movie_map, show_map)
|
||||
if builder.sync and len(builder.rating_keys) > 0:
|
||||
builder.sync_collection()
|
||||
logger.info("")
|
||||
|
||||
builder.update_details()
|
||||
|
||||
if builder.run_again and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
|
||||
library.run_again.append(builder)
|
||||
|
||||
except Failed as e:
|
||||
util.print_stacktrace()
|
||||
util.print_end(length, f"{'Cache | ! |' if config.Cache else 'Mapping Error:'} | {item.guid} for {item.title} not found")
|
||||
continue
|
||||
if not isinstance(main_id, list):
|
||||
main_id = [main_id]
|
||||
if id_type == "movie":
|
||||
for m in main_id:
|
||||
if m in movie_map: movie_map[m].append(item.ratingKey)
|
||||
else: movie_map[m] = [item.ratingKey]
|
||||
elif id_type == "show":
|
||||
for m in main_id:
|
||||
if m in show_map: show_map[m].append(item.ratingKey)
|
||||
else: show_map[m] = [item.ratingKey]
|
||||
util.print_end(length, f"Processed {len(items)} {'Movies' if library.is_movie else 'Shows'}")
|
||||
return movie_map, show_map
|
||||
util.print_multiline(e, error=True)
|
||||
except Exception as e:
|
||||
util.print_stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
return resume_from
|
||||
|
||||
try:
|
||||
if run or test or collections or libraries or resume:
|
||||
|
|
Loading…
Reference in a new issue