Periodic Rescans Added (New Feature)

This commit is contained in:
Benjamin Harder
2024-09-14 13:32:54 +02:00
parent c7e5c0e374
commit 34fcff4de8
21 changed files with 472 additions and 253 deletions

View File

@@ -12,6 +12,9 @@ REMOVE_ORPHANS = True
REMOVE_SLOW = True
REMOVE_STALLED = True
REMOVE_UNMONITORED = True
RUN_PERIODIC_RESCANS = {"SONARR": {"MISSING": true, CUTOFF_UNMET": true, "MAX_CONCURRENT_SCANS": 3, "MIN_DAYS_BEFORE_RESCAN": 7}, "RADARR": {"MISSING": true, "CUTOFF_UNMET": true, "MAX_CONCURRENT_SCANS": 3, "MIN_DAYS_BEFORE_RESCAN": 7}}
[feature_settings]
MIN_DOWNLOAD_SPEED = 100
PERMITTED_ATTEMPTS = 3
NO_STALLED_REMOVAL_QBIT_TAG = Don't Kill

View File

@@ -12,17 +12,20 @@ REMOVE_TIMER = get_config_value('REMOVE_TIMER',
REMOVE_FAILED = get_config_value('REMOVE_FAILED', 'features', False, bool, False)
REMOVE_FAILED_IMPORTS = get_config_value('REMOVE_FAILED_IMPORTS' , 'features', False, bool, False)
REMOVE_METADATA_MISSING = get_config_value('REMOVE_METADATA_MISSING', 'features', False, bool, False)
REMOVE_MISSING_FILES = get_config_value('REMOVE_MISSING_FILES' , 'features', False, bool, False)
REMOVE_NO_FORMAT_UPGRADE = get_config_value('REMOVE_NO_FORMAT_UPGRADE' , 'features', False, bool, False) # OUTDATED - WILL RETURN WARNING
REMOVE_ORPHANS = get_config_value('REMOVE_ORPHANS' , 'features', False, bool, False)
REMOVE_SLOW = get_config_value('REMOVE_SLOW' , 'features', False, bool, False)
REMOVE_MISSING_FILES = get_config_value('REMOVE_MISSING_FILES', 'features', False, bool, False)
REMOVE_NO_FORMAT_UPGRADE = get_config_value('REMOVE_NO_FORMAT_UPGRADE', 'features', False, bool, False) # OUTDATED - WILL RETURN WARNING
REMOVE_ORPHANS = get_config_value('REMOVE_ORPHANS', 'features', False, bool, False)
REMOVE_SLOW = get_config_value('REMOVE_SLOW', 'features', False, bool, False)
REMOVE_STALLED = get_config_value('REMOVE_STALLED', 'features', False, bool, False)
REMOVE_UNMONITORED = get_config_value('REMOVE_UNMONITORED' , 'features', False, bool, False)
MIN_DOWNLOAD_SPEED = get_config_value('MIN_DOWNLOAD_SPEED', 'features', False, int, 0)
PERMITTED_ATTEMPTS = get_config_value('PERMITTED_ATTEMPTS', 'features', False, int, 3)
NO_STALLED_REMOVAL_QBIT_TAG = get_config_value('NO_STALLED_REMOVAL_QBIT_TAG', 'features', False, str, 'Don\'t Kill')
IGNORE_PRIVATE_TRACKERS = get_config_value('IGNORE_PRIVATE_TRACKERS', 'features', False, bool, True)
FAILED_IMPORT_MESSAGE_PATTERNS = get_config_value('FAILED_IMPORT_MESSAGE_PATTERNS','features', False, list, [])
REMOVE_UNMONITORED = get_config_value('REMOVE_UNMONITORED', 'features', False, bool, False)
RUN_PERIODIC_RESCANS = get_config_value('RUN_PERIODIC_RESCANS', 'features', False, dict, {})
# Feature Settings
MIN_DOWNLOAD_SPEED = get_config_value('MIN_DOWNLOAD_SPEED', 'feature_settings', False, int, 0)
PERMITTED_ATTEMPTS = get_config_value('PERMITTED_ATTEMPTS', 'feature_settings', False, int, 3)
NO_STALLED_REMOVAL_QBIT_TAG = get_config_value('NO_STALLED_REMOVAL_QBIT_TAG', 'feature_settings', False, str, 'Don\'t Kill')
IGNORE_PRIVATE_TRACKERS = get_config_value('IGNORE_PRIVATE_TRACKERS', 'feature_settings', False, bool, True)
FAILED_IMPORT_MESSAGE_PATTERNS = get_config_value('FAILED_IMPORT_MESSAGE_PATTERNS','feature_settings', False, list, [])
# Radarr
RADARR_URL = get_config_value('RADARR_URL', 'radarr', False, str)
@@ -60,6 +63,41 @@ if not (IS_IN_PYTEST or RADARR_URL or SONARR_URL or LIDARR_URL or READARR_URL or
print(f'[ ERROR ]: No Radarr/Sonarr/Lidarr/Readarr/Whisparr URLs specified (nothing to monitor)')
exit()
#### Validate rescan settings
PERIODIC_RESCANS = get_config_value("PERIODIC_RESCANS", "features", False, dict, {})
rescan_supported_apps = ["SONARR", "RADARR"]
rescan_default_values = {
"MISSING": (True, bool),
"CUTOFF_UNMET": (True, bool),
"MAX_CONCURRENT_SCANS": (3, int),
"MIN_DAYS_BEFORE_RESCAN": (7, int),
}
# Remove rescan apps that are not supported
for key in list(RUN_PERIODIC_RESCANS.keys()):
if key not in rescan_supported_apps:
print(f"[ WARNING ]: Removed '{key}' from RUN_PERIODIC_RESCANS since only {rescan_supported_apps} are supported.")
RUN_PERIODIC_RESCANS.pop(key)
# Ensure SONARR and RADARR have the required parameters with default values if they are present
for app in rescan_supported_apps:
if app in RUN_PERIODIC_RESCANS:
for param, (default, expected_type) in rescan_default_values.items():
if param not in RUN_PERIODIC_RESCANS[app]:
print(f"[ INFO ]: Adding missing parameter '{param}' to '{app}' with default value '{default}'.")
RUN_PERIODIC_RESCANS[app][param] = default
else:
# Check the type and correct if necessary
current_value = RUN_PERIODIC_RESCANS[app][param]
if not isinstance(current_value, expected_type):
print(
f"[ INFO ]: Parameter '{param}' for '{app}' must be of type {expected_type.__name__} and found value '{current_value}' (type '{type(current_value).__name__}'). Defaulting to '{default}'."
)
RUN_PERIODIC_RESCANS[app][param] = default
########### Enrich setting variables
if RADARR_URL: RADARR_URL = RADARR_URL.rstrip('/') + '/api/v3'
if SONARR_URL: SONARR_URL = SONARR_URL.rstrip('/') + '/api/v3'
@@ -68,8 +106,14 @@ if READARR_URL: READARR_URL = READARR_URL.rstrip('/') + '/api/v1'
if WHISPARR_URL: WHISPARR_URL = WHISPARR_URL.rstrip('/') + '/api/v3'
if QBITTORRENT_URL: QBITTORRENT_URL = QBITTORRENT_URL.rstrip('/') + '/api/v2'
RADARR_MIN_VERSION = '5.3.6.8608'
SONARR_MIN_VERSION = '4.0.1.1131'
RADARR_MIN_VERSION = "5.3.6.8608"
if "RADARR" in PERIODIC_RESCANS:
RADARR_MIN_VERSION = "5.10.3.9171"
SONARR_MIN_VERSION = "4.0.1.1131"
if "SONARR" in PERIODIC_RESCANS:
SONARR_MIN_VERSION = "4.0.9.2332"
LIDARR_MIN_VERSION = None
READARR_MIN_VERSION = None
WHISPARR_MIN_VERSION = '2.0.0.548'
@@ -82,4 +126,3 @@ settingsDict = {}
for var_name in dir():
if var_name.isupper():
settingsDict[var_name] = locals()[var_name]

View File

@@ -22,9 +22,14 @@ def config_section_map(section):
options = config.options(section)
for option in options:
try:
dict1[option] = config.get(section, option)
except:
print("exception on %s!" % option)
value = config.get(section, option)
# Attempt to parse JSON for dictionary-like values
try:
dict1[option] = json.loads(value)
except json.JSONDecodeError:
dict1[option] = value
except Exception as e:
print(f"Exception on {option}: {e}")
dict1[option] = None
return dict1
@@ -38,44 +43,33 @@ def get_config_value(key, config_section, is_mandatory, datatype, default_value=
if IS_IN_DOCKER:
config_value = os.environ.get(key)
if config_value is not None:
# print(f'The value retrieved for [{config_section}]: {key} is "{config_value}"')
config_value = config_value
# return config_value
elif is_mandatory:
print(f"[ ERROR ]: Variable not specified in Docker environment: {key}")
sys.exit(0)
else:
# return default_value
# print(f'The default value used for [{config_section}]: {key} is "{default_value}" (data type: {type(default_value).__name__})')
config_value = default_value
else:
try:
config_value = config_section_map(config_section).get(key)
except configparser.NoSectionError:
config_value = None
if config_value is not None:
# print(f'The value retrieved for [{config_section}]: {key} is "{config_value}"')
config_value = config_value
# return config_value
elif is_mandatory:
print(
f"[ ERROR ]: Mandatory variable not specified in config file, section [{config_section}]: {key} (data type: {datatype.__name__})"
)
sys.exit(0)
else:
# return default_value
# print(f'The default value used for [{config_section}]: {key} is "{default_value}" (data type: {type(default_value).__name__})')
config_value = default_value
# Apply data type
try:
if datatype == bool:
config_value = eval(str(config_value).capitalize())
elif datatype == list:
if (
type(config_value) != list
): # Default value is already a list, doesn't need to be pushed through json.loads
elif datatype == list or datatype == dict:
if not isinstance(config_value, datatype):
config_value = json.loads(config_value)
elif config_value is not None:
config_value = cast(config_value, datatype)