mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
add sync_to_user to playlists
This commit is contained in:
parent
e3e0ef116a
commit
6871587bb6
5 changed files with 363 additions and 298 deletions
|
@ -86,7 +86,7 @@ scheduled_boolean = ["visible_library", "visible_home", "visible_shared"]
|
|||
string_details = ["sort_title", "content_rating", "name_mapping"]
|
||||
ignored_details = [
|
||||
"smart_filter", "smart_label", "smart_url", "run_again", "schedule", "sync_mode", "template", "test", "delete_not_scheduled",
|
||||
"tmdb_person", "build_collection", "collection_order", "collection_level", "validate_builders", "collection_name", "sort_by", "libraries"
|
||||
"tmdb_person", "build_collection", "collection_order", "collection_level", "validate_builders", "collection_name", "sort_by", "libraries", "sync_to_users"
|
||||
]
|
||||
details = ["ignore_ids", "ignore_imdb_ids", "server_preroll", "collection_changes_webhooks", "collection_mode",
|
||||
"collection_minimum", "label"] + boolean_details + scheduled_boolean + string_details
|
||||
|
@ -2136,6 +2136,37 @@ class CollectionBuilder:
|
|||
self.library.moveItem(self.obj, item, previous)
|
||||
previous = item
|
||||
|
||||
def delete_user_playlist(self, title, user):
|
||||
user_server = self.library.PlexServer.switchUser(user)
|
||||
user_playlist = user_server.playlist(title)
|
||||
user_playlist.delete()
|
||||
|
||||
def delete_playlist(self, users):
|
||||
if self.obj:
|
||||
self.library.query(self.obj.delete)
|
||||
logger.info("")
|
||||
logger.info(f"Playlist {self.obj.title} deleted")
|
||||
if users:
|
||||
for user in users:
|
||||
try:
|
||||
self.delete_user_playlist(self.obj.title, user)
|
||||
logger.info(f"Playlist {self.obj.title} deleted on User {user}")
|
||||
except NotFound:
|
||||
logger.error(f"Playlist {self.obj.title} not found on User {user}")
|
||||
|
||||
def sync_playlist(self, users):
|
||||
if self.obj and users:
|
||||
logger.info("")
|
||||
util.separator(f"Syncing Playlist to Users", space=False, border=False)
|
||||
logger.info("")
|
||||
for user in users:
|
||||
try:
|
||||
self.delete_user_playlist(self.obj.title, user)
|
||||
except NotFound:
|
||||
pass
|
||||
self.obj.copyToUser(user)
|
||||
logger.info(f"Playlist: {self.name} synced to {user}")
|
||||
|
||||
def send_notifications(self, playlist=False):
|
||||
if self.obj and self.details["collection_changes_webhooks"] and \
|
||||
(self.created or len(self.notification_additions) > 0 or len(self.notification_removals) > 0):
|
||||
|
|
|
@ -241,6 +241,7 @@ class ConfigFile:
|
|||
"tvdb_language": check_for_attribute(self.data, "tvdb_language", parent="settings", default="default"),
|
||||
"ignore_ids": check_for_attribute(self.data, "ignore_ids", parent="settings", var_type="int_list", default_is_none=True),
|
||||
"ignore_imdb_ids": check_for_attribute(self.data, "ignore_imdb_ids", parent="settings", var_type="list", default_is_none=True),
|
||||
"playlist_sync_to_user": check_for_attribute(self.data, "playlist_sync_to_user", parent="settings", default="all"),
|
||||
"assets_for_all": check_for_attribute(self.data, "assets_for_all", parent="settings", var_type="bool", default=False, save=False, do_print=False)
|
||||
}
|
||||
self.webhooks = {
|
||||
|
|
|
@ -252,6 +252,7 @@ class Plex(Library):
|
|||
else:
|
||||
raise Failed(f"Plex Error: Plex Library must be a Movies or TV Shows library")
|
||||
|
||||
self._users = []
|
||||
self.agent = self.Plex.agent
|
||||
self.is_movie = self.type == "Movie"
|
||||
self.is_show = self.type == "Show"
|
||||
|
@ -412,6 +413,16 @@ class Plex(Library):
|
|||
else: method = None
|
||||
return self.Plex._server.query(key, method=method)
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
if not self._users:
|
||||
users = []
|
||||
for user in self.PlexServer.myPlexAccount().users():
|
||||
if self.PlexServer.machineIdentifier in [s.machineIdentifier for s in user.servers]:
|
||||
users.append(user.title)
|
||||
self._users = users
|
||||
return self._users
|
||||
|
||||
def alter_collection(self, item, collection, smart_label_collection=False, add=True):
|
||||
if smart_label_collection:
|
||||
self.query_data(item.addLabel if add else item.removeLabel, collection)
|
||||
|
|
|
@ -66,14 +66,10 @@ class Webhooks:
|
|||
def error_hooks(self, text, server=None, library=None, collection=None, playlist=None, critical=True):
|
||||
if self.error_webhooks:
|
||||
json = {"error": str(text), "critical": critical}
|
||||
if server:
|
||||
json["server_name"] = str(server)
|
||||
if library:
|
||||
json["library_name"] = str(library)
|
||||
if collection:
|
||||
json["collection"] = str(collection)
|
||||
if playlist:
|
||||
json["playlist"] = str(playlist)
|
||||
if server: json["server_name"] = str(server)
|
||||
if library: json["library_name"] = str(library)
|
||||
if collection: json["collection"] = str(collection)
|
||||
if playlist: json["playlist"] = str(playlist)
|
||||
self._request(self.error_webhooks, json)
|
||||
|
||||
def collection_hooks(self, webhooks, collection, poster_url=None, background_url=None, created=False, deleted=False, additions=None, removals=None, playlist=False):
|
||||
|
|
|
@ -287,10 +287,7 @@ def update_libraries(config):
|
|||
util.print_stacktrace()
|
||||
util.print_multiline(e, critical=True)
|
||||
|
||||
|
||||
|
||||
if config.playlist_files:
|
||||
library_map = {_l.original_mapping_name: _l for _l in config.libraries}
|
||||
os.makedirs(os.path.join(default_dir, "logs", "playlists"), exist_ok=True)
|
||||
pf_file_logger = os.path.join(default_dir, "logs", "playlists", "playlists.log")
|
||||
should_roll_over = os.path.isfile(pf_file_logger)
|
||||
|
@ -299,292 +296,7 @@ def update_libraries(config):
|
|||
if should_roll_over:
|
||||
playlists_handler.doRollover()
|
||||
logger.addHandler(playlists_handler)
|
||||
|
||||
logger.info("")
|
||||
util.separator("Playlists")
|
||||
logger.info("")
|
||||
for playlist_file in config.playlist_files:
|
||||
for mapping_name, playlist_attrs in playlist_file.playlists.items():
|
||||
playlist_start = datetime.now()
|
||||
if config.test_mode and ("test" not in playlist_attrs or playlist_attrs["test"] is not True):
|
||||
no_template_test = True
|
||||
if "template" in playlist_attrs and playlist_attrs["template"]:
|
||||
for data_template in util.get_list(playlist_attrs["template"], split=False):
|
||||
if "name" in data_template \
|
||||
and data_template["name"] \
|
||||
and playlist_file.templates \
|
||||
and data_template["name"] in playlist_file.templates \
|
||||
and playlist_file.templates[data_template["name"]] \
|
||||
and "test" in playlist_file.templates[data_template["name"]] \
|
||||
and playlist_file.templates[data_template["name"]]["test"] is True:
|
||||
no_template_test = False
|
||||
if no_template_test:
|
||||
continue
|
||||
|
||||
if "name_mapping" in playlist_attrs and playlist_attrs["name_mapping"]:
|
||||
playlist_log_name, output_str = util.validate_filename(playlist_attrs["name_mapping"])
|
||||
else:
|
||||
playlist_log_name, output_str = util.validate_filename(mapping_name)
|
||||
playlist_log_folder = os.path.join(default_dir, "logs", "playlists", playlist_log_name)
|
||||
os.makedirs(playlist_log_folder, exist_ok=True)
|
||||
ply_file_logger = os.path.join(playlist_log_folder, "playlist.log")
|
||||
should_roll_over = os.path.isfile(ply_file_logger)
|
||||
playlist_handler = RotatingFileHandler(ply_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8")
|
||||
util.apply_formatter(playlist_handler)
|
||||
if should_roll_over:
|
||||
playlist_handler.doRollover()
|
||||
logger.addHandler(playlist_handler)
|
||||
server_name = None
|
||||
library_names = None
|
||||
try:
|
||||
util.separator(f"{mapping_name} Playlist")
|
||||
logger.info("")
|
||||
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
|
||||
|
||||
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)
|
||||
logger.info("")
|
||||
|
||||
util.separator(f"Running {mapping_name} Playlist", space=False, border=False)
|
||||
|
||||
if len(builder.schedule) > 0:
|
||||
util.print_multiline(builder.schedule, info=True)
|
||||
|
||||
items_added = 0
|
||||
items_removed = 0
|
||||
logger.info("")
|
||||
logger.info(f"Sync Mode: {'sync' if builder.sync else 'append'}")
|
||||
|
||||
if builder.filters or builder.tmdb_filters:
|
||||
logger.info("")
|
||||
for filter_key, filter_value in builder.filters:
|
||||
logger.info(f"Playlist Filter {filter_key}: {filter_value}")
|
||||
for filter_key, filter_value in builder.tmdb_filters:
|
||||
logger.info(f"Playlist Filter {filter_key}: {filter_value}")
|
||||
|
||||
method, value = builder.builders[0]
|
||||
logger.debug("")
|
||||
logger.debug(f"Builder: {method}: {value}")
|
||||
logger.info("")
|
||||
items = []
|
||||
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.error(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.error(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)
|
||||
|
||||
if len(builder.added_items) >= builder.minimum:
|
||||
logger.info("")
|
||||
util.separator(f"Adding to {mapping_name} Playlist", space=False, border=False)
|
||||
logger.info("")
|
||||
items_added = builder.add_to_collection()
|
||||
stats["added"] += items_added
|
||||
items_removed = 0
|
||||
if builder.sync:
|
||||
items_removed = builder.sync_collection()
|
||||
stats["removed"] += items_removed
|
||||
elif len(builder.added_items) < builder.minimum:
|
||||
logger.info("")
|
||||
logger.info(f"Playlist Minimum: {builder.minimum} not met for {mapping_name} Playlist")
|
||||
if builder.details["delete_below_minimum"] and builder.obj:
|
||||
builder.delete_collection()
|
||||
builder.deleted = True
|
||||
logger.info("")
|
||||
logger.info(f"Playlist {builder.obj.title} deleted")
|
||||
|
||||
if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
|
||||
if builder.details["show_missing"] is True:
|
||||
logger.info("")
|
||||
util.separator(f"Missing from Library", space=False, border=False)
|
||||
logger.info("")
|
||||
radarr_add, sonarr_add = builder.run_missing()
|
||||
stats["radarr"] += radarr_add
|
||||
stats["sonarr"] += sonarr_add
|
||||
|
||||
run_item_details = True
|
||||
try:
|
||||
builder.load_collection()
|
||||
if builder.created:
|
||||
stats["created"] += 1
|
||||
elif items_added > 0 or items_removed > 0:
|
||||
stats["modified"] += 1
|
||||
except Failed:
|
||||
util.print_stacktrace()
|
||||
run_item_details = False
|
||||
logger.info("")
|
||||
util.separator("No Playlist to Update", space=False, border=False)
|
||||
else:
|
||||
builder.update_details()
|
||||
|
||||
if builder.deleted:
|
||||
stats["deleted"] += 1
|
||||
|
||||
if (builder.item_details or builder.custom_sort) and run_item_details and builder.builders:
|
||||
try:
|
||||
builder.load_collection_items()
|
||||
except Failed:
|
||||
logger.info("")
|
||||
util.separator("No Items Found", space=False, border=False)
|
||||
else:
|
||||
if builder.item_details:
|
||||
builder.update_item_details()
|
||||
if builder.custom_sort:
|
||||
builder.sort_collection()
|
||||
|
||||
builder.send_notifications(playlist=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
except NotScheduled as e:
|
||||
util.print_multiline(e, info=True)
|
||||
except Failed as e:
|
||||
config.notify(e, server=server_name, library=library_names, playlist=mapping_name)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
except Exception as e:
|
||||
config.notify(f"Unknown Error: {e}", server=server_name, library=library_names, playlist=mapping_name)
|
||||
util.print_stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
logger.info("")
|
||||
util.separator(f"Finished {mapping_name} Playlist\nPlaylist Run Time: {str(datetime.now() - playlist_start).split('.')[0]}")
|
||||
logger.removeHandler(playlist_handler)
|
||||
run_playlists(config)
|
||||
logger.removeHandler(playlists_handler)
|
||||
|
||||
has_run_again = False
|
||||
|
@ -1077,6 +789,320 @@ def run_collection(config, library, metadata, requested_collections):
|
|||
util.separator(f"Finished {mapping_name} Collection\nCollection Run Time: {str(datetime.now() - collection_start).split('.')[0]}")
|
||||
logger.removeHandler(collection_handler)
|
||||
|
||||
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()
|
||||
if config.test_mode and ("test" not in playlist_attrs or playlist_attrs["test"] is not True):
|
||||
no_template_test = True
|
||||
if "template" in playlist_attrs and playlist_attrs["template"]:
|
||||
for data_template in util.get_list(playlist_attrs["template"], split=False):
|
||||
if "name" in data_template \
|
||||
and data_template["name"] \
|
||||
and playlist_file.templates \
|
||||
and data_template["name"] in playlist_file.templates \
|
||||
and playlist_file.templates[data_template["name"]] \
|
||||
and "test" in playlist_file.templates[data_template["name"]] \
|
||||
and playlist_file.templates[data_template["name"]]["test"] is True:
|
||||
no_template_test = False
|
||||
if no_template_test:
|
||||
continue
|
||||
|
||||
if "name_mapping" in playlist_attrs and playlist_attrs["name_mapping"]:
|
||||
playlist_log_name, output_str = util.validate_filename(playlist_attrs["name_mapping"])
|
||||
else:
|
||||
playlist_log_name, output_str = util.validate_filename(mapping_name)
|
||||
playlist_log_folder = os.path.join(default_dir, "logs", "playlists", playlist_log_name)
|
||||
os.makedirs(playlist_log_folder, exist_ok=True)
|
||||
ply_file_logger = os.path.join(playlist_log_folder, "playlist.log")
|
||||
should_roll_over = os.path.isfile(ply_file_logger)
|
||||
playlist_handler = RotatingFileHandler(ply_file_logger, delay=True, mode="w", backupCount=3,
|
||||
encoding="utf-8")
|
||||
util.apply_formatter(playlist_handler)
|
||||
if should_roll_over:
|
||||
playlist_handler.doRollover()
|
||||
logger.addHandler(playlist_handler)
|
||||
server_name = None
|
||||
library_names = None
|
||||
try:
|
||||
util.separator(f"{mapping_name} Playlist")
|
||||
logger.info("")
|
||||
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_user"]
|
||||
if "sync_to_users" not in playlist_attrs:
|
||||
logger.warning(f"Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_user: {sync_to_users}")
|
||||
elif not playlist_attrs["sync_to_users"]:
|
||||
logger.warning(f"Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_user: {sync_to_users}")
|
||||
else:
|
||||
sync_to_users = playlist_attrs["sync_to_users"]
|
||||
|
||||
valid_users = []
|
||||
plex_users = pl_libraries[0].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)
|
||||
logger.info("")
|
||||
|
||||
util.separator(f"Running {mapping_name} Playlist", space=False, border=False)
|
||||
|
||||
if len(builder.schedule) > 0:
|
||||
util.print_multiline(builder.schedule, info=True)
|
||||
|
||||
items_added = 0
|
||||
items_removed = 0
|
||||
valid = True
|
||||
logger.info("")
|
||||
logger.info(f"Sync Mode: {'sync' if builder.sync else 'append'}")
|
||||
|
||||
if builder.filters or builder.tmdb_filters:
|
||||
logger.info("")
|
||||
for filter_key, filter_value in builder.filters:
|
||||
logger.info(f"Playlist Filter {filter_key}: {filter_value}")
|
||||
for filter_key, filter_value in builder.tmdb_filters:
|
||||
logger.info(f"Playlist Filter {filter_key}: {filter_value}")
|
||||
|
||||
method, value = builder.builders[0]
|
||||
logger.debug("")
|
||||
logger.debug(f"Builder: {method}: {value}")
|
||||
logger.info("")
|
||||
items = []
|
||||
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.error(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.error(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)
|
||||
|
||||
if len(builder.added_items) >= builder.minimum:
|
||||
logger.info("")
|
||||
util.separator(f"Adding to {mapping_name} Playlist", space=False, border=False)
|
||||
logger.info("")
|
||||
items_added = builder.add_to_collection()
|
||||
stats["added"] += items_added
|
||||
items_removed = 0
|
||||
if builder.sync:
|
||||
items_removed = builder.sync_collection()
|
||||
stats["removed"] += items_removed
|
||||
elif len(builder.added_items) < builder.minimum:
|
||||
logger.info("")
|
||||
logger.info(f"Playlist Minimum: {builder.minimum} not met for {mapping_name} Playlist")
|
||||
valid = False
|
||||
if builder.details["delete_below_minimum"] and builder.obj:
|
||||
builder.delete_playlist(valid_users)
|
||||
builder.deleted = True
|
||||
|
||||
if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
|
||||
if builder.details["show_missing"] is True:
|
||||
logger.info("")
|
||||
util.separator(f"Missing from Library", space=False, border=False)
|
||||
logger.info("")
|
||||
radarr_add, sonarr_add = builder.run_missing()
|
||||
stats["radarr"] += radarr_add
|
||||
stats["sonarr"] += sonarr_add
|
||||
|
||||
run_item_details = True
|
||||
try:
|
||||
builder.load_collection()
|
||||
if builder.created:
|
||||
stats["created"] += 1
|
||||
elif items_added > 0 or items_removed > 0:
|
||||
stats["modified"] += 1
|
||||
except Failed:
|
||||
util.print_stacktrace()
|
||||
run_item_details = False
|
||||
logger.info("")
|
||||
util.separator("No Playlist to Update", space=False, border=False)
|
||||
else:
|
||||
builder.update_details()
|
||||
|
||||
if builder.deleted:
|
||||
stats["deleted"] += 1
|
||||
|
||||
if valid and run_item_details and builder.builders and (builder.item_details or builder.custom_sort):
|
||||
try:
|
||||
builder.load_collection_items()
|
||||
except Failed:
|
||||
logger.info("")
|
||||
util.separator("No Items Found", space=False, border=False)
|
||||
else:
|
||||
if builder.item_details:
|
||||
builder.update_item_details()
|
||||
if builder.custom_sort:
|
||||
builder.sort_collection()
|
||||
|
||||
if valid:
|
||||
builder.sync_playlist(valid_users)
|
||||
|
||||
builder.send_notifications(playlist=True)
|
||||
|
||||
except NotScheduled as e:
|
||||
util.print_multiline(e, info=True)
|
||||
except Failed as e:
|
||||
config.notify(e, server=server_name, library=library_names, playlist=mapping_name)
|
||||
util.print_stacktrace()
|
||||
util.print_multiline(e, error=True)
|
||||
except Exception as e:
|
||||
config.notify(f"Unknown Error: {e}", server=server_name, library=library_names, playlist=mapping_name)
|
||||
util.print_stacktrace()
|
||||
logger.error(f"Unknown Error: {e}")
|
||||
logger.info("")
|
||||
util.separator(
|
||||
f"Finished {mapping_name} Playlist\nPlaylist Run Time: {str(datetime.now() - playlist_start).split('.')[0]}")
|
||||
logger.removeHandler(playlist_handler)
|
||||
|
||||
|
||||
try:
|
||||
if run or test or collections or libraries or resume:
|
||||
start({
|
||||
|
|
Loading…
Reference in a new issue