From fb0760acdef471432a33b79906bd5910a406de44 Mon Sep 17 00:00:00 2001 From: David Maisonave <47364845+David-Maisonave@users.noreply.github.com> Date: Sun, 18 Aug 2024 07:43:55 -0400 Subject: [PATCH] Separated example and unit test task to separate files. --- plugins/FileMonitor/filemonitor.py | 240 ++++++++++-------- plugins/FileMonitor/filemonitor_config.py | 85 +------ .../FileMonitor/filemonitor_self_unit_test.py | 42 +++ .../FileMonitor/filemonitor_task_examples.py | 49 ++++ 4 files changed, 229 insertions(+), 187 deletions(-) create mode 100644 plugins/FileMonitor/filemonitor_self_unit_test.py create mode 100644 plugins/FileMonitor/filemonitor_task_examples.py diff --git a/plugins/FileMonitor/filemonitor.py b/plugins/FileMonitor/filemonitor.py index b2d8de0..ecc2d5d 100644 --- a/plugins/FileMonitor/filemonitor.py +++ b/plugins/FileMonitor/filemonitor.py @@ -9,7 +9,13 @@ import watchdog # pip install watchdog # https://pythonhosted.org/watchdog/ from watchdog.observers import Observer # This is also needed for event attributes from threading import Lock, Condition from multiprocessing import shared_memory -from filemonitor_config import config # Import settings from filemonitor_config.py +from filemonitor_config import config +from filemonitor_task_examples import task_examples +from filemonitor_self_unit_test import self_unit_test + +config['task_scheduler'] = config['task_scheduler'] + task_examples['task_scheduler'] +if self_unit_test['selfUnitTest']: + config['task_scheduler'] = config['task_scheduler'] + self_unit_test['task_scheduler'] CONTINUE_RUNNING_SIG = 99 STOP_RUNNING_SIG = 32 @@ -74,11 +80,6 @@ fileExtTypes = stash.pluginConfig['fileExtTypes'].split(",") if stash.pluginConf includePathChanges = stash.pluginConfig['includePathChanges'] if len(stash.pluginConfig['includePathChanges']) > 0 else stash.STASH_PATHS excludePathChanges = stash.pluginConfig['excludePathChanges'] - -stash.Trace(f"(apiKey={stash.API_KEY})") -stash.Trace(f"(includePathChanges={includePathChanges})") - - if stash.DRY_RUN: stash.Log("Dry run mode is enabled.") stash.Trace(f"(SCAN_MODIFIED={SCAN_MODIFIED}) (SCAN_ON_ANY_EVENT={SCAN_ON_ANY_EVENT}) (RECURSIVE={RECURSIVE})") @@ -88,7 +89,6 @@ StartFileMonitorAsAServiceTaskName = "Start Library Monitor Service" StartFileMonitorAsAPluginTaskID = "start_library_monitor" StartFileMonitorAsAServiceTaskID = "start_library_monitor_service" - FileMonitorPluginIsOnTaskQue = stash.CALLED_AS_STASH_PLUGIN StopLibraryMonitorWaitingInTaskQueue = False JobIdInTheQue = 0 @@ -211,25 +211,7 @@ class StashScheduler: # Stash Scheduler elif task['task'] == "Generate": result = stash.metadata_generate() elif task['task'] == "Backup": - stash.LogOnce("Note: Backup task does not get listed in the Task Queue, but user can verify that it started by looking in the Stash log file as an INFO level log line.") - result = stash.backup_database() - maximumBackup = stash.pluginSettings['zmaximumBackups'] - stash.Trace(f"maximumBackup={maximumBackup}") - if "maxBackups" in task: - maximumBackup = task['maxBackups'] - stash.Trace(f"maximumBackup={maximumBackup}") - if isinstance(maximumBackup,str): - maximumBackup = int(maximumBackup) - if maximumBackup < 2: - stash.TraceOnce(f"Skipping DB backup file trim because zmaximumBackups={maximumBackup}. Value has to be greater than 1.") - elif 'backupDirectoryPath' in stash.STASH_CONFIGURATION: - if len(stash.STASH_CONFIGURATION['backupDirectoryPath']) < 5: - stash.TraceOnce(f"Skipping DB backup file trim because backupDirectoryPath length is to short. Len={len(stash.STASH_CONFIGURATION['backupDirectoryPath'])}. Only support length greater than 4 characters.") - elif os.path.exists(stash.STASH_CONFIGURATION['backupDirectoryPath']): - stash.LogOnce(f"Checking quantity of DB backups if path {stash.STASH_CONFIGURATION['backupDirectoryPath']} exceeds {maximumBackup} backup files.") - self.trimDbFiles(stash.STASH_CONFIGURATION['backupDirectoryPath'], maximumBackup) - else: - stash.TraceOnce(f"Skipping DB backup file trim because backupDirectoryPath does NOT exist. backupDirectoryPath={stash.STASH_CONFIGURATION['backupDirectoryPath']}") + result = self.runBackupTask(task) elif task['task'] == "Scan": result = stash.metadata_scan(paths=targetPaths) elif task['task'] == "Auto Tag": @@ -261,94 +243,138 @@ class StashScheduler: # Stash Scheduler Msg = task['msg'] result = stash.TraceOnce(Msg) elif task['task'] == "CheckStashIsRunning": - try: - result = stash.stash_version() - except: - pass - # Note: Can not call stash.Error if Stash is not running, because that might throw another exception. - stash.Trace("Failed to get response from Stash.") - if platform.system() == "Windows": - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-win.exe"] - elif platform.system() == "Darwin": # MacOS - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep} stash-macos "] - elif platform.system().lower().startswith("linux"): - # ToDo: Need to verify this method will work for (stash-linux-arm32v6, stash-linux-arm32v7, and stash-linux-arm64v8) - if platform.system().lower().find("32v6") > -1: - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm32v6"] - elif platform.system().lower().find("32v7") > -1: - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm32v7"] - elif platform.system().lower().find("64v8 ") > -1: - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm64v8"] - else: - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux"] - elif platform.system().lower().startswith("freebsd"): - args = [f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-freebsd"] - elif 'command' not in task or task['command'] == "": - stash.Trace("Error: Can not start Stash, because failed to determine platform OS. As a workaround, add 'command' field to this task.") - return - if 'command' in task and task['command'] != "": - cmd = task['command'].replace("", f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}") - args = [cmd] - result = f"Execute process PID = {stash.ExecuteProcess(args)}" - time.sleep(10) - if "RunAfter" in task and len(task['RunAfter']) > 0: - for runAfterTask in task['RunAfter']: - self.runTask(runAfterTask) + result = self.checkStashIsRunning(task) elif task['task'] == "python": - if 'script' in task and task['script'] != "": - script = task['script'].replace("", f"{pathlib.Path(__file__).resolve().parent}{os.sep}") - stash.Log(f"Executing python script {script}.") - args = [script] - if 'args' in task and len(task['args']) > 0: - args = args + [task['args']] - detached = True - if 'detach' in task: - detached = task['detach'] - result = f"Python process PID = {stash.ExecutePythonScript(args, ExecDetach=detached)}" - else: - stash.Error(f"Can not run task '{task['task']}', because it's missing 'script' field.") + result = self.runPythonScript(task) elif task['task'] == "execute": - if 'command' in task and task['command'] != "": - cmd = task['command'].replace("", f"{pathlib.Path(__file__).resolve().parent}{os.sep}") - args = [cmd] - if 'args' in task and len(task['args']) > 0: - args = args + [task['args']] - stash.Log(f"Executing command arguments {args}.") - result = f"Execute process PID = {stash.ExecuteProcess(args)}" - else: - stash.Error(f"Can not run task '{task['task']}', because it's missing 'command' field.") + result = self.runExecuteProcessTask(task) else: - # ToDo: Add code to check if plugin is installed. - try: - if 'pluginId' in task and task['pluginId'] != "": - invalidDir = False - validDirMsg = "" - if 'validateDir' in task and task['validateDir'] != "": - invalidDir = True - communityPluginPath = f"{stash.PLUGINS_PATH}{os.sep}community{os.sep}{task['validateDir']}" - basePluginPath = f"{stash.PLUGINS_PATH}{os.sep}{task['validateDir']}" - if os.path.exists(communityPluginPath): - invalidDir = False - validDirMsg = f"Valid path in {communityPluginPath}" - elif os.path.exists(basePluginPath): - invalidDir = False - validDirMsg = f"Valid path in {basePluginPath}" - if invalidDir: - stash.Error(f"Could not run task '{task['task']}' because sub directory '{task['validateDir']}' does not exist under path '{stash.PLUGINS_PATH}'") - else: - stash.Trace(f"Running plugin task pluginID={task['pluginId']}, task name = {task['task']}. {validDirMsg}") - stash.run_plugin_task(plugin_id=task['pluginId'], task_name=task['task']) - else: - stash.Error(f"Can not run task '{task['task']}', because it's an invalid task.") - stash.LogOnce(f"If task '{task['task']}' is supposed to be a built-in task, check for correct task name spelling.") - stash.LogOnce(f"If task '{task['task']}' is supposed to be a plugin, make sure to include the pluginId field in the task. task={task}") - except Exception as e: - stash.LogOnce(f"Failed to call plugin {task['task']} with plugin-ID {task['pluginId']}. Error: {e}") - pass + result = self.runPluginTask(task) if result: stash.Trace(f"Task '{task['task']}' result={result}") + def runExecuteProcessTask(self, task): + if 'command' in task and task['command'] != "": + cmd = task['command'].replace("", f"{pathlib.Path(__file__).resolve().parent}{os.sep}") + args = [cmd] + if 'args' in task and len(task['args']) > 0: + args = args + [task['args']] + stash.Log(f"Executing command arguments {args}.") + return f"Execute process PID = {stash.ExecuteProcess(args)}" + else: + stash.Error(f"Can not run task '{task['task']}', because it's missing 'command' field.") + return None + + def runPythonScript(self, task): + if 'script' in task and task['script'] != "": + script = task['script'].replace("", f"{pathlib.Path(__file__).resolve().parent}{os.sep}") + stash.Log(f"Executing python script {script}.") + args = [script] + if 'args' in task and len(task['args']) > 0: + args = args + [task['args']] + detached = True + if 'detach' in task: + detached = task['detach'] + return f"Python process PID = {stash.ExecutePythonScript(args, ExecDetach=detached)}" + else: + stash.Error(f"Can not run task '{task['task']}', because it's missing 'script' field.") + return None + + def runPluginTask(self, task): + # ToDo: Add code to check if plugin is installed. + try: + if 'pluginId' in task and task['pluginId'] != "": + invalidDir = False + validDirMsg = "" + if 'validateDir' in task and task['validateDir'] != "": + invalidDir = True + communityPluginPath = f"{stash.PLUGINS_PATH}{os.sep}community{os.sep}{task['validateDir']}" + basePluginPath = f"{stash.PLUGINS_PATH}{os.sep}{task['validateDir']}" + if os.path.exists(communityPluginPath): + invalidDir = False + validDirMsg = f"Valid path in {communityPluginPath}" + elif os.path.exists(basePluginPath): + invalidDir = False + validDirMsg = f"Valid path in {basePluginPath}" + if invalidDir: + stash.Error(f"Could not run task '{task['task']}' because sub directory '{task['validateDir']}' does not exist under path '{stash.PLUGINS_PATH}'") + else: + stash.Trace(f"Running plugin task pluginID={task['pluginId']}, task name = {task['task']}. {validDirMsg}") + return stash.run_plugin_task(plugin_id=task['pluginId'], task_name=task['task']) + else: + stash.Error(f"Can not run task '{task['task']}', because it's an invalid task.") + stash.LogOnce(f"If task '{task['task']}' is supposed to be a built-in task, check for correct task name spelling.") + stash.LogOnce(f"If task '{task['task']}' is supposed to be a plugin, make sure to include the pluginId field in the task. task={task}") + except Exception as e: + stash.LogOnce(f"Failed to call plugin {task['task']} with plugin-ID {task['pluginId']}. Error: {e}") + pass + return None + + def checkStashIsRunning(self, task): + try: + result = stash.stash_version() + except: + pass + stash.Error("Failed to get response from Stash.") + if platform.system() == "Windows": + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-win.exe" + elif platform.system() == "Darwin": # MacOS + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep} stash-macos " + elif platform.system().lower().startswith("linux"): + # ToDo: Need to verify this method will work for (stash-linux-arm32v6, stash-linux-arm32v7, and stash-linux-arm64v8) + if platform.system().lower().find("32v6") > -1: + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm32v6" + elif platform.system().lower().find("32v7") > -1: + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm32v7" + elif platform.system().lower().find("64v8 ") > -1: + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux-arm64v8" + else: + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-linux" + elif platform.system().lower().startswith("freebsd"): + execPath = f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}stash-freebsd" + elif 'command' not in task or task['command'] == "": + stash.Error("Can not start Stash, because failed to determine platform OS. As a workaround, add 'command' field to this task.") + return None + + if 'command' in task and task['command'] != "": + cmd = task['command'].replace("", f"{pathlib.Path(stash.PLUGINS_PATH).resolve().parent}{os.sep}") + args = [cmd] + else: + if os.path.isfile(execPath): + args = [execPath] + else: + stash.Error("Could not start Stash, because could not find executable Stash file '{execPath}'") + return None + result = f"Execute process PID = {stash.ExecuteProcess(args)}" + time.sleep(10) + if "RunAfter" in task and len(task['RunAfter']) > 0: + for runAfterTask in task['RunAfter']: + self.runTask(runAfterTask) + return result + + def runBackupTask(self, task): + stash.LogOnce("Note: Backup task does not get listed in the Task Queue, but user can verify that it started by looking in the Stash log file as an INFO level log line.") + result = stash.backup_database() + maximumBackup = stash.pluginSettings['zmaximumBackups'] + stash.Trace(f"maximumBackup={maximumBackup}") + if "maxBackups" in task: + maximumBackup = task['maxBackups'] + stash.Trace(f"maximumBackup={maximumBackup}") + if isinstance(maximumBackup,str): + maximumBackup = int(maximumBackup) + if maximumBackup < 2: + stash.TraceOnce(f"Skipping DB backup file trim because zmaximumBackups={maximumBackup}. Value has to be greater than 1.") + elif 'backupDirectoryPath' in stash.STASH_CONFIGURATION: + if len(stash.STASH_CONFIGURATION['backupDirectoryPath']) < 5: + stash.TraceOnce(f"Skipping DB backup file trim because backupDirectoryPath length is to short. Len={len(stash.STASH_CONFIGURATION['backupDirectoryPath'])}. Only support length greater than 4 characters.") + elif os.path.exists(stash.STASH_CONFIGURATION['backupDirectoryPath']): + stash.LogOnce(f"Checking quantity of DB backups if path {stash.STASH_CONFIGURATION['backupDirectoryPath']} exceeds {maximumBackup} backup files.") + self.trimDbFiles(stash.STASH_CONFIGURATION['backupDirectoryPath'], maximumBackup) + else: + stash.TraceOnce(f"Skipping DB backup file trim because backupDirectoryPath does NOT exist. backupDirectoryPath={stash.STASH_CONFIGURATION['backupDirectoryPath']}") + return result + def trimDbFiles(self, dbPath, maxFiles): if not os.path.exists(dbPath): stash.LogOnce(f"Exiting trimDbFiles, because path {dbPath} does not exists.") diff --git a/plugins/FileMonitor/filemonitor_config.py b/plugins/FileMonitor/filemonitor_config.py index 7741efb..96563d5 100644 --- a/plugins/FileMonitor/filemonitor_config.py +++ b/plugins/FileMonitor/filemonitor_config.py @@ -8,7 +8,8 @@ config = { # The [Auto Tag] task is an example of a daily scheduled task. # The [Generate] task is an example of a weekly scheduled task. # The [Backup] task is an example of a monthly scheduled task. - # Note: The hour section in time MUST be a two digit number, and use military time format. Example: 1PM = "13:00" and 1AM = "01:00" + # The hour section in time MUST be a two digit number, and use military time format. Example: 1PM = "13:00" and 1AM = "01:00" + # Note: Look at filemonitor_task_examples.py for many example task having more detailed usage. "task_scheduler": [ # To create a daily task, include each day of the week for the weekday field. {"task" : "Auto Tag", "weekday" : "monday,tuesday,wednesday,thursday,friday,saturday,sunday", "time" : "06:00"}, # Auto Tag -> [Auto Tag] (Daily at 6AM) @@ -32,86 +33,10 @@ config = { {"task" : "Clean", "weekday" : "sunday", "time" : "01:00", "monthly" : 3}, # Maintenance -> [Clean] {"task" : "Clean Generated Files", "weekday" : "sunday", "time" : "03:00", "monthly" : 3}, # Maintenance -> [Clean Generated Files] - # The [CheckStashIsRunning] task checks if Stash is running. If it's not, it will start up stash. This task only works if FileMonitor is started as a service or in command line mode. + # The [CheckStashIsRunning] task checks if Stash is running. If not running, it will start up stash. + # This task only works if FileMonitor is started as a service or in command line mode. + # For more detailed usage, see examples #C1 and #C2 in filemonitor_task_examples.py {"task" : "CheckStashIsRunning", "minutes" :5}, # Checks every 5 minutes - - # Example#C1 Some OS may need the "command" field, which specifies the binary path - {"task" : "CheckStashIsRunning", "command" : "stash-linux-arm64v8", "minutes" :0}, - # Example#C2 RunAfter field can be used to specify task to run after starting Stash - {"task" : "CheckStashIsRunning", "RunAfter" : [{"task" : "Scan"},{"task" : "Backup", "maxBackup" : 0},{"task" : "Clean"}], "minutes" :0}, - - # Example#A1: Task to call call_GQL API with custom input - {"task" : "GQL", "input" : "mutation OptimiseDatabase { optimiseDatabase }", "weekday" : "sunday", "time" : "DISABLED"}, # To enable, change "DISABLED" to valid time - - # Example#A2: Task to call a python script. When this task is executed, the keyword is replaced by filemonitor.py current directory. - # The args field is NOT required. - {"task" : "python", "script" : "test_script_hello_world.py", "args" : "--MyArguments Hello", "weekday" : "monday", "time" : "DISABLED"}, # change "DISABLED" to valid time - - # Example#A3: The following task types can optionally take a [paths] field. If the paths field does not exists, the paths in the Stash library is used. - {"task" : "Scan", "paths" : [r"E:\MyVideos\downloads", r"V:\MyOtherVideos"], "weekday" : "sunday", "time" : "DISABLED"}, # Library -> [Scan] - {"task" : "Auto Tag", "paths" : [r"E:\MyVideos\downloads", r"V:\MyOtherVideos"], "weekday" : "monday,tuesday,wednesday,thursday,friday,saturday,sunday", "time" : "DISABLED"}, # Auto Tag -> [Auto Tag] - {"task" : "Clean", "paths" : ["E:\\MyVideos\\downloads", "V:\\MyOtherVideos"], "weekday" : "sunday", "time" : "DISABLED"}, # Generated Content-> [Generate] - - # Example#A4: Task which calls Migrations -> [Rename generated files] - {"task" : "RenameGeneratedFiles", "weekday" : "tuesday,thursday", "time" : "DISABLED"}, # (bi-weekly) example - - # Example#A5: The Backup task using optional field maxBackup, which overrides the UI [Max DB Backups] value - {"task" : "Backup", "maxBackup" : 12, "weekday" : "sunday", "time" : "DISABLED"}, # Trim the DB backup files down to 12 backup files. - {"task" : "Backup", "maxBackup" : 0, "weekday" : "sunday", "time" : "DISABLED"}, # When used with a zero value, it will make sure no file trimming will occur no matter the value of the UI [Max DB Backups] - - # The above weekday method is the more reliable method to schedule task, because it doesn't rely on FileMonitor running continuously (non-stop). - - # The below examples use frequency field method which can work with minutes and hours. A zero frequency value disables the task. - # Note: Both seconds and days are also supported for the frequency field. - # However, seconds is mainly used for test purposes. - # And days usage is discourage, because it only works if FileMonitor is running for X many days non-stop. - # The below example tasks are done using hours and minutes, however any of these task types can be converted to a daily, weekly, or monthly syntax. - - # Example#B1: The following task is the syntax used for a plugin. A plugin task requires the plugin name for the [task] field, and the plugin-ID for the [pluginId] field. - {"task" : "PluginButtonName_Here", "pluginId" : "PluginId_Here", "hours" : 0}, # The zero frequency value makes this task disabled. - # Example#B2: Optionally, the validateDir field can be included which is used to validate that the plugin is installed either under the plugins folder or under the plugins-community folder. - {"task" : "PluginButtonName_Here", "pluginId" : "PluginId_Here", "validateDir" : "UsuallySameAsPluginID", "hours" : 0}, # The zero frequency value makes this task disabled. - - # Example#B3: Task to execute a command - {"task" : "execute", "command" : "C:\\MyPath\\HelloWorld.bat", "hours" : 0}, - - # Example#B4: Task to execute a command with optional args field, and using keyword , which gets replaced with filemonitor.py current directory. - {"task" : "execute", "command" : "HelloWorld.cmd", "args" : "--name David", "minutes" : 0}, - - # Comment out **test** tasks. - # To run test, enable all task, and start FileMonitor as a service. - # When executed, these task should be seen in the Task Queue unless otherwise stated in comments. - # These tasks are usually executed before updating major releases on https://github.com/David-Maisonave/Axter-Stash/blob/main/plugins/FileMonitor - # These tasks are ALWAYS executed before updating to https://github.com/stashapp/CommunityScripts - # MUST ToDo: Always comment out below test task before checking in this code!!! - # {"task" : "TestBadTaskNameError", "minutes" : 1}, # Test invalid task name - # {"task" : "execute", "minutes" : 1}, # Test invalid task (missing command) - # {"task" : "python", "minutes" : 1}, # Test invalid task (missing scripts) - # {"task" : "PluginWithOutID", "minutes" : 1}, # Test invalid task (missing pluginId) - # {"task" : "execute", "command" : "", "minutes" : 1}, # Test invalid task (missing command) - # {"task" : "python", "script" : "", "minutes" : 1}, # Test invalid task (missing scripts) - # {"task" : "PluginWithOutID", "pluginId" : "", "minutes" : 1}, # Test invalid task (missing pluginId) - # {"task" : "Foo","pluginId":"foo","validateDir":"foo", "minutes" : 1}, # Test invalid task (missing plugin directory) - # {"task" : "Log", "msg" : "Testing Scheduled Log", "minutes" : 1}, # Test plugin log file - # {"task" : "Trace", "minutes" : 1}, # Test plugin trace logging - # {"task" : "LogOnce", "seconds" :15}, # Test LogOnce - # {"task" : "TraceOnce", "seconds" : 5}, # Test TraceOnce - # {"task" : "CheckStashIsRunning", "RunAfter" : [{"task" : "Scan"},{"task" : "Backup", "maxBackup" : 0},{"task" : "Clean"}], "seconds" :15}, # Test RunAfter - # {"task" : "CheckStashIsRunning", "command" : "stash-win.exe", "seconds" :10}, # Check if Stash is running. If not running, start up Stash. - # {"task" : "Generate", "weekday" : "friday", "time" : "12:03"}, - # {"task" : "Clean", "weekday" : "friday", "time" : "12:03"}, - # {"task" : "Auto Tag", "weekday" : "friday", "time" : "12:03"}, - # {"task" : "Optimise Database", "weekday" : "friday", "time" : "12:03"}, - # {"task" : "Create Tags", "pluginId" : "pathParser", "validateDir" : "pathParser", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Running plugin task: Create Tags - # {"task" : "Scan","paths": [r"B:\_\SpecialSet", r"C:\foo"], "weekday" : "friday", "time" : "12:03"}, - # {"task" : "GQL", "input" : "mutation OptimiseDatabase { optimiseDatabase }", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Optimising database... - # {"task" : "Clean Generated Files", "weekday" : "friday", "time" : "12:03"}, - # {"task" : "RenameGeneratedFiles", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Migrating scene hashes... - # {"task" : "Backup", "maxBackups" : 0, "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Must check STASH log file to verify run. - # {"task" : "python", "script" : "test_hello_world2.py", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'python' result=??? - # {"task" : "python", "script" : "test_hello_world.py", "detach" : False, "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'python' result=??? - # {"task" : "execute", "command" : "test_hello_world2.cmd", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'execute' result=??? - # {"task" : "execute", "command" : "test_hello_world.bat", "args" : "--name David", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'execute' result=??? ], # ApiKey only needed when Stash credentials are set and while calling FileMonitor via command line. diff --git a/plugins/FileMonitor/filemonitor_self_unit_test.py b/plugins/FileMonitor/filemonitor_self_unit_test.py new file mode 100644 index 0000000..c30311f --- /dev/null +++ b/plugins/FileMonitor/filemonitor_self_unit_test.py @@ -0,0 +1,42 @@ +# **test** tasks which are disabled by default. To enable test tasks, set selfUnitTest to True. +# To run test, enable all task, and start FileMonitor as a service. +# When executed, these task should be seen in the Task Queue unless otherwise stated in comments. +# These tasks are usually executed before updating major releases on https://github.com/David-Maisonave/Axter-Stash/blob/main/plugins/FileMonitor +# These tasks are ALWAYS executed before updating to https://github.com/stashapp/CommunityScripts +self_unit_test = { + "task_scheduler": [ + {"task" : "TestBadTaskNameError", "minutes" : 1}, # Test invalid task name + {"task" : "execute", "minutes" : 1}, # Test invalid task (missing command) + {"task" : "python", "minutes" : 1}, # Test invalid task (missing scripts) + {"task" : "PluginWithOutID", "minutes" : 1}, # Test invalid task (missing pluginId) + {"task" : "execute", "command" : "", "minutes" : 1}, # Test invalid task (missing command) + {"task" : "python", "script" : "", "minutes" : 1}, # Test invalid task (missing scripts) + {"task" : "PluginWithOutID", "pluginId" : "", "minutes" : 1}, # Test invalid task (missing pluginId) + {"task" : "Foo","pluginId":"foo","validateDir":"foo", "minutes" : 1}, # Test invalid task (missing plugin directory) + {"task" : "Log", "msg" : "Testing Scheduled Log", "minutes" : 1}, # Test plugin log file + {"task" : "Trace", "minutes" : 1}, # Test plugin trace logging + {"task" : "LogOnce", "seconds" :15}, # Test LogOnce + {"task" : "TraceOnce", "seconds" : 5}, # Test TraceOnce + # {"task" : "CheckStashIsRunning", "RunAfter" : [{"task" : "Scan"}], "seconds" :15}, # To test CheckStashIsRunning, kill Stash after starting FileMonitor service via following command:taskkill /F /IM "stash-win.exe" + {"task" : "CheckStashIsRunning", "RunAfter" : [{"task" : "Scan"},{"task" : "Backup", "maxBackup" : 0},{"task" : "Clean"}], "seconds" :15}, # Test RunAfter + {"task" : "CheckStashIsRunning", "command" : "stash-win.exe", "seconds" :10}, # Check if Stash is running. If not running, start up Stash. + {"task" : "Generate", "weekday" : "friday", "time" : "12:03"}, + {"task" : "Clean", "weekday" : "friday", "time" : "12:03"}, + {"task" : "Auto Tag", "weekday" : "friday", "time" : "12:03"}, + {"task" : "Optimise Database", "weekday" : "friday", "time" : "12:03"}, + {"task" : "Create Tags", "pluginId" : "pathParser", "validateDir" : "pathParser", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Running plugin task: Create Tags + {"task" : "Scan","paths": [r"B:\_\SpecialSet", r"C:\foo"], "weekday" : "friday", "time" : "12:03"}, + {"task" : "GQL", "input" : "mutation OptimiseDatabase { optimiseDatabase }", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Optimising database... + {"task" : "Clean Generated Files", "weekday" : "friday", "time" : "12:03"}, + {"task" : "RenameGeneratedFiles", "weekday" : "friday", "time" : "12:03"}, # In task queue as -> Migrating scene hashes... + {"task" : "Backup", "maxBackups" : 0, "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Must check STASH log file to verify run. + {"task" : "python", "script" : "test_hello_world2.py", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'python' result=??? + {"task" : "python", "script" : "test_hello_world.py", "detach" : False, "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'python' result=??? + {"task" : "execute", "command" : "test_hello_world2.cmd", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'execute' result=??? + {"task" : "execute", "command" : "test_hello_world.bat", "args" : "--name David", "weekday" : "friday", "time" : "12:03"}, # Does NOT show up in the Task Queue. Check FileMonitor log file, and look for -> Task 'execute' result=??? + ], + + # MUST ToDo: Always set selfUnitTest to False before checking in this code!!! + # Enable to turn on self unit test. + "selfUnitTest": False, +} diff --git a/plugins/FileMonitor/filemonitor_task_examples.py b/plugins/FileMonitor/filemonitor_task_examples.py new file mode 100644 index 0000000..84b988b --- /dev/null +++ b/plugins/FileMonitor/filemonitor_task_examples.py @@ -0,0 +1,49 @@ +# Below are example tasks. +# They are all disabled by default, by having zero value for time frequency, or by having "DISABLED" set for the time field. +# To enable these tasks, set the frequency or the time value to a valid frequency or time stamp. +task_examples = { + "task_scheduler": [ + # Example#A1: Task to call call_GQL API with custom input + {"task" : "GQL", "input" : "mutation OptimiseDatabase { optimiseDatabase }", "weekday" : "sunday", "time" : "DISABLED"}, # To enable, change "DISABLED" to valid time + + # Example#A2: Task to call a python script. When this task is executed, the keyword is replaced by filemonitor.py current directory. + # The args field is NOT required. + {"task" : "python", "script" : "test_script_hello_world.py", "args" : "--MyArguments Hello", "weekday" : "monday", "time" : "DISABLED"}, # change "DISABLED" to valid time + + # Example#A3: The following task types can optionally take a [paths] field. If the paths field does not exists, the paths in the Stash library is used. + {"task" : "Scan", "paths" : [r"E:\MyVideos\downloads", r"V:\MyOtherVideos"], "weekday" : "sunday", "time" : "DISABLED"}, # Library -> [Scan] + {"task" : "Auto Tag", "paths" : [r"E:\MyVideos\downloads", r"V:\MyOtherVideos"], "weekday" : "monday,tuesday,wednesday,thursday,friday,saturday,sunday", "time" : "DISABLED"}, # Auto Tag -> [Auto Tag] + {"task" : "Clean", "paths" : ["E:\\MyVideos\\downloads", "V:\\MyOtherVideos"], "weekday" : "sunday", "time" : "DISABLED"}, # Generated Content-> [Generate] + + # Example#A4: Task which calls Migrations -> [Rename generated files] + {"task" : "RenameGeneratedFiles", "weekday" : "tuesday,thursday", "time" : "DISABLED"}, # (bi-weekly) example + + # Example#A5: The Backup task using optional field maxBackup, which overrides the UI [Max DB Backups] value + {"task" : "Backup", "maxBackup" : 12, "weekday" : "sunday", "time" : "DISABLED"}, # Trim the DB backup files down to 12 backup files. + {"task" : "Backup", "maxBackup" : 0, "weekday" : "sunday", "time" : "DISABLED"}, # When used with a zero value, it will make sure no file trimming will occur no matter the value of the UI [Max DB Backups] + + # The above weekday method is the more reliable method to schedule task, because it doesn't rely on FileMonitor running continuously (non-stop). + + # The below examples use frequency field method which can work with minutes and hours. A zero frequency value disables the task. + # Note: Both seconds and days are also supported for the frequency field. + # However, seconds is mainly used for test purposes. + # And days usage is discourage, because it only works if FileMonitor is running for X many days non-stop. + # The below example tasks are done using hours and minutes, however any of these task types can be converted to a daily, weekly, or monthly syntax. + + # Example#B1: The following task is the syntax used for a plugin. A plugin task requires the plugin name for the [task] field, and the plugin-ID for the [pluginId] field. + {"task" : "PluginButtonName_Here", "pluginId" : "PluginId_Here", "hours" : 0}, # The zero frequency value makes this task disabled. + # Example#B2: Optionally, the validateDir field can be included which is used to validate that the plugin is installed either under the plugins folder or under the plugins-community folder. + {"task" : "PluginButtonName_Here", "pluginId" : "PluginId_Here", "validateDir" : "UsuallySameAsPluginID", "hours" : 0}, # The zero frequency value makes this task disabled. + + # Example#B3: Task to execute a command + {"task" : "execute", "command" : "C:\\MyPath\\HelloWorld.bat", "hours" : 0}, + + # Example#B4: Task to execute a command with optional args field, and using keyword , which gets replaced with filemonitor.py current directory. + {"task" : "execute", "command" : "HelloWorld.cmd", "args" : "--name David", "minutes" : 0}, + + # Example#C1 Some OS may need the "command" field, which specifies the binary path + {"task" : "CheckStashIsRunning", "command" : "stash-linux-arm64v8", "minutes" :0}, + # Example#C2 RunAfter field can be used to specify task to run after starting Stash + {"task" : "CheckStashIsRunning", "RunAfter" : [{"task" : "Scan"},{"task" : "Backup", "maxBackup" : 0},{"task" : "Clean"}], "minutes" :0}, + ], +}