#238 Summary by Collection

This commit is contained in:
meisnate12 2021-12-30 16:29:16 -05:00
parent 9ee0bf829c
commit ee2039039c
4 changed files with 119 additions and 23 deletions

View file

@ -1660,6 +1660,7 @@ class CollectionBuilder:
total = len(self.added_items) total = len(self.added_items)
spacing = len(str(total)) * 2 + 1 spacing = len(str(total)) * 2 + 1
amount_added = 0 amount_added = 0
amount_unchanged = 0
playlist_adds = [] playlist_adds = []
for i, item in enumerate(self.added_items, 1): for i, item in enumerate(self.added_items, 1):
current_operation = "=" if item in collection_items else "+" current_operation = "=" if item in collection_items else "+"
@ -1667,6 +1668,7 @@ class CollectionBuilder:
logger.info(util.adjust_space(f"{number_text:>{spacing}} | {name} {self.Type} | {current_operation} | {util.item_title(item)}")) logger.info(util.adjust_space(f"{number_text:>{spacing}} | {name} {self.Type} | {current_operation} | {util.item_title(item)}"))
if item in collection_items: if item in collection_items:
self.plex_map[item.ratingKey] = None self.plex_map[item.ratingKey] = None
amount_unchanged += 1
else: else:
if self.playlist: if self.playlist:
playlist_adds.append(item) playlist_adds.append(item)
@ -1690,7 +1692,7 @@ class CollectionBuilder:
util.print_end() util.print_end()
logger.info("") logger.info("")
logger.info(f"{total} {self.collection_level.capitalize()}{'s' if total > 1 else ''} Processed") logger.info(f"{total} {self.collection_level.capitalize()}{'s' if total > 1 else ''} Processed")
return amount_added return amount_added, amount_unchanged
def sync_collection(self): def sync_collection(self):
amount_removed = 0 amount_removed = 0
@ -2326,6 +2328,7 @@ class CollectionBuilder:
name, collection_items = self.library.get_collection_name_and_items(self.obj, self.smart_label_collection) name, collection_items = self.library.get_collection_name_and_items(self.obj, self.smart_label_collection)
self.created = False self.created = False
rating_keys = [] rating_keys = []
amount_added = 0
self.notification_additions = [] self.notification_additions = []
for mm in self.run_again_movies: for mm in self.run_again_movies:
if mm in self.library.movie_map: if mm in self.library.movie_map:
@ -2345,6 +2348,7 @@ class CollectionBuilder:
logger.info(f"{name} {self.Type} | = | {util.item_title(current)}") logger.info(f"{name} {self.Type} | = | {util.item_title(current)}")
else: else:
self.library.alter_collection(current, name, smart_label_collection=self.smart_label_collection) self.library.alter_collection(current, name, smart_label_collection=self.smart_label_collection)
amount_added += 1
logger.info(f"{name} {self.Type} | + | {util.item_title(current)}") logger.info(f"{name} {self.Type} | + | {util.item_title(current)}")
if self.library.is_movie and current.ratingKey in self.library.movie_rating_key_map: if self.library.is_movie and current.ratingKey in self.library.movie_rating_key_map:
add_id = self.library.movie_rating_key_map[current.ratingKey] add_id = self.library.movie_rating_key_map[current.ratingKey]
@ -2383,3 +2387,5 @@ class CollectionBuilder:
if self.details["show_missing"] is True: if self.details["show_missing"] is True:
logger.info(f"{name} {self.Type} | ? | {title} (TVDb: {missing_id})") logger.info(f"{name} {self.Type} | ? | {title} (TVDb: {missing_id})")
logger.info(f"{len(self.run_again_shows)} Show{'s' if len(self.run_again_shows) > 1 else ''} Missing") logger.info(f"{len(self.run_again_shows)} Show{'s' if len(self.run_again_shows) > 1 else ''} Missing")
return amount_added

View file

@ -80,6 +80,8 @@ class Library(ABC):
self.clean_bundles = params["plex"]["clean_bundles"] # TODO: Here or just in Plex? self.clean_bundles = params["plex"]["clean_bundles"] # TODO: Here or just in Plex?
self.empty_trash = params["plex"]["empty_trash"] # TODO: Here or just in Plex? self.empty_trash = params["plex"]["empty_trash"] # TODO: Here or just in Plex?
self.optimize = params["plex"]["optimize"] # TODO: Here or just in Plex? self.optimize = params["plex"]["optimize"] # TODO: Here or just in Plex?
self.stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
self.status = {}
self.tmdb_library_operation = self.assets_for_all or self.mass_genre_update or self.mass_audience_rating_update \ self.tmdb_library_operation = self.assets_for_all or self.mass_genre_update or self.mass_audience_rating_update \
or self.mass_critic_rating_update or self.mass_trakt_rating_update \ or self.mass_critic_rating_update or self.mass_trakt_rating_update \

View file

@ -1,6 +1,6 @@
import logging import logging
from json import JSONDecodeError from json import JSONDecodeError
from modules import util
from modules.util import Failed from modules.util import Failed
logger = logging.getLogger("Plex Meta Manager") logger = logging.getLogger("Plex Meta Manager")
@ -16,6 +16,7 @@ class Webhooks:
def _request(self, webhooks, json): def _request(self, webhooks, json):
if self.config.trace_mode: if self.config.trace_mode:
util.separator("Webhooks", space=False, border=False)
logger.debug("") logger.debug("")
logger.debug(f"JSON: {json}") logger.debug(f"JSON: {json}")
for webhook in list(set(webhooks)): for webhook in list(set(webhooks)):

View file

@ -72,7 +72,6 @@ divider = get_arg("PMM_DIVIDER", args.divider)
screen_width = get_arg("PMM_WIDTH", args.width, arg_int=True) screen_width = get_arg("PMM_WIDTH", args.width, arg_int=True)
debug = get_arg("PMM_DEBUG", args.debug, arg_bool=True) debug = get_arg("PMM_DEBUG", args.debug, arg_bool=True)
trace = get_arg("PMM_TRACE", args.trace, arg_bool=True) trace = get_arg("PMM_TRACE", args.trace, arg_bool=True)
stats = {}
util.separating_character = divider[0] util.separating_character = divider[0]
@ -163,8 +162,7 @@ def start(attrs):
logger.debug("") logger.debug("")
util.separator(f"Starting {start_type}Run") util.separator(f"Starting {start_type}Run")
config = None config = None
global stats stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "removed": 0, "radarr": 0, "sonarr": 0}
try: try:
config = ConfigFile(default_dir, attrs, read_only_config) config = ConfigFile(default_dir, attrs, read_only_config)
except Exception as e: except Exception as e:
@ -172,7 +170,7 @@ def start(attrs):
util.print_multiline(e, critical=True) util.print_multiline(e, critical=True)
else: else:
try: try:
update_libraries(config) stats = update_libraries(config)
except Exception as e: except Exception as e:
config.notify(e) config.notify(e)
util.print_stacktrace() util.print_stacktrace()
@ -190,7 +188,6 @@ def start(attrs):
logger.removeHandler(file_handler) logger.removeHandler(file_handler)
def update_libraries(config): def update_libraries(config):
global stats
for library in config.libraries: for library in config.libraries:
if library.skip_library: if library.skip_library:
logger.info("") logger.info("")
@ -278,6 +275,8 @@ def update_libraries(config):
util.print_stacktrace() util.print_stacktrace()
util.print_multiline(e, critical=True) util.print_multiline(e, critical=True)
playlist_status = {}
playlist_stats = {}
if config.playlist_files: if config.playlist_files:
os.makedirs(os.path.join(default_dir, "logs", "playlists"), exist_ok=True) os.makedirs(os.path.join(default_dir, "logs", "playlists"), exist_ok=True)
pf_file_logger = os.path.join(default_dir, "logs", "playlists", "playlists.log") pf_file_logger = os.path.join(default_dir, "logs", "playlists", "playlists.log")
@ -287,7 +286,7 @@ def update_libraries(config):
if should_roll_over: if should_roll_over:
playlists_handler.doRollover() playlists_handler.doRollover()
logger.addHandler(playlists_handler) logger.addHandler(playlists_handler)
run_playlists(config) playlist_status, playlist_stats = run_playlists(config)
logger.removeHandler(playlists_handler) logger.removeHandler(playlists_handler)
has_run_again = False has_run_again = False
@ -296,6 +295,7 @@ def update_libraries(config):
has_run_again = True has_run_again = True
break break
amount_added = 0
if has_run_again and not library_only: if has_run_again and not library_only:
logger.info("") logger.info("")
util.separator("Run Again") util.separator("Run Again")
@ -320,10 +320,10 @@ def update_libraries(config):
library.map_guids() library.map_guids()
for builder in library.run_again: for builder in library.run_again:
logger.info("") logger.info("")
util.separator(f"{builder.name} Collection") util.separator(f"{builder.name} Collection in {library.name}")
logger.info("") logger.info("")
try: try:
builder.run_collections_again() amount_added += builder.run_collections_again()
except Failed as e: except Failed as e:
library.notify(e, collection=builder.name, critical=False) library.notify(e, collection=builder.name, critical=False)
util.print_stacktrace() util.print_stacktrace()
@ -345,6 +345,55 @@ def update_libraries(config):
if library.optimize: if library.optimize:
library.query(library.PlexServer.library.optimize) library.query(library.PlexServer.library.optimize)
longest = 20
for library in config.libraries:
for title in library.status:
if len(title) > longest:
longest = len(title)
if playlist_status:
for title in playlist_status:
if len(title) > longest:
longest = len(title)
def print_status(section, status):
logger.info("")
util.separator(f"{section} Summary", space=False, border=False)
logger.info("")
for name, data in status.items():
logger.info(f"{name:<{longest}} | {data['status']:<13} | +{data['added']} ={data['unchanged']} -{data['removed']}")
if data["errors"]:
for error in data["errors"]:
util.print_multiline(error, info=True)
logger.info("")
util.separator("Summary")
for library in config.libraries:
print_status(library.name, library.status)
if playlist_status:
print_status("Playlists", playlist_status)
stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
stats["added"] += amount_added
for library in config.libraries:
stats["created"] += library.stats["created"]
stats["modified"] += library.stats["modified"]
stats["deleted"] += library.stats["deleted"]
stats["added"] += library.stats["added"]
stats["unchanged"] += library.stats["unchanged"]
stats["removed"] += library.stats["removed"]
stats["radarr"] += library.stats["radarr"]
stats["sonarr"] += library.stats["sonarr"]
if playlist_stats:
stats["created"] += playlist_stats["created"]
stats["modified"] += playlist_stats["modified"]
stats["deleted"] += playlist_stats["deleted"]
stats["added"] += playlist_stats["added"]
stats["unchanged"] += playlist_stats["unchanged"]
stats["removed"] += playlist_stats["removed"]
stats["radarr"] += playlist_stats["radarr"]
stats["sonarr"] += playlist_stats["sonarr"]
return stats
def library_operations(config, library): def library_operations(config, library):
logger.info("") logger.info("")
util.separator(f"{library.name} Library Operations") util.separator(f"{library.name} Library Operations")
@ -614,7 +663,6 @@ def library_operations(config, library):
library.find_assets(col) library.find_assets(col)
def run_collection(config, library, metadata, requested_collections): def run_collection(config, library, metadata, requested_collections):
global stats
logger.info("") logger.info("")
for mapping_name, collection_attrs in requested_collections.items(): for mapping_name, collection_attrs in requested_collections.items():
collection_start = datetime.now() collection_start = datetime.now()
@ -653,6 +701,7 @@ def run_collection(config, library, metadata, requested_collections):
if should_roll_over: if should_roll_over:
collection_handler.doRollover() collection_handler.doRollover()
logger.addHandler(collection_handler) logger.addHandler(collection_handler)
library.status[mapping_name] = {"status": "", "errors": [], "created": False, "modified": False, "deleted": False, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
try: try:
util.separator(f"{mapping_name} Collection in {library.name}") util.separator(f"{mapping_name} Collection in {library.name}")
@ -692,12 +741,16 @@ def run_collection(config, library, metadata, requested_collections):
builder.find_rating_keys() builder.find_rating_keys()
if len(builder.added_items) >= builder.minimum and builder.build_collection: if len(builder.added_items) >= builder.minimum and builder.build_collection:
items_added = builder.add_to_collection() items_added, items_unchanged = builder.add_to_collection()
stats["added"] += items_added library.stats["added"] += items_added
library.status[mapping_name]["added"] = items_added
library.stats["unchanged"] += items_unchanged
library.status[mapping_name]["unchanged"] = items_unchanged
items_removed = 0 items_removed = 0
if builder.sync: if builder.sync:
items_removed = builder.sync_collection() items_removed = builder.sync_collection()
stats["removed"] += items_removed library.stats["removed"] += items_removed
library.status[mapping_name]["removed"] = items_removed
elif len(builder.added_items) < builder.minimum and builder.build_collection: elif len(builder.added_items) < builder.minimum and builder.build_collection:
logger.info("") logger.info("")
logger.info(f"Collection Minimum: {builder.minimum} not met for {mapping_name} Collection") logger.info(f"Collection Minimum: {builder.minimum} not met for {mapping_name} Collection")
@ -709,17 +762,21 @@ def run_collection(config, library, metadata, requested_collections):
if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0): if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
radarr_add, sonarr_add = builder.run_missing() radarr_add, sonarr_add = builder.run_missing()
stats["radarr"] += radarr_add library.stats["radarr"] += radarr_add
stats["sonarr"] += sonarr_add library.status[mapping_name]["radarr"] += radarr_add
library.stats["sonarr"] += sonarr_add
library.status[mapping_name]["sonarr"] += sonarr_add
run_item_details = True run_item_details = True
if valid and builder.build_collection and (builder.builders or builder.smart_url): if valid and builder.build_collection and (builder.builders or builder.smart_url):
try: try:
builder.load_collection() builder.load_collection()
if builder.created: if builder.created:
stats["created"] += 1 library.stats["created"] += 1
library.status[mapping_name]["created"] = True
elif items_added > 0 or items_removed > 0: elif items_added > 0 or items_removed > 0:
stats["modified"] += 1 library.stats["modified"] += 1
library.status[mapping_name]["modified"] = True
except Failed: except Failed:
util.print_stacktrace() util.print_stacktrace()
run_item_details = False run_item_details = False
@ -729,7 +786,8 @@ def run_collection(config, library, metadata, requested_collections):
builder.update_details() builder.update_details()
if builder.deleted: if builder.deleted:
stats["deleted"] += 1 library.stats["deleted"] += 1
library.status[mapping_name]["deleted"] = True
if builder.server_preroll is not None: if builder.server_preroll is not None:
library.set_server_preroll(builder.server_preroll) library.set_server_preroll(builder.server_preroll)
@ -754,21 +812,36 @@ def run_collection(config, library, metadata, requested_collections):
if builder.run_again and (len(builder.run_again_movies) > 0 or len(builder.run_again_shows) > 0): if builder.run_again and (len(builder.run_again_movies) > 0 or len(builder.run_again_shows) > 0):
library.run_again.append(builder) library.run_again.append(builder)
if library.status[mapping_name]["created"]:
library.status[mapping_name]["status"] = "Created"
elif library.status[mapping_name]["deleted"]:
library.status[mapping_name]["status"] = "Deleted"
elif library.status[mapping_name]["modified"]:
library.status[mapping_name]["status"] = "Modified"
else:
library.status[mapping_name]["status"] = "Unchanged"
except NotScheduled as e: except NotScheduled as e:
util.print_multiline(e, info=True) util.print_multiline(e, info=True)
library.status[mapping_name]["status"] = "Not Scheduled"
except Failed as e: except Failed as e:
library.notify(e, collection=mapping_name) library.notify(e, collection=mapping_name)
util.print_stacktrace() util.print_stacktrace()
util.print_multiline(e, error=True) util.print_multiline(e, error=True)
library.status[mapping_name]["status"] = "PMM Failure"
library.status[mapping_name]["errors"].append(e)
except Exception as e: except Exception as e:
library.notify(f"Unknown Error: {e}", collection=mapping_name) library.notify(f"Unknown Error: {e}", collection=mapping_name)
util.print_stacktrace() util.print_stacktrace()
logger.error(f"Unknown Error: {e}") logger.error(f"Unknown Error: {e}")
library.status[mapping_name]["status"] = "Unknown Error"
library.status[mapping_name]["errors"].append(e)
logger.info("") logger.info("")
util.separator(f"Finished {mapping_name} Collection\nCollection Run Time: {str(datetime.now() - collection_start).split('.')[0]}") util.separator(f"Finished {mapping_name} Collection\nCollection Run Time: {str(datetime.now() - collection_start).split('.')[0]}")
logger.removeHandler(collection_handler) logger.removeHandler(collection_handler)
def run_playlists(config): def run_playlists(config):
stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
status = {}
logger.info("") logger.info("")
util.separator("Playlists") util.separator("Playlists")
logger.info("") logger.info("")
@ -805,6 +878,7 @@ def run_playlists(config):
if should_roll_over: if should_roll_over:
playlist_handler.doRollover() playlist_handler.doRollover()
logger.addHandler(playlist_handler) logger.addHandler(playlist_handler)
status[mapping_name] = {"status": "", "errors": [], "created": False, "modified": False, "deleted": False, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0}
server_name = None server_name = None
library_names = None library_names = None
try: try:
@ -1004,12 +1078,16 @@ def run_playlists(config):
builder.filter_and_save_items(items) builder.filter_and_save_items(items)
if len(builder.added_items) >= builder.minimum: if len(builder.added_items) >= builder.minimum:
items_added = builder.add_to_collection() items_added, items_unchanged = builder.add_to_collection()
stats["added"] += items_added stats["added"] += items_added
status[mapping_name]["added"] += items_added
stats["unchanged"] += items_unchanged
status[mapping_name]["unchanged"] += items_unchanged
items_removed = 0 items_removed = 0
if builder.sync: if builder.sync:
items_removed = builder.sync_collection() items_removed = builder.sync_collection()
stats["removed"] += items_removed stats["removed"] += items_removed
status[mapping_name]["removed"] += items_removed
elif len(builder.added_items) < builder.minimum: elif len(builder.added_items) < builder.minimum:
logger.info("") logger.info("")
logger.info(f"Playlist Minimum: {builder.minimum} not met for {mapping_name} Playlist") logger.info(f"Playlist Minimum: {builder.minimum} not met for {mapping_name} Playlist")
@ -1022,15 +1100,19 @@ def run_playlists(config):
if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0): if builder.do_missing and (len(builder.missing_movies) > 0 or len(builder.missing_shows) > 0):
radarr_add, sonarr_add = builder.run_missing() radarr_add, sonarr_add = builder.run_missing()
stats["radarr"] += radarr_add stats["radarr"] += radarr_add
status[mapping_name]["radarr"] += radarr_add
stats["sonarr"] += sonarr_add stats["sonarr"] += sonarr_add
status[mapping_name]["sonarr"] += sonarr_add
run_item_details = True run_item_details = True
try: try:
builder.load_collection() builder.load_collection()
if builder.created: if builder.created:
stats["created"] += 1 stats["created"] += 1
status[mapping_name]["created"] = True
elif items_added > 0 or items_removed > 0: elif items_added > 0 or items_removed > 0:
stats["modified"] += 1 stats["modified"] += 1
status[mapping_name]["modified"] = True
except Failed: except Failed:
util.print_stacktrace() util.print_stacktrace()
run_item_details = False run_item_details = False
@ -1041,6 +1123,7 @@ def run_playlists(config):
if builder.deleted: if builder.deleted:
stats["deleted"] += 1 stats["deleted"] += 1
status[mapping_name]["deleted"] = True
if valid and run_item_details and builder.builders and (builder.item_details or builder.custom_sort): if valid and run_item_details and builder.builders and (builder.item_details or builder.custom_sort):
try: try:
@ -1061,19 +1144,23 @@ def run_playlists(config):
except NotScheduled as e: except NotScheduled as e:
util.print_multiline(e, info=True) util.print_multiline(e, info=True)
status[mapping_name]["status"] = "Not Scheduled"
except Failed as e: except Failed as e:
config.notify(e, server=server_name, library=library_names, playlist=mapping_name) config.notify(e, server=server_name, library=library_names, playlist=mapping_name)
util.print_stacktrace() util.print_stacktrace()
util.print_multiline(e, error=True) util.print_multiline(e, error=True)
status[mapping_name]["status"] = "PMM Failure"
status[mapping_name]["errors"].append(e)
except Exception as e: except Exception as e:
config.notify(f"Unknown Error: {e}", server=server_name, library=library_names, playlist=mapping_name) config.notify(f"Unknown Error: {e}", server=server_name, library=library_names, playlist=mapping_name)
util.print_stacktrace() util.print_stacktrace()
logger.error(f"Unknown Error: {e}") logger.error(f"Unknown Error: {e}")
status[mapping_name]["status"] = "Unknown Error"
status[mapping_name]["errors"].append(e)
logger.info("") logger.info("")
util.separator( util.separator(f"Finished {mapping_name} Playlist\nPlaylist Run Time: {str(datetime.now() - playlist_start).split('.')[0]}")
f"Finished {mapping_name} Playlist\nPlaylist Run Time: {str(datetime.now() - playlist_start).split('.')[0]}")
logger.removeHandler(playlist_handler) logger.removeHandler(playlist_handler)
return status, stats
try: try:
if run or test or collections or libraries or resume: if run or test or collections or libraries or resume: