From 51a8089d4b0201e8e369c4677a211931aebe35bf Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 11:40:13 -0700 Subject: [PATCH 01/10] initial From e2ff676a602bd5c358ba953c1e40f5e281eb1f64 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 12:06:22 -0700 Subject: [PATCH 02/10] added safety check logic --- repair.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/repair.py b/repair.py index 16eacf5..6ff458e 100644 --- a/repair.py +++ b/repair.py @@ -67,6 +67,29 @@ def main(): getItems = lambda media, childId: arr.getFiles(media=media, childId=childId) if args.mode == 'symlink' else arr.getHistory(media=media, childId=childId, includeGrandchildDetails=True) childrenIds = media.childrenIds if args.include_unmonitored else media.monitoredChildrenIds + # perform a "Safety check" to make sure that the torrents folder is mounted to prevent deleting everything in *arrs + # we do this inside of the loop because the mount could drop at any time; so, best to check with each iteration + mount_torrents_path = realdebrid['mountTorrentsPath'] + if not os.path.exists(mount_torrents_path): + print(f"Path to mounted torrents does not exist: {mount_torrents_path}") + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Path to mounted torrents does not exist: {mount_torrents_path}", e) + break + # use a custom shell command or just count the subfolders within the mount to make sure the count is >0 + safety_check_command = os.getenv('MOUNT_SAFETY_CHECK_COMMAND', f'[ $(find {mount_torrents_path} -mindepth 1 -maxdepth 1 -type d | wc -l) -gt 0 ] && echo "true" || echo "false"') + + try: + result = subprocess.run(safety_check_command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + safety_check_failed = result.stdout.strip().decode('utf-8') == 'false' + + if safety_check_failed: + print(f"Safety check failed: couldn't verify torrent folder is mounted: {mount_torrents_path}") + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted: {mount_torrents_path}", e) + break + + except subprocess.CalledProcessError as e: + print(f"Error running safety check command: {e}") + break + for childId in childrenIds: brokenItems = [] childItems = list(getItems(media=media, childId=childId)) From 9a9c87e308f017c5f7739bdb4b58f8d3c0199e7b Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 12:11:26 -0700 Subject: [PATCH 03/10] add missing import for subprocess --- repair.py | 1 + 1 file changed, 1 insertion(+) diff --git a/repair.py b/repair.py index 6ff458e..5a8798e 100644 --- a/repair.py +++ b/repair.py @@ -1,4 +1,5 @@ import os +import subprocess import argparse import time import traceback From ebedbb66dbdb0101e0b1ad7525cae62d8cf39e92 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 12:19:30 -0700 Subject: [PATCH 04/10] fixed discordError arguments --- repair.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repair.py b/repair.py index 5a8798e..84ce7dc 100644 --- a/repair.py +++ b/repair.py @@ -73,7 +73,7 @@ def main(): mount_torrents_path = realdebrid['mountTorrentsPath'] if not os.path.exists(mount_torrents_path): print(f"Path to mounted torrents does not exist: {mount_torrents_path}") - discordError(f"[{args.mode}] An error occurred while processing {media.title}: Path to mounted torrents does not exist: {mount_torrents_path}", e) + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Path to mounted torrents does not exist", mount_torrents_path) break # use a custom shell command or just count the subfolders within the mount to make sure the count is >0 safety_check_command = os.getenv('MOUNT_SAFETY_CHECK_COMMAND', f'[ $(find {mount_torrents_path} -mindepth 1 -maxdepth 1 -type d | wc -l) -gt 0 ] && echo "true" || echo "false"') @@ -84,7 +84,7 @@ def main(): if safety_check_failed: print(f"Safety check failed: couldn't verify torrent folder is mounted: {mount_torrents_path}") - discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted: {mount_torrents_path}", e) + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted", mount_torrents_path) break except subprocess.CalledProcessError as e: From 8126ef6909e79ccd3c8bf14f992d5cbcda790619 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 17:59:04 -0700 Subject: [PATCH 05/10] refactored into function and removed environment variable config for now --- repair.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/repair.py b/repair.py index 84ce7dc..c36aaaa 100644 --- a/repair.py +++ b/repair.py @@ -55,6 +55,15 @@ def print(*values: object): print(f"Invalid interval format for run interval: {args.run_interval}") exit(1) +def safety_check(mount_torrents_path): + try: + folder_count = len([name for name in os.listdir(mount_torrents_path) if os.path.isdir(os.path.join(mount_torrents_path, name))]) + return folder_count > 0 + + except Exception as e: + print(f"Error checking torrent mount path: {e}") + return False + def main(): print("Collecting media...") sonarr = Sonarr() @@ -70,26 +79,12 @@ def main(): # perform a "Safety check" to make sure that the torrents folder is mounted to prevent deleting everything in *arrs # we do this inside of the loop because the mount could drop at any time; so, best to check with each iteration - mount_torrents_path = realdebrid['mountTorrentsPath'] - if not os.path.exists(mount_torrents_path): - print(f"Path to mounted torrents does not exist: {mount_torrents_path}") - discordError(f"[{args.mode}] An error occurred while processing {media.title}: Path to mounted torrents does not exist", mount_torrents_path) - break - # use a custom shell command or just count the subfolders within the mount to make sure the count is >0 - safety_check_command = os.getenv('MOUNT_SAFETY_CHECK_COMMAND', f'[ $(find {mount_torrents_path} -mindepth 1 -maxdepth 1 -type d | wc -l) -gt 0 ] && echo "true" || echo "false"') - - try: - result = subprocess.run(safety_check_command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - safety_check_failed = result.stdout.strip().decode('utf-8') == 'false' - - if safety_check_failed: - print(f"Safety check failed: couldn't verify torrent folder is mounted: {mount_torrents_path}") - discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted", mount_torrents_path) - break - - except subprocess.CalledProcessError as e: - print(f"Error running safety check command: {e}") - break + if not safety_check(realdebrid['mountTorrentsPath']): + print(f"Safety check failed: couldn't verify torrent folder is mounted: {realdebrid['mountTorrentsPath']}") + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted", realdebrid['mountTorrentsPath']) + if repairIntervalSeconds > 0: + time.sleep(repairIntervalSeconds) + continue for childId in childrenIds: brokenItems = [] From 3b9c03b57b33abe0fb826fa8318de3a4ae32ec13 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 18:09:08 -0700 Subject: [PATCH 06/10] added environment variable option back in --- repair.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/repair.py b/repair.py index c36aaaa..0f8ebb8 100644 --- a/repair.py +++ b/repair.py @@ -57,11 +57,13 @@ def print(*values: object): def safety_check(mount_torrents_path): try: - folder_count = len([name for name in os.listdir(mount_torrents_path) if os.path.isdir(os.path.join(mount_torrents_path, name))]) - return folder_count > 0 - + # Default script to check if the number of directories is greater than zero + default_script = "len([name for name in os.listdir(mount_torrents_path) if os.path.isdir(os.path.join(mount_torrents_path, name))]) > 0" + safety_check_script = os.getenv('SAFETY_CHECK_SCRIPT', default_script) + safety_check_passed = eval(safety_check_script) + return safety_check_passed except Exception as e: - print(f"Error checking torrent mount path: {e}") + print(f"Error checking torrent mount or evaluating safety check script: {e}") return False def main(): From 0206525c3347309a83b45f490413c5f354e50c44 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 19:37:04 -0700 Subject: [PATCH 07/10] changed to python eval with default script --- repair.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/repair.py b/repair.py index 0f8ebb8..706f043 100644 --- a/repair.py +++ b/repair.py @@ -55,15 +55,15 @@ def print(*values: object): print(f"Invalid interval format for run interval: {args.run_interval}") exit(1) -def safety_check(mount_torrents_path): +def safety_check(mountTorrentsPath): try: # Default script to check if the number of directories is greater than zero - default_script = "len([name for name in os.listdir(mount_torrents_path) if os.path.isdir(os.path.join(mount_torrents_path, name))]) > 0" + default_script = "len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0" safety_check_script = os.getenv('SAFETY_CHECK_SCRIPT', default_script) - safety_check_passed = eval(safety_check_script) + safety_check_passed = eval(safety_check_script, {"os": os, "mountTorrentsPath": mountTorrentsPath}) return safety_check_passed except Exception as e: - print(f"Error checking torrent mount or evaluating safety check script: {e}") + print(f"Error checking torrent mount or evaluating safety check script: {safety_check_script} exception: {e}") return False def main(): @@ -81,9 +81,9 @@ def main(): # perform a "Safety check" to make sure that the torrents folder is mounted to prevent deleting everything in *arrs # we do this inside of the loop because the mount could drop at any time; so, best to check with each iteration - if not safety_check(realdebrid['mountTorrentsPath']): - print(f"Safety check failed: couldn't verify torrent folder is mounted: {realdebrid['mountTorrentsPath']}") - discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted", realdebrid['mountTorrentsPath']) + if ((realdebrid['enabled'] and not safety_check(realdebrid['mountTorrentsPath'])) or (torbox['enabled'] and not safety_check(torbox['mountTorrentsPath']))): + print(f"Safety check failed: couldn't verify torrent folder is mounted") + discordError(f"[{args.mode}] An error occurred while processing {media.title}: Safety check failed: couldn't verify torrent folder is mounted") if repairIntervalSeconds > 0: time.sleep(repairIntervalSeconds) continue From 77db52d19a3edfc9479bb7d558102f923bf01eb0 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 19:51:04 -0700 Subject: [PATCH 08/10] first attempt at command line args and default environment variable usage --- repair.py | 7 ++++--- shared/shared.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/repair.py b/repair.py index 706f043..dff1fd3 100644 --- a/repair.py +++ b/repair.py @@ -31,6 +31,7 @@ def parseInterval(intervalStr): parser.add_argument('--run-interval', type=str, default=repair['runInterval'], help='Optional interval in smart format (e.g. 1w2d3h4m5s) to run the repair process.') parser.add_argument('--mode', type=str, choices=['symlink', 'file'], default='symlink', help='Choose repair mode: `symlink` or `file`. `symlink` to repair broken symlinks and `file` to repair missing files.') parser.add_argument('--include-unmonitored', action='store_true', help='Include unmonitored media in the repair process') +parser.add_argument('--safety-check-script', type=str, default=repair['safetyCheckScript'], help='Python code to be evaluated and return boolean for whether torrent mount is working or not. mountTorrentsPath will be replaced by Realdebrid/Torbox mount path.') args = parser.parse_args() _print = print @@ -58,9 +59,9 @@ def print(*values: object): def safety_check(mountTorrentsPath): try: # Default script to check if the number of directories is greater than zero - default_script = "len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0" - safety_check_script = os.getenv('SAFETY_CHECK_SCRIPT', default_script) - safety_check_passed = eval(safety_check_script, {"os": os, "mountTorrentsPath": mountTorrentsPath}) + #default_script = "len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0" + #safety_check_script = os.getenv('SAFETY_CHECK_SCRIPT', default_script) + safety_check_passed = eval(args.safety_check_script, {"os": os, "mountTorrentsPath": mountTorrentsPath}) return safety_check_passed except Exception as e: print(f"Error checking torrent mount or evaluating safety check script: {safety_check_script} exception: {e}") diff --git a/shared/shared.py b/shared/shared.py index c8c20e8..0fe24e4 100644 --- a/shared/shared.py +++ b/shared/shared.py @@ -89,7 +89,8 @@ def stringEnvParser(value): repair = { 'repairInterval': env.string('REPAIR_REPAIR_INTERVAL', default=None), - 'runInterval': env.string('REPAIR_RUN_INTERVAL', default=None) + 'runInterval': env.string('REPAIR_RUN_INTERVAL', default=None), + 'safetyCheckScript': env.string('SAFETY_CHECK_SCRIPT', default="len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0") } plexHeaders = { From 3bebdf2d1d3e918b103b4fbf84beda4173887050 Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 20:10:55 -0700 Subject: [PATCH 09/10] fixed command line args and defaults --- repair.py | 5 +---- shared/shared.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/repair.py b/repair.py index dff1fd3..f5f2df3 100644 --- a/repair.py +++ b/repair.py @@ -58,13 +58,10 @@ def print(*values: object): def safety_check(mountTorrentsPath): try: - # Default script to check if the number of directories is greater than zero - #default_script = "len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0" - #safety_check_script = os.getenv('SAFETY_CHECK_SCRIPT', default_script) safety_check_passed = eval(args.safety_check_script, {"os": os, "mountTorrentsPath": mountTorrentsPath}) return safety_check_passed except Exception as e: - print(f"Error checking torrent mount or evaluating safety check script: {safety_check_script} exception: {e}") + print(f"Error checking torrent mount or evaluating safety check script [{safety_check_script}] for mount path [{mountTorrentsPath}] : exception: {e}") return False def main(): diff --git a/shared/shared.py b/shared/shared.py index 0fe24e4..419c52f 100644 --- a/shared/shared.py +++ b/shared/shared.py @@ -90,7 +90,7 @@ def stringEnvParser(value): repair = { 'repairInterval': env.string('REPAIR_REPAIR_INTERVAL', default=None), 'runInterval': env.string('REPAIR_RUN_INTERVAL', default=None), - 'safetyCheckScript': env.string('SAFETY_CHECK_SCRIPT', default="len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0") + 'safetyCheckScript': env.string('SAFETY_CHECK_SCRIPT', default='len([name for name in os.listdir(mountTorrentsPath) if os.path.isdir(os.path.join(mountTorrentsPath, name))]) > 0') } plexHeaders = { From b2708d39ecf492f989525effcedd31a6912e9e7f Mon Sep 17 00:00:00 2001 From: Dan Secrist Date: Tue, 30 Jul 2024 20:19:21 -0700 Subject: [PATCH 10/10] removed unneeded import --- repair.py | 1 - 1 file changed, 1 deletion(-) diff --git a/repair.py b/repair.py index f5f2df3..080e39d 100644 --- a/repair.py +++ b/repair.py @@ -1,5 +1,4 @@ import os -import subprocess import argparse import time import traceback