Add start FileMonitor as a service from UI

This commit is contained in:
David Maisonave
2024-08-09 06:58:26 -04:00
parent b804edea43
commit 0ee95c065c
7 changed files with 133 additions and 55 deletions

View File

@@ -142,7 +142,7 @@ class StashPluginHelper:
self.FRAGMENT_SERVER['Scheme'] = endpointUrlArr[0] self.FRAGMENT_SERVER['Scheme'] = endpointUrlArr[0]
self.FRAGMENT_SERVER['Host'] = endpointUrlArr[1][2:] self.FRAGMENT_SERVER['Host'] = endpointUrlArr[1][2:]
self.FRAGMENT_SERVER['Port'] = endpointUrlArr[2] self.FRAGMENT_SERVER['Port'] = endpointUrlArr[2]
self.STASH_INTERFACE = StashInterface(self.FRAGMENT_SERVER) self.STASH_INTERFACE = self.ExtendStashInterface(self.FRAGMENT_SERVER)
else: else:
try: try:
self.STDIN_READ = sys.stdin.read() self.STDIN_READ = sys.stdin.read()
@@ -155,7 +155,7 @@ class StashPluginHelper:
self.PLUGIN_TASK_NAME = self.JSON_INPUT["args"]["mode"] self.PLUGIN_TASK_NAME = self.JSON_INPUT["args"]["mode"]
self.FRAGMENT_SERVER = self.JSON_INPUT["server_connection"] self.FRAGMENT_SERVER = self.JSON_INPUT["server_connection"]
self.STASH_URL = f"{self.FRAGMENT_SERVER['Scheme']}://{self.FRAGMENT_SERVER['Host']}:{self.FRAGMENT_SERVER['Port']}" self.STASH_URL = f"{self.FRAGMENT_SERVER['Scheme']}://{self.FRAGMENT_SERVER['Host']}:{self.FRAGMENT_SERVER['Port']}"
self.STASH_INTERFACE = StashInterface(self.FRAGMENT_SERVER) self.STASH_INTERFACE = self.ExtendStashInterface(self.FRAGMENT_SERVER)
if self.STASH_INTERFACE: if self.STASH_INTERFACE:
self.PLUGIN_CONFIGURATION = self.STASH_INTERFACE.get_configuration()["plugins"] self.PLUGIN_CONFIGURATION = self.STASH_INTERFACE.get_configuration()["plugins"]
@@ -239,3 +239,27 @@ class StashPluginHelper:
lineNo = inspect.currentframe().f_back.f_lineno lineNo = inspect.currentframe().f_back.f_lineno
self.Log(f"StashPluginHelper Status: (CALLED_AS_STASH_PLUGIN={self.CALLED_AS_STASH_PLUGIN}), (RUNNING_IN_COMMAND_LINE_MODE={self.RUNNING_IN_COMMAND_LINE_MODE}), (DEBUG_TRACING={self.DEBUG_TRACING}), (DRY_RUN={self.DRY_RUN}), (PLUGIN_ID={self.PLUGIN_ID}), (PLUGIN_TASK_NAME={self.PLUGIN_TASK_NAME}), (STASH_URL={self.STASH_URL}), (MAIN_SCRIPT_NAME={self.MAIN_SCRIPT_NAME})", self.Log(f"StashPluginHelper Status: (CALLED_AS_STASH_PLUGIN={self.CALLED_AS_STASH_PLUGIN}), (RUNNING_IN_COMMAND_LINE_MODE={self.RUNNING_IN_COMMAND_LINE_MODE}), (DEBUG_TRACING={self.DEBUG_TRACING}), (DRY_RUN={self.DRY_RUN}), (PLUGIN_ID={self.PLUGIN_ID}), (PLUGIN_TASK_NAME={self.PLUGIN_TASK_NAME}), (STASH_URL={self.STASH_URL}), (MAIN_SCRIPT_NAME={self.MAIN_SCRIPT_NAME})",
printTo, logLevel, lineNo) printTo, logLevel, lineNo)
# Extends class StashInterface with functions which are not yet in the class
class ExtendStashInterface(StashInterface):
def metadata_autotag(self, paths:list=[], dry_run=False):
if not paths:
return
query = """
mutation MetadataAutoTag($input:AutoTagMetadataInput!) {
metadataAutoTag(input: $input)
}
"""
metadata_autotag_input = {
"paths": paths
}
result = self.call_GQL(query, {"input": metadata_autotag_input})
return result
def backup_database(self):
return self.call_GQL("mutation { backupDatabase(input: {download: false})}")
def optimise_database(self):
return self.call_GQL("mutation OptimiseDatabase { optimiseDatabase }")

View File

@@ -1,4 +1,4 @@
# FileMonitor: Ver 0.6.1 (By David Maisonave) # FileMonitor: Ver 0.7.0 (By David Maisonave)
FileMonitor is a [Stash](https://github.com/stashapp/stash) plugin which updates Stash if any changes occurs in the Stash library paths. FileMonitor is a [Stash](https://github.com/stashapp/stash) plugin which updates Stash if any changes occurs in the Stash library paths.
### Using FileMonitor as a plugin ### Using FileMonitor as a plugin

View File

@@ -142,7 +142,7 @@ class StashPluginHelper:
self.FRAGMENT_SERVER['Scheme'] = endpointUrlArr[0] self.FRAGMENT_SERVER['Scheme'] = endpointUrlArr[0]
self.FRAGMENT_SERVER['Host'] = endpointUrlArr[1][2:] self.FRAGMENT_SERVER['Host'] = endpointUrlArr[1][2:]
self.FRAGMENT_SERVER['Port'] = endpointUrlArr[2] self.FRAGMENT_SERVER['Port'] = endpointUrlArr[2]
self.STASH_INTERFACE = StashInterface(self.FRAGMENT_SERVER) self.STASH_INTERFACE = self.ExtendStashInterface(self.FRAGMENT_SERVER)
else: else:
try: try:
self.STDIN_READ = sys.stdin.read() self.STDIN_READ = sys.stdin.read()
@@ -155,7 +155,7 @@ class StashPluginHelper:
self.PLUGIN_TASK_NAME = self.JSON_INPUT["args"]["mode"] self.PLUGIN_TASK_NAME = self.JSON_INPUT["args"]["mode"]
self.FRAGMENT_SERVER = self.JSON_INPUT["server_connection"] self.FRAGMENT_SERVER = self.JSON_INPUT["server_connection"]
self.STASH_URL = f"{self.FRAGMENT_SERVER['Scheme']}://{self.FRAGMENT_SERVER['Host']}:{self.FRAGMENT_SERVER['Port']}" self.STASH_URL = f"{self.FRAGMENT_SERVER['Scheme']}://{self.FRAGMENT_SERVER['Host']}:{self.FRAGMENT_SERVER['Port']}"
self.STASH_INTERFACE = StashInterface(self.FRAGMENT_SERVER) self.STASH_INTERFACE = self.ExtendStashInterface(self.FRAGMENT_SERVER)
if self.STASH_INTERFACE: if self.STASH_INTERFACE:
self.PLUGIN_CONFIGURATION = self.STASH_INTERFACE.get_configuration()["plugins"] self.PLUGIN_CONFIGURATION = self.STASH_INTERFACE.get_configuration()["plugins"]
@@ -239,3 +239,27 @@ class StashPluginHelper:
lineNo = inspect.currentframe().f_back.f_lineno lineNo = inspect.currentframe().f_back.f_lineno
self.Log(f"StashPluginHelper Status: (CALLED_AS_STASH_PLUGIN={self.CALLED_AS_STASH_PLUGIN}), (RUNNING_IN_COMMAND_LINE_MODE={self.RUNNING_IN_COMMAND_LINE_MODE}), (DEBUG_TRACING={self.DEBUG_TRACING}), (DRY_RUN={self.DRY_RUN}), (PLUGIN_ID={self.PLUGIN_ID}), (PLUGIN_TASK_NAME={self.PLUGIN_TASK_NAME}), (STASH_URL={self.STASH_URL}), (MAIN_SCRIPT_NAME={self.MAIN_SCRIPT_NAME})", self.Log(f"StashPluginHelper Status: (CALLED_AS_STASH_PLUGIN={self.CALLED_AS_STASH_PLUGIN}), (RUNNING_IN_COMMAND_LINE_MODE={self.RUNNING_IN_COMMAND_LINE_MODE}), (DEBUG_TRACING={self.DEBUG_TRACING}), (DRY_RUN={self.DRY_RUN}), (PLUGIN_ID={self.PLUGIN_ID}), (PLUGIN_TASK_NAME={self.PLUGIN_TASK_NAME}), (STASH_URL={self.STASH_URL}), (MAIN_SCRIPT_NAME={self.MAIN_SCRIPT_NAME})",
printTo, logLevel, lineNo) printTo, logLevel, lineNo)
# Extends class StashInterface with functions which are not yet in the class
class ExtendStashInterface(StashInterface):
def metadata_autotag(self, paths:list=[], dry_run=False):
if not paths:
return
query = """
mutation MetadataAutoTag($input:AutoTagMetadataInput!) {
metadataAutoTag(input: $input)
}
"""
metadata_autotag_input = {
"paths": paths
}
result = self.call_GQL(query, {"input": metadata_autotag_input})
return result
def backup_database(self):
return self.call_GQL("mutation { backupDatabase(input: {download: false})}")
def optimise_database(self):
return self.call_GQL("mutation OptimiseDatabase { optimiseDatabase }")

View File

@@ -3,7 +3,7 @@
# Get the latest developers version from following link: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor # Get the latest developers version from following link: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor
# Note: To call this script outside of Stash, pass --url and the Stash URL. # Note: To call this script outside of Stash, pass --url and the Stash URL.
# Example: python filemonitor.py --url http://localhost:9999 # Example: python filemonitor.py --url http://localhost:9999
import os, time, pathlib, argparse import os, sys, time, pathlib, argparse
from StashPluginHelper import StashPluginHelper from StashPluginHelper import StashPluginHelper
import watchdog # pip install watchdog # https://pythonhosted.org/watchdog/ import watchdog # pip install watchdog # https://pythonhosted.org/watchdog/
from watchdog.observers import Observer # This is also needed for event attributes from watchdog.observers import Observer # This is also needed for event attributes
@@ -50,6 +50,7 @@ signal = Condition(mutex)
shouldUpdate = False shouldUpdate = False
TargetPaths = [] TargetPaths = []
SHAREDMEMORY_NAME = "DavidMaisonaveAxter_FileMonitor"
RECURSIVE = plugin.pluginSettings["recursiveDisabled"] == False RECURSIVE = plugin.pluginSettings["recursiveDisabled"] == False
SCAN_MODIFIED = plugin.pluginConfig["scanModified"] SCAN_MODIFIED = plugin.pluginConfig["scanModified"]
RUN_CLEAN_AFTER_DELETE = plugin.pluginConfig["runCleanAfterDelete"] RUN_CLEAN_AFTER_DELETE = plugin.pluginConfig["runCleanAfterDelete"]
@@ -84,30 +85,25 @@ def isJobWaitingToRun():
global FileMonitorPluginIsOnTaskQue global FileMonitorPluginIsOnTaskQue
FileMonitorPluginIsOnTaskQue = False FileMonitorPluginIsOnTaskQue = False
jobIsWaiting = False jobIsWaiting = False
i = 1 taskQue = plugin.STASH_INTERFACE.job_queue()
while True: for jobDetails in taskQue:
jobDetails = plugin.STASH_INTERFACE.find_job(i) plugin.Trace(f"(Job ID({jobDetails['id']})={jobDetails})")
if jobDetails: if jobDetails['status'] == "READY":
plugin.Trace(f"(Job ID({i})={jobDetails})") if jobDetails['description'] == "Running plugin task: Stop Library Monitor":
if jobDetails['status'] == "READY": StopLibraryMonitorWaitingInTaskQueue = True
if jobDetails['description'] == "Running plugin task: Stop Library Monitor": JobIdInTheQue = jobDetails['id']
StopLibraryMonitorWaitingInTaskQueue = True jobIsWaiting = True
JobIdInTheQue = i elif jobDetails['status'] == "RUNNING" and jobDetails['description'].find("Start Library Monitor") > -1:
jobIsWaiting = True FileMonitorPluginIsOnTaskQue = True
elif jobDetails['status'] == "RUNNING" and jobDetails['description'] == "Running plugin task: Start Library Monitor":
FileMonitorPluginIsOnTaskQue = True
else:
plugin.Trace(f"Last job {i}")
break
i += 1
JobIdInTheQue = 0 JobIdInTheQue = 0
return jobIsWaiting return jobIsWaiting
if plugin.CALLED_AS_STASH_PLUGIN: if plugin.CALLED_AS_STASH_PLUGIN:
plugin.Trace(f"isJobWaitingToRun() = {isJobWaitingToRun()})") plugin.Trace(f"isJobWaitingToRun() = {isJobWaitingToRun()})")
# ToDo: Add logic here for reoccurring scheduler # Reoccurring scheduler code
def runTask(task): def runTask(task):
plugin.Trace(f"Running task {task}")
if task['task'] == "Clean": if task['task'] == "Clean":
plugin.STASH_INTERFACE.metadata_clean(paths=stashPaths, dry_run=plugin.DRY_RUN) plugin.STASH_INTERFACE.metadata_clean(paths=stashPaths, dry_run=plugin.DRY_RUN)
elif task['task'] == "Generate": elif task['task'] == "Generate":
@@ -116,21 +112,29 @@ def runTask(task):
plugin.STASH_INTERFACE.call_GQL("mutation { backupDatabase(input: {download: false})}") plugin.STASH_INTERFACE.call_GQL("mutation { backupDatabase(input: {download: false})}")
elif task['task'] == "Scan": elif task['task'] == "Scan":
plugin.STASH_INTERFACE.metadata_scan(paths=stashPaths) plugin.STASH_INTERFACE.metadata_scan(paths=stashPaths)
# elif task['task'] == "Create Tags":
# plugin.STASH_INTERFACE.run_plugin_task(plugin_id="pathParser", task_name="Create Tags")
elif task['task'] == "Auto Tag": elif task['task'] == "Auto Tag":
plugin.Warn("Auto Tag is not implemented!!!") plugin.STASH_INTERFACE.metadata_autotag(paths=stashPaths, dry_run=plugin.DRY_RUN)
elif task['task'] == "Optimise Database":
plugin.STASH_INTERFACE.optimise_database()
else: else:
# ToDo: Add code to check if plugin is installed.
plugin.Trace(f"Running plugin task pluginID={task['pluginId']}, task name = {task['task']}")
plugin.STASH_INTERFACE.run_plugin_task(plugin_id=task['pluginId'], task_name=task['task']) plugin.STASH_INTERFACE.run_plugin_task(plugin_id=task['pluginId'], task_name=task['task'])
def reoccurringScheduler(): def reoccurringScheduler():
import schedule # pip install schedule # https://github.com/dbader/schedule import schedule # pip install schedule # https://github.com/dbader/schedule
for task in plugin.pluginConfig['task_reoccurring_scheduler']: for task in plugin.pluginConfig['task_reoccurring_scheduler']:
if task['hours'] > 0: if 'days' in task and task['days'] > 0:
plugin.Log(f"Adding to reoccurring scheduler task '{task['task']}' at {task['hours']} hour(s) interval") plugin.Log(f"Adding to reoccurring scheduler task '{task['task']}' at {task['days']} days interval")
schedule.every(task['days']).days.do(runTask, task)
elif 'hours' in task and task['hours'] > 0:
plugin.Log(f"Adding to reoccurring scheduler task '{task['task']}' at {task['hours']} hours interval")
schedule.every(task['hours']).hours.do(runTask, task) schedule.every(task['hours']).hours.do(runTask, task)
elif 'minutes' in task and task['minutes'] > 0:
plugin.Log(f"Adding to reoccurring scheduler task '{task['task']}' at {task['minutes']} minutes interval")
schedule.every(task['minutes']).minutes.do(runTask, task)
def checkSchedulePending():
import schedule # pip install schedule # https://github.com/dbader/schedule
schedule.run_pending()
if plugin.pluginConfig['turnOnScheduler']: if plugin.pluginConfig['turnOnScheduler']:
reoccurringScheduler() reoccurringScheduler()
@@ -139,9 +143,9 @@ def start_library_monitor():
global TargetPaths global TargetPaths
try: try:
# Create shared memory buffer which can be used as singleton logic or to get a signal to quit task from external script # Create shared memory buffer which can be used as singleton logic or to get a signal to quit task from external script
shm_a = shared_memory.SharedMemory(name="DavidMaisonaveAxter_FileMonitor", create=True, size=4) shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=True, size=4)
except: except:
plugin.Error("Could not open shared memory map. Change File Monitor must be running. Can not run multiple instance of Change File Monitor.") plugin.Error(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must be running. Can not run multiple instance of Change File Monitor.")
return return
type(shm_a.buf) type(shm_a.buf)
shm_buffer = shm_a.buf shm_buffer = shm_a.buf
@@ -238,6 +242,8 @@ def start_library_monitor():
if shm_buffer[0] != CONTINUE_RUNNING_SIG: if shm_buffer[0] != CONTINUE_RUNNING_SIG:
plugin.Log(f"Breaking out of loop. (shm_buffer[0]={shm_buffer[0]})") plugin.Log(f"Breaking out of loop. (shm_buffer[0]={shm_buffer[0]})")
break break
if plugin.pluginConfig['turnOnScheduler']:
checkSchedulePending()
plugin.Trace("Wait start") plugin.Trace("Wait start")
if plugin.CALLED_AS_STASH_PLUGIN: if plugin.CALLED_AS_STASH_PLUGIN:
signal.wait(timeout=SIGNAL_TIMEOUT) signal.wait(timeout=SIGNAL_TIMEOUT)
@@ -251,7 +257,9 @@ def start_library_monitor():
if TargetPath == SPECIAL_FILE_DIR: if TargetPath == SPECIAL_FILE_DIR:
if os.path.isfile(SPECIAL_FILE_NAME): if os.path.isfile(SPECIAL_FILE_NAME):
shm_buffer[0] = STOP_RUNNING_SIG shm_buffer[0] = STOP_RUNNING_SIG
plugin.Log(f"Detected trigger file to kill FileMonitor. {SPECIAL_FILE_NAME}", printTo = plugin.LOG_TO_FILE + plugin.LOG_TO_CONSOLE + plugin.LOG_TO_STASH) plugin.Log(f"[SpFl]Detected trigger file to kill FileMonitor. {SPECIAL_FILE_NAME}", printTo = plugin.LOG_TO_FILE + plugin.LOG_TO_CONSOLE + plugin.LOG_TO_STASH)
else:
plugin.Trace(f"[SpFl]Did not find file {SPECIAL_FILE_NAME}.")
TargetPaths = [] TargetPaths = []
TmpTargetPaths = list(set(TmpTargetPaths)) TmpTargetPaths = list(set(TmpTargetPaths))
if TmpTargetPaths != []: if TmpTargetPaths != []:
@@ -285,7 +293,6 @@ def start_library_monitor():
observer.join() observer.join()
plugin.Trace("Exiting function") plugin.Trace("Exiting function")
# This function is only useful when called outside of Stash.
# Example: python filemonitor.py --stop # Example: python filemonitor.py --stop
def stop_library_monitor(): def stop_library_monitor():
if CREATE_SPECIAL_FILE_TO_EXIT: if CREATE_SPECIAL_FILE_TO_EXIT:
@@ -296,10 +303,10 @@ def stop_library_monitor():
os.remove(SPECIAL_FILE_NAME) os.remove(SPECIAL_FILE_NAME)
plugin.Trace("Opening shared memory map.") plugin.Trace("Opening shared memory map.")
try: try:
shm_a = shared_memory.SharedMemory(name="DavidMaisonaveAxter_FileMonitor", create=False, size=4) shm_a = shared_memory.SharedMemory(name=SHAREDMEMORY_NAME, create=False, size=4)
except: except:
pass # If FileMonitor is running as plugin, then it's expected behavior that SharedMemory will not be avialable.
plugin.Log("Could not open shared memory map. Change File Monitor must not be running.") plugin.Trace(f"Could not open shared memory map ({SHAREDMEMORY_NAME}). Change File Monitor must not be running.")
return return
type(shm_a.buf) type(shm_a.buf)
shm_buffer = shm_a.buf shm_buffer = shm_a.buf
@@ -318,6 +325,23 @@ if parse_args.stop or parse_args.restart or plugin.PLUGIN_TASK_NAME == "stop_lib
plugin.Trace(f"Restart FileMonitor EXIT") plugin.Trace(f"Restart FileMonitor EXIT")
else: else:
plugin.Trace(f"Stop FileMonitor EXIT") plugin.Trace(f"Stop FileMonitor EXIT")
elif plugin.PLUGIN_TASK_NAME == "start_library_monitor_service":
import subprocess
import platform
is_windows = any(platform.win32_ver())
PythonExe = f"{sys.executable}"
# PythonExe = PythonExe.replace("python.exe", "pythonw.exe")
args = [f"{PythonExe}", f"{pathlib.Path(__file__).resolve().parent}{os.sep}filemonitor.py", '--url', f"{plugin.STASH_URL}"]
plugin.Trace(f"args={args}")
if is_windows:
plugin.Trace("Executing process using Windows DETACHED_PROCESS")
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen(args,creationflags=DETACHED_PROCESS, shell=True).pid
else:
plugin.Trace("Executing process using normal Popen")
pid = subprocess.Popen(args).pid
plugin.Trace(f"pid={pid}")
plugin.Trace(f"start_library_monitor_service EXIT")
elif plugin.PLUGIN_TASK_NAME == "start_library_monitor" or not plugin.CALLED_AS_STASH_PLUGIN: elif plugin.PLUGIN_TASK_NAME == "start_library_monitor" or not plugin.CALLED_AS_STASH_PLUGIN:
start_library_monitor() start_library_monitor()
plugin.Trace(f"start_library_monitor EXIT") plugin.Trace(f"start_library_monitor EXIT")

View File

@@ -1,6 +1,6 @@
name: FileMonitor name: FileMonitor
description: Monitors the Stash library folders, and updates Stash if any changes occurs in the Stash library paths. description: Monitors the Stash library folders, and updates Stash if any changes occurs in the Stash library paths.
version: 0.6.1 version: 0.7.0
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/FileMonitor
settings: settings:
recursiveDisabled: recursiveDisabled:
@@ -20,8 +20,12 @@ exec:
- "{pluginDir}/filemonitor.py" - "{pluginDir}/filemonitor.py"
interface: raw interface: raw
tasks: tasks:
- name: Start Library Monitor - name: Start Library Monitor Service
description: Monitors paths in Stash library for media file changes, and updates Stash. description: Run as a SERVICE to monitors paths in Stash library for media file changes, and updates Stash. Recommended start method.
defaultArgs:
mode: start_library_monitor_service
- name: Start Library Monitor Plugin
description: Run as a plugin (not recommended method)
defaultArgs: defaultArgs:
mode: start_library_monitor mode: start_library_monitor
- name: Stop Library Monitor - name: Stop Library Monitor

View File

@@ -17,20 +17,22 @@ config = {
# Enable to run metadata clean task after file deletion. # Enable to run metadata clean task after file deletion.
"runCleanAfterDelete": False, "runCleanAfterDelete": False,
# The scheduler my only work reliably when FileMonitor runs in command line mode (as a service)
# Enable to turn on scheduler_task_list # Enable to turn on scheduler_task_list
"turnOnScheduler": True, "turnOnScheduler": True,
# Reoccurring scheduler task list. To activate schedule, change number from zero to the number of hours interval # Reoccurring scheduler task list.
"task_reoccurring_scheduler": [ "task_reoccurring_scheduler": [
# Example: To perform a 'Clean' task every 48 hours, change zero to 48 # Frequency can be in minutes, hours, or days.
# Hours Conversion: 24=Daily, 168=Weekly, 720=Monthly, 1440=Bi-Monthly, 2160=Quarterly, 8760=Yearly # A zero frequency value disables the task.
{"task" : "Clean", "hours" : 48}, # Maintenance Clean (every 2 days) {"task" : "Clean", "days" : 2}, # Maintenance -> [Clean] (every 2 days)
{"task" : "Generate", "hours" : 168}, # Generated Content (Weekly) {"task" : "Generate", "days" : 7}, # Generated Content-> [Generate] (Weekly)
{"task" : "Backup", "hours" : 720}, # Backup Backup (Monthly) {"task" : "Backup", "days" : 30}, # Backup -> [Backup] (Monthly)
{"task" : "Scan", "hours" : 168}, # Library Scan (Weekly) {"task" : "Scan", "days" : 7}, # Library -> [Scan] (Weekly)
# {"task" : "Create Tags", "hours" : 24},# Requires plugin [Path Parser] {"task" : "Auto Tag", "hours" : 24}, # Auto Tag -> [Auto Tag] (Daily)
{"task" : "Create Tags", "pluginId" : "pathParser", "hours" : 24}, # Requires plugin [Path Parser] {"task" : "Optimise Database", "hours" : 24}, # Maintenance -> [Optimise Database] (Daily)
{"task" : "Auto Tag", "hours" : 0}, # !!! Not yet implemented!!! {"task" : "Create Tags", "pluginId" : "pathParser", "days" : 1}, # Requires plugin [Path Parser] (Daily)
{"task" : "MyTaskHere", "pluginId" : "MyPluginId", "hours" : 0}, # Place holder for custom task. {"task" : "PluginButtonName_Here", "pluginId" : "PluginId_Here", "hours" : 0}, # Place holder for custom task.
# Add additional task here.
], ],
# Maximum backups to keep. When scheduler is enabled, and the Backup runs, delete older backups after reaching maximum backups. # Maximum backups to keep. When scheduler is enabled, and the Backup runs, delete older backups after reaching maximum backups.
"BackupsMax" : 6, # Not yet implemented!!! "BackupsMax" : 6, # Not yet implemented!!!

View File

@@ -1,3 +1,3 @@
stashapp-tools >= 0.2.48 stashapp-tools >= 0.2.49
pyYAML pyYAML
watchdog watchdog