update playlist ids

This commit is contained in:
meisnate12 2022-02-08 17:57:36 -05:00
parent 20719de518
commit 65130f9333
4 changed files with 234 additions and 319 deletions

View file

@ -1 +1 @@
1.15.1-develop58
1.15.1-develop59

View file

@ -196,15 +196,65 @@ music_attributes = [
] + details + summary_details + poster_details + background_details
class CollectionBuilder:
def __init__(self, config, library, metadata, name, no_missing, data, playlist=False, valid_users=None):
def __init__(self, config, metadata, name, no_missing, data, library=None):
self.config = config
self.library = library
self.metadata = metadata
self.mapping_name = name
self.no_missing = no_missing
self.data = data
self.playlist = playlist
self.valid_users = valid_users
self.library = library
self.libraries = []
self.playlist = library is None
methods = {m.lower(): m for m in self.data}
self.type = "playlist" if self.playlist else "collection"
self.Type = self.type.capitalize()
if "name" in methods:
name = self.data[methods["name"]]
elif f"{self.type}_name" in methods:
logger.warning(f"Config Warning: Running {self.type}_name as name")
name = self.data[methods[f"{self.type}_name"]]
else:
name = None
if name:
logger.debug("")
logger.debug("Validating Method: name")
if not name:
raise Failed(f"{self.Type} Error: name attribute is blank")
logger.debug(f"Value: {name}")
self.name = name
else:
self.name = self.mapping_name
if "template" in methods:
logger.debug("")
logger.debug("Validating Method: template")
new_attributes = self.metadata.apply_template(self.name, self.data, self.data[methods["template"]])
for attr in new_attributes:
if attr.lower() not in methods:
self.data[attr] = new_attributes[attr]
methods[attr.lower()] = attr
if self.playlist:
if "libraries" in methods:
logger.debug("")
logger.debug("Validating Method: libraries")
if not self.data[methods["libraries"]]:
raise Failed(f"{self.Type} Error: libraries attribute is blank")
else:
logger.debug(f"Value: {self.data[methods['libraries']]}")
for pl_library in util.get_list(self.data[methods["libraries"]]):
if str(pl_library) in config.library_map:
self.libraries.append(config.library_map[pl_library])
else:
raise Failed(f"Playlist Error: Library: {pl_library} not defined")
self.library = self.libraries[0]
else:
raise Failed("Playlist Error: libraries attribute is required")
else:
self.libraries.append(self.library)
self.language = self.library.Plex.language
self.details = {
"show_filtered": self.library.show_filtered,
@ -253,37 +303,41 @@ class CollectionBuilder:
self.exists = False
self.created = False
self.deleted = False
self.type = "playlist" if self.playlist else "collection"
self.Type = self.type.capitalize()
self.sync_to_users = None
self.valid_users = []
methods = {m.lower(): m for m in self.data}
if "name" in methods:
name = self.data[methods["name"]]
elif f"{self.type}_name" in methods:
logger.warning(f"Config Warning: Running {self.type}_name as name")
name = self.data[methods[f"{self.type}_name"]]
if self.playlist:
server_check = None
for pl_library in self.libraries:
if server_check:
if pl_library.PlexServer.machineIdentifier != server_check:
raise Failed("Playlist Error: All defined libraries must be on the same server")
else:
name = None
server_check = pl_library.PlexServer.machineIdentifier
if name:
self.sync_to_users = config.general["playlist_sync_to_users"]
if "sync_to_users" in methods or "sync_to_user" in methods:
s_attr = f"sync_to_user{'s' if 'sync_to_users' in methods else ''}"
logger.debug("")
logger.debug("Validating Method: name")
if not name:
raise Failed(f"{self.Type} Error: name attribute is blank")
logger.debug(f"Value: {name}")
self.name = name
logger.debug("Validating Method: sync_to_users")
if self.data[methods[s_attr]]:
logger.warning(f"Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_user: {self.sync_to_users}")
else:
self.name = self.mapping_name
logger.debug(f"Value: {self.data[methods[s_attr]]}")
self.sync_to_users = self.data[methods[s_attr]]
else:
logger.warning(f"Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_user: {self.sync_to_users}")
if "template" in methods:
logger.debug("")
logger.debug("Validating Method: template")
new_attributes = self.metadata.apply_template(self.name, self.data, self.data[methods["template"]])
for attr in new_attributes:
if attr.lower() not in methods:
self.data[attr] = new_attributes[attr]
methods[attr.lower()] = attr
plex_users = self.library.users
if self.sync_to_users:
if str(self.sync_to_users) == "all":
self.valid_users = plex_users
else:
for user in util.get_list(self.sync_to_users):
if user in plex_users:
self.valid_users.append(user)
else:
raise Failed(f"Playlist Error: User: {user} not found in plex\nOptions: {plex_users}")
if "delete_not_scheduled" in methods:
logger.debug("")
@ -594,7 +648,7 @@ class CollectionBuilder:
raise Failed(f"{self.Type} Error: No builders allowed with blank_collection")
if self.custom_sort is True and (len(self.builders) > 1 or self.builders[0][0] not in custom_sort_builders):
raise Failed(f"{self.Type} Error: " + ('Playlists' if playlist else 'collection_order: custom') +
raise Failed(f"{self.Type} Error: " + ('Playlists' if self.playlist else 'collection_order: custom') +
(f" can only be used with a single builder per {self.type}" if len(self.builders) > 1 else f" cannot be used with {self.builders[0][0]}"))
if "add_missing" not in self.radarr_details:
@ -1254,13 +1308,8 @@ class CollectionBuilder:
self.config.Cache.update_list_ids(list_key, ids)
return ids
def find_rating_keys(self):
for method, value in self.builders:
logger.debug("")
logger.debug(f"Builder: {method}: {value}")
logger.info("")
def filter_and_save_items(self, ids):
items = []
ids = self.gather_ids(method, value)
if len(ids) > 0:
total_ids = len(ids)
logger.debug("")
@ -1269,118 +1318,139 @@ class CollectionBuilder:
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 == "tvdb_season" and self.collection_level == "season":
show_id, season_num = input_id.split("_")
show_id = int(show_id)
if show_id in self.library.show_map:
show_item = self.library.fetchItem(self.library.show_map[show_id][0])
try:
items.append(show_item.season(season=int(season_num)))
except NotFound:
self.missing_parts.append(f"{show_item.title} Season: {season_num} Missing")
elif show_id not in self.missing_shows:
self.missing_shows.append(show_id)
elif id_type in ["tvdb_episode", "imdb"] and self.collection_level == "episode":
if id_type == "tvdb_episode":
show_id, season_num, episode_num = input_id.split("_")
show_id = int(show_id)
elif id_type == "imdb" and input_id not in self.ignore_imdb_ids:
rating_keys = []
if id_type == "ratingKey":
rating_keys = int(input_id)
elif id_type == "imdb":
if input_id not in self.ignore_imdb_ids:
found = False
for pl_library in self.libraries:
if input_id in pl_library.imdb_map:
found = True
rating_keys = pl_library.imdb_map[input_id]
break
if not found and (self.collection_level == "episode" or self.playlist or self.do_missing):
try:
_id, tmdb_type = self.config.Convert.imdb_to_tmdb(input_id, fail=True)
if tmdb_type != "episode":
raise Failed
tmdb_id, season_num, episode_num = _id.split("_")
show_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
except Failed as e:
if self.config.OMDb:
if tmdb_type == "episode" and (self.collection_level == "episode" or self.playlist):
try:
tmdb_id, season_num, episode_num = _id.split("_")
tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
tvdb_id = int(tvdb_id)
except Failed as e:
try:
if not self.config.OMDb:
raise Failed("")
if self.config.OMDb.limit:
raise Failed(f" and OMDb limit reached.")
raise Failed(" and OMDb limit reached.")
omdb_item = self.config.OMDb.get_omdb(input_id)
show_id = omdb_item.series_id
tvdb_id = omdb_item.series_id
season_num = omdb_item.season_num
episode_num = omdb_item.episode_num
if not show_id or not season_num or not episode_num:
if not tvdb_id or not season_num or not episode_num:
raise Failed(f" and OMDb metadata lookup Failed for IMDb ID: {input_id}")
except Failed as ee:
logger.error(f"{e}{ee}")
continue
else:
logger.error(e)
continue
else:
continue
if show_id in self.library.show_map or show_id in self.library.imdb_map:
show_item = self.library.fetchItem(self.library.show_map[show_id][0] if show_id in self.library.show_map else self.library.imdb_map[show_id][0])
for pl_library in self.libraries:
if tvdb_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(pl_library.show_map[tvdb_id][0])
try:
items.append(show_item.episode(season=int(season_num), episode=int(episode_num)))
except NotFound:
self.missing_parts.append(f"{show_item.title} Season: {season_num} Episode: {episode_num} Missing")
else:
if isinstance(show_id, str) and self.do_missing:
try:
tmdb_id, _ = self.config.Convert.imdb_to_tmdb(input_id, fail=True)
tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
break
if not found and tvdb_id not in self.missing_shows and self.do_missing:
self.missing_shows.append(tvdb_id)
elif tmdb_type == "movie" and self.do_missing and _id not in self.missing_movies:
self.missing_movies.append(_id)
elif tmdb_type == "show" and self.do_missing:
tvdb_id = self.config.Convert.tmdb_to_tvdb(_id, fail=True)
if tvdb_id not in self.missing_shows:
self.missing_shows.append(tvdb_id)
except Failed as e:
logger.warning(e)
elif show_id not in self.missing_shows:
self.missing_shows.append(show_id)
else:
rating_keys = []
if id_type == "ratingKey":
rating_keys = int(input_id)
continue
elif id_type == "tmdb" and not self.parts_collection:
input_id = int(input_id)
if input_id not in self.ignore_ids:
if input_id in self.library.movie_map:
rating_keys = self.library.movie_map[input_id]
elif input_id not in self.missing_movies:
found = False
for pl_library in self.libraries:
if input_id in pl_library.movie_map:
found = True
rating_keys = pl_library.movie_map[input_id]
break
if not found and input_id not in self.missing_movies:
self.missing_movies.append(input_id)
elif id_type in ["tvdb", "tmdb_show"] and not self.parts_collection:
input_id = int(input_id)
if id_type == "tmdb_show":
try:
input_id = self.config.Convert.tmdb_to_tvdb(input_id, fail=True)
tvdb_id = self.config.Convert.tmdb_to_tvdb(input_id, fail=True)
except Failed as e:
logger.warning(e)
continue
if input_id not in self.ignore_ids:
if input_id in self.library.show_map:
rating_keys = self.library.show_map[input_id]
elif input_id not in self.missing_shows:
else:
tvdb_id = int(input_id)
if tvdb_id not in self.ignore_ids:
found = False
for pl_library in self.libraries:
if tvdb_id in pl_library.show_map:
found = True
rating_keys = pl_library.show_map[tvdb_id]
break
if not found and tvdb_id not in self.missing_shows:
self.missing_shows.append(input_id)
elif id_type == "imdb" and not self.parts_collection:
if input_id not in self.ignore_imdb_ids:
if input_id in self.library.imdb_map:
rating_keys = self.library.imdb_map[input_id]
elif self.do_missing:
elif id_type == "tvdb_season" and (self.collection_level == "season" or self.playlist):
tvdb_id, season_num = input_id.split("_")
tvdb_id = int(tvdb_id)
found = False
for pl_library in self.libraries:
if tvdb_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(pl_library.show_map[tvdb_id][0])
try:
tmdb_id, tmdb_type = self.config.Convert.imdb_to_tmdb(input_id, fail=True)
if tmdb_type == "movie":
if tmdb_id not in self.missing_movies:
self.missing_movies.append(tmdb_id)
elif tmdb_type == "show":
tvdb_id = self.config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
if tvdb_id not in self.missing_shows:
season_obj = show_item.season(season=int(season_num))
if self.playlist:
items.extend(season_obj.episodes())
else:
items.append(season_obj)
except NotFound:
self.missing_parts.append(f"{show_item.title} Season: {season_num} Missing")
break
if not found and tvdb_id not in self.missing_shows:
self.missing_shows.append(tvdb_id)
except Failed as e:
logger.warning(e)
elif id_type == "tvdb_episode" and (self.collection_level == "episode" or self.playlist):
tvdb_id, season_num, episode_num = input_id.split("_")
tvdb_id = int(tvdb_id)
found = False
for pl_library in self.libraries:
if tvdb_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(pl_library.show_map[tvdb_id][0])
try:
items.append(show_item.episode(season=int(season_num), episode=int(episode_num)))
except NotFound:
self.missing_parts.append(f"{show_item.title} Season: {season_num} Episode: {episode_num} Missing")
if not found and tvdb_id not in self.missing_shows and self.do_missing:
self.missing_shows.append(tvdb_id)
else:
continue
if not isinstance(rating_keys, list):
rating_keys = [rating_keys]
for rk in rating_keys:
try:
items.append(self.fetch_item(rk))
item = self.fetch_item(rk)
if self.playlist and isinstance(item, (Show, Season)):
items.extend(item.episodes())
else:
items.append(item)
except Failed as e:
logger.error(e)
util.print_end()
if len(items) > 0:
self.filter_and_save_items(items)
def filter_and_save_items(self, items):
if not items:
return None
name = self.obj.title if self.obj else self.name
total = len(items)
max_length = len(str(total))

View file

@ -931,6 +931,8 @@ class ConfigFile:
util.separator()
self.library_map = {_l.original_mapping_name: _l for _l in self.libraries}
if len(self.libraries) > 0:
logger.info(f"{len(self.libraries)} Plex Library Connection{'s' if len(self.libraries) > 1 else ''} Successful")
else:

View file

@ -258,7 +258,7 @@ def update_libraries(config):
for collection in library.get_all_collections():
logger.info(f"Collection {collection.title} Deleted")
library.query(collection.delete)
if not library.is_other and not library.is_music and library.metadata_files and not library_only:
if not library.is_other and not library.is_music and (library.metadata_files or library.original_mapping_name in config.library_map) and not library_only:
logger.info("")
util.separator(f"Mapping {library.name} Library", space=False, border=False)
logger.info("")
@ -847,7 +847,7 @@ def run_collection(config, library, metadata, requested_collections):
util.separator(f"Validating {mapping_name} Attributes", space=False, border=False)
builder = CollectionBuilder(config, library, metadata, mapping_name, no_missing, collection_attrs)
builder = CollectionBuilder(config, metadata, mapping_name, no_missing, collection_attrs, library=library)
logger.info("")
util.separator(f"Running {mapping_name} Collection", space=False, border=False)
@ -873,7 +873,11 @@ def run_collection(config, library, metadata, requested_collections):
for filter_key, filter_value in builder.tmdb_filters:
logger.info(f"Collection Filter {filter_key}: {filter_value}")
builder.find_rating_keys()
for method, value in builder.builders:
logger.debug("")
logger.debug(f"Builder: {method}: {value}")
logger.info("")
builder.filter_and_save_items(builder.gather_ids(method, value))
if len(builder.added_items) >= builder.minimum and builder.build_collection:
items_added, items_unchanged = builder.add_to_collection()
@ -980,7 +984,6 @@ def run_playlists(config):
logger.info("")
util.separator("Playlists")
logger.info("")
library_map = {_l.original_mapping_name: _l for _l in config.libraries}
for playlist_file in config.playlist_files:
for mapping_name, playlist_attrs in playlist_file.playlists.items():
playlist_start = datetime.now()
@ -1022,46 +1025,10 @@ def run_playlists(config):
if output_str:
logger.info(output_str)
logger.info("")
if "libraries" not in playlist_attrs or not playlist_attrs["libraries"]:
raise Failed("Playlist Error: libraries attribute is required and cannot be blank")
pl_libraries = []
for pl_library in util.get_list(playlist_attrs["libraries"]):
if str(pl_library) in library_map:
pl_libraries.append(library_map[pl_library])
else:
raise Failed(f"Playlist Error: Library: {pl_library} not defined")
server_check = None
for pl_library in pl_libraries:
if server_check:
if pl_library.PlexServer.machineIdentifier != server_check:
raise Failed("Playlist Error: All defined libraries must be on the same server")
else:
server_check = pl_library.PlexServer.machineIdentifier
sync_to_users = config.general["playlist_sync_to_users"]
if "sync_to_users" in playlist_attrs:
sync_to_users = playlist_attrs["sync_to_users"]
elif "sync_to_user" in playlist_attrs:
sync_to_users = playlist_attrs["sync_to_user"]
else:
logger.warning(f"Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_user: {sync_to_users}")
valid_users = []
plex_users = pl_libraries[0].users
if sync_to_users:
if str(sync_to_users) == "all":
valid_users = plex_users
else:
for user in util.get_list(sync_to_users):
if user in plex_users:
valid_users.append(user)
else:
raise Failed(f"Playlist Error: User: {user} not found in plex\nOptions: {plex_users}")
util.separator(f"Validating {mapping_name} Attributes", space=False, border=False)
builder = CollectionBuilder(config, pl_libraries[0], playlist_file, mapping_name, no_missing,
playlist_attrs, playlist=True, valid_users=valid_users)
builder = CollectionBuilder(config, playlist_file, mapping_name, no_missing, playlist_attrs)
logger.info("")
util.separator(f"Running {mapping_name} Playlist", space=False, border=False)
@ -1086,142 +1053,18 @@ def run_playlists(config):
logger.debug("")
logger.debug(f"Builder: {method}: {value}")
logger.info("")
items = []
if "plex" in method:
ids = []
for pl_library in pl_libraries:
for pl_library in builder.libraries:
ids.extend(pl_library.get_rating_keys(method, value))
elif "tautulli" in method:
ids = []
for pl_library in pl_libraries:
for pl_library in builder.libraries:
ids.extend(pl_library.Tautulli.get_rating_keys(pl_library, value, True))
else:
ids = builder.gather_ids(method, value)
if len(ids) > 0:
total_ids = len(ids)
logger.debug("")
logger.debug(f"{total_ids} IDs Found: {ids}")
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 == "tvdb_season":
show_id, season_num = input_id.split("_")
show_id = int(show_id)
found = False
for pl_library in pl_libraries:
if show_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(pl_library.show_map[show_id][0])
try:
items.extend(show_item.season(season=int(season_num)).episodes())
except NotFound:
builder.missing_parts.append(f"{show_item.title} Season: {season_num} Missing")
break
if not found and show_id not in builder.missing_shows:
builder.missing_shows.append(show_id)
elif id_type == "tvdb_episode":
show_id, season_num, episode_num = input_id.split("_")
show_id = int(show_id)
found = False
for pl_library in pl_libraries:
if show_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(pl_library.show_map[show_id][0])
try:
items.append(
show_item.episode(season=int(season_num), episode=int(episode_num)))
except NotFound:
builder.missing_parts.append(
f"{show_item.title} Season: {season_num} Episode: {episode_num} Missing")
break
if not found and show_id not in builder.missing_shows:
builder.missing_shows.append(show_id)
else:
rating_keys = []
if id_type == "ratingKey":
rating_keys = input_id
elif id_type == "tmdb":
if input_id not in builder.ignore_ids:
found = False
for pl_library in pl_libraries:
if input_id in pl_library.movie_map:
found = True
rating_keys = pl_library.movie_map[input_id]
break
if not found and input_id not in builder.missing_movies:
builder.missing_movies.append(input_id)
elif id_type in ["tvdb", "tmdb_show"]:
if id_type == "tmdb_show":
try:
input_id = config.Convert.tmdb_to_tvdb(input_id, fail=True)
except Failed as e:
logger.warning(e)
continue
if input_id not in builder.ignore_ids:
found = False
for pl_library in pl_libraries:
if input_id in pl_library.show_map:
found = True
rating_keys = pl_library.show_map[input_id]
break
if not found and input_id not in builder.missing_shows:
builder.missing_shows.append(input_id)
elif id_type == "imdb":
if input_id not in builder.ignore_imdb_ids:
found = False
for pl_library in pl_libraries:
if input_id in pl_library.imdb_map:
found = True
rating_keys = pl_library.imdb_map[input_id]
break
if not found:
try:
_id, tmdb_type = config.Convert.imdb_to_tmdb(input_id, fail=True)
if tmdb_type == "episode":
tmdb_id, season_num, episode_num = _id.split("_")
show_id = config.Convert.tmdb_to_tvdb(tmdb_id, fail=True)
show_id = int(show_id)
found = False
for pl_library in pl_libraries:
if show_id in pl_library.show_map:
found = True
show_item = pl_library.fetchItem(
pl_library.show_map[show_id][0])
try:
items.append(show_item.episode(season=int(season_num),
episode=int(episode_num)))
except NotFound:
builder.missing_parts.append(
f"{show_item.title} Season: {season_num} Episode: {episode_num} Missing")
break
if not found and show_id not in builder.missing_shows:
builder.missing_shows.append(show_id)
elif tmdb_type == "movie" and builder.do_missing:
if _id not in builder.missing_movies:
builder.missing_movies.append(_id)
elif tmdb_type == "show" and builder.do_missing:
tvdb_id = config.Convert.tmdb_to_tvdb(_id, fail=True)
if tvdb_id not in builder.missing_shows:
builder.missing_shows.append(tvdb_id)
except Failed as e:
logger.warning(e)
continue
if not isinstance(rating_keys, list):
rating_keys = [rating_keys]
for rk in rating_keys:
try:
item = builder.fetch_item(rk)
if isinstance(item, (Show, Season)):
items.extend(item.episodes())
else:
items.append(item)
except Failed as e:
logger.error(e)
util.print_end()
if len(items) > 0:
builder.filter_and_save_items(items)
builder.filter_and_save_items(ids)
if len(builder.added_items) >= builder.minimum:
items_added, items_unchanged = builder.add_to_collection()