diff --git a/README.md b/README.md index ea6f78a3..e13ac1c8 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The script is designed to work with most Metadata agents including the new Plex ## Support -* Before posting on Github about an enhancement, error, or configuration question please visit the [Plex Meta Manager Discord Server](https://discord.gg/NfH6mGFuAB). +* Before posting on Github about an enhancement, error, or configuration question please visit the [Plex Meta Manager Discord Server](https://discord.gg/TsdpsFYqqm). * If you're getting an Error or have an Enhancement post in the [Issues](https://github.com/meisnate12/Plex-Meta-Manager/issues). * If you have a configuration question post in the [Discussions](https://github.com/meisnate12/Plex-Meta-Manager/discussions). * To see user submitted Metadata configuration files, and you to even add your own, go to the [Plex Meta Manager Configs](https://github.com/meisnate12/Plex-Meta-Manager-Configs). diff --git a/modules/plex.py b/modules/plex.py index 768032a4..98809443 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -324,6 +324,7 @@ class PlexAPI: self.Sonarr = None self.Tautulli = None self.name = params["name"] + self.mapping_name = util.validate_filename(params["mapping_name"]) self.missing_path = os.path.join(params["default_dir"], f"{self.name}_missing.yml") self.metadata_path = params["metadata_path"] self.asset_directory = params["asset_directory"] diff --git a/modules/util.py b/modules/util.py index a91cc145..993b444b 100644 --- a/modules/util.py +++ b/modules/util.py @@ -1,5 +1,6 @@ import logging, re, signal, sys, time, traceback from datetime import datetime +from pathvalidate import is_valid_filename, sanitize_filename from plexapi.exceptions import BadRequest, NotFound, Unauthorized try: @@ -378,7 +379,7 @@ def separator(text=None): def apply_formatter(handler, border=True): text = f"| %(message)-{screen_width - 2}s |" if border else f"%(message)-{screen_width - 2}s" - if not isinstance(handler, logging.StreamHandler): + if isinstance(handler, logging.handlers.RotatingFileHandler): text = f"[%(asctime)s] %(filename)-27s %(levelname)-10s {text}" handler.setFormatter(logging.Formatter(text)) @@ -389,3 +390,11 @@ def print_return(length, text): def print_end(length, text=None): if text: logger.info(adjust_space(length, text)) else: print(adjust_space(length, " "), end="\r") + +def validate_filename(filename): + if is_valid_filename(filename): + return filename + else: + mapping_name = sanitize_filename(filename) + logger.info(f"Folder Name: {filename} is invalid using {mapping_name}") + return mapping_name diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 43a77aa7..2c2a614e 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -74,29 +74,32 @@ def fmt_filter(record): record.filename = f"[{record.filename}:{record.lineno}]" return True -file_handler = logging.handlers.TimedRotatingFileHandler(os.path.join(default_dir, "logs", "meta.log"), when="midnight", backupCount=10, encoding="utf-8") -file_handler.addFilter(fmt_filter) - cmd_handler = logging.StreamHandler() cmd_handler.setLevel(logging.DEBUG if test or debug else logging.INFO) logger.addHandler(cmd_handler) -logger.addHandler(file_handler) sys.excepthook = util.my_except_hook -util.separator() -util.centered(" ") -util.centered(" ____ _ __ __ _ __ __ ") -util.centered("| _ \\| | _____ __ | \\/ | ___| |_ __ _ | \\/ | __ _ _ __ __ _ __ _ ___ _ __ ") -util.centered("| |_) | |/ _ \\ \\/ / | |\\/| |/ _ \\ __/ _` | | |\\/| |/ _` | '_ \\ / _` |/ _` |/ _ \\ '__|") -util.centered("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | ") -util.centered("|_| |_|\\___/_/\\_\\ |_| |_|\\___|\\__\\__,_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| ") -util.centered(" |___/ ") -util.centered(" Version: 1.9.1 ") -util.separator() - def start(config_path, is_test, daily, requested_collections, requested_libraries, resume_from): + file_logger = os.path.join(default_dir, "logs", "meta.log") + should_roll_over = os.path.isfile(file_logger) + file_handler = logging.handlers.RotatingFileHandler(file_logger, delay=True, mode="w", backupCount=10, encoding="utf-8") + util.apply_formatter(file_handler) + file_handler.addFilter(fmt_filter) + if should_roll_over: + file_handler.doRollover() + logger.addHandler(file_handler) + util.separator() + util.centered(" ") + util.centered(" ____ _ __ __ _ __ __ ") + util.centered("| _ \\| | _____ __ | \\/ | ___| |_ __ _ | \\/ | __ _ _ __ __ _ __ _ ___ _ __ ") + util.centered("| |_) | |/ _ \\ \\/ / | |\\/| |/ _ \\ __/ _` | | |\\/| |/ _` | '_ \\ / _` |/ _` |/ _ \\ '__|") + util.centered("| __/| | __/> < | | | | __/ || (_| | | | | | (_| | | | | (_| | (_| | __/ | ") + util.centered("|_| |_|\\___/_/\\_\\ |_| |_|\\___|\\__\\__,_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| ") + util.centered(" |___/ ") + util.centered(" Version: 1.9.1 ") + util.separator() if daily: start_type = "Daily " elif is_test: start_type = "Test " elif requested_collections: start_type = "Collections " @@ -112,9 +115,20 @@ def start(config_path, is_test, daily, requested_collections, requested_librarie logger.critical(e) logger.info("") util.separator(f"Finished {start_type}Run\nRun Time: {str(datetime.now() - start_time).split('.')[0]}") + logger.addHandler(file_handler) def update_libraries(config, is_test, requested_collections, resume_from): for library in config.libraries: + os.makedirs(os.path.join(default_dir, "logs", library.mapping_name, "collections"), exist_ok=True) + col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, "library.log") + should_roll_over = os.path.isfile(col_file_logger) + library_handler = logging.handlers.RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8") + util.apply_formatter(library_handler) + library_handler.addFilter(fmt_filter) + if should_roll_over: + library_handler.doRollover() + logger.addHandler(library_handler) + os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout) logger.info("") util.separator(f"{library.name} Library") @@ -139,7 +153,9 @@ def update_libraries(config, is_test, requested_collections, resume_from): logger.warning(f"Collection: {resume_from} not in Metadata File: {metadata.path}") continue if collections_to_run and not library_only: + logger.removeHandler(library_handler) resume_from = run_collection(config, library, metadata, collections_to_run, is_test, resume_from, movie_map, show_map) + logger.addHandler(library_handler) if not is_test and not requested_collections: unmanaged_collections = [] @@ -164,6 +180,7 @@ def update_libraries(config, is_test, requested_collections, resume_from): for item in library.get_all(): library.update_item_from_assets(item) + logger.removeHandler(library_handler) has_run_again = False for library in config.libraries: @@ -183,6 +200,11 @@ def update_libraries(config, is_test, requested_collections, resume_from): util.print_end(length) for library in config.libraries: if library.run_again: + col_file_logger = os.path.join(default_dir, "logs", library.mapping_name, f"library.log") + library_handler = logging.handlers.RotatingFileHandler(col_file_logger, mode="w", backupCount=3, encoding="utf-8") + util.apply_formatter(library_handler) + logger.addHandler(library_handler) + library_handler.addFilter(fmt_filter) os.environ["PLEXAPI_PLEXAPI_TIMEOUT"] = str(library.timeout) logger.info("") util.separator(f"{library.name} Library Run Again") @@ -197,6 +219,7 @@ def update_libraries(config, is_test, requested_collections, resume_from): except Failed as e: util.print_stacktrace() util.print_multiline(e, error=True) + logger.removeHandler(library_handler) used_url = [] for library in config.libraries: @@ -355,6 +378,21 @@ def run_collection(config, library, metadata, requested_collections, is_test, re logger.info("") util.separator(f"Resuming Collections") + if "name_mapping" in collection_attrs and collection_attrs["name_mapping"]: + collection_log_name = util.validate_filename(collection_attrs["name_mapping"]) + else: + collection_log_name = util.validate_filename(mapping_name) + collection_log_folder = os.path.join(default_dir, "logs", library.mapping_name, "collections", collection_log_name) + os.makedirs(collection_log_folder, exist_ok=True) + col_file_logger = os.path.join(collection_log_folder, f"collection.log") + should_roll_over = os.path.isfile(col_file_logger) + collection_handler = logging.handlers.RotatingFileHandler(col_file_logger, delay=True, mode="w", backupCount=3, encoding="utf-8") + util.apply_formatter(collection_handler) + collection_handler.addFilter(fmt_filter) + if should_roll_over: + collection_handler.doRollover() + logger.addHandler(collection_handler) + try: util.separator(f"{mapping_name} Collection") logger.info("") @@ -396,6 +434,7 @@ def run_collection(config, library, metadata, requested_collections, is_test, re util.print_stacktrace() logger.error(f"Unknown Error: {e}") logger.info("") + logger.removeHandler(collection_handler) return resume_from try: diff --git a/requirements.txt b/requirements.txt index b53e2dfa..0fcb4c69 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ requests>=2.4.2 ruamel.yaml schedule retrying +pathvalidate