mirror of
https://github.com/ManiMatter/decluttarr.git
synced 2026-04-28 03:39:35 +02:00
Initial commit (fork from ManiMatter/mirrarr)
Carved out the queue cleaning bits from mirrarr, without taking over the instance synchronization items. Not tested yet.
This commit is contained in:
0
config/__init__.py
Normal file
0
config/__init__.py
Normal file
24
config/config.conf-Example
Normal file
24
config/config.conf-Example
Normal file
@@ -0,0 +1,24 @@
|
||||
[general]
|
||||
LOG_LEVEL = VERBOSE
|
||||
TEST_RUN = True
|
||||
|
||||
[features]
|
||||
REMOVE_TIMER = 10
|
||||
REMOVE_FAILED = True
|
||||
REMOVE_STALLED = True
|
||||
REMOVE_METADATA_MISSING = True
|
||||
REMOVE_ORPHANS = True
|
||||
REMOVE_UNMONITORED = True
|
||||
PERMITTED_ATTEMPTS = 3
|
||||
NO_STALLED_REMOVAL_QBIT_TAG = Don't Kill If Stalled
|
||||
|
||||
[radarr]
|
||||
RADARR_URL = http://radarr:7878
|
||||
RADARR_KEY = $RADARR_KEY
|
||||
|
||||
[sonarr]
|
||||
SONARR_URL = http://sonarr:8989
|
||||
SONARR_KEY = $SONARR_KEY
|
||||
|
||||
[qbittorrent]
|
||||
QBITTORRENT_URL = http://qbittorrent:8080
|
||||
115
config/config.conf-Explained
Normal file
115
config/config.conf-Explained
Normal file
@@ -0,0 +1,115 @@
|
||||
# The config file is only relevant when running main.py locally.
|
||||
# When running within docker, all settings are to be set via the docker-compose.yml, and this config.conf file will be ignored
|
||||
|
||||
################################# GENERAL SECTION #################################
|
||||
# General parameters such as log level / test run
|
||||
[general]
|
||||
|
||||
###### LOG_LEVEL ######
|
||||
# Sets the level at which logging will take place.
|
||||
# INFO will only show changes applied to Radarr/Sonarr
|
||||
# VERBOSE will show when script runs (even if it results in no change)
|
||||
# Type: String
|
||||
# Permissible Values: CRITICAL, ERROR, WARNING, INFO, VERBOSE, DEBUG
|
||||
# Is Mandatory: No (Defaults to INFO)
|
||||
LOG_LEVEL = INFO
|
||||
|
||||
###### TEST_RUN ######
|
||||
# Allows you to safely try out this tool. If active, downloads will not be removed.
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
TEST_RUN = False
|
||||
|
||||
################################# FEATURES SETTINGS #################################
|
||||
# Steers which type of cleaning is applied to the downloads queue.
|
||||
# Requires QUEUE_CLEANING to be set to True to take effect.
|
||||
[features]
|
||||
|
||||
###### REMOVE_TIMER ######
|
||||
# Sets the frequency how often the queue is cleaned form orphan and stalled downloads
|
||||
# Type: Integer
|
||||
# Unit: Minutes
|
||||
# Is Mandatory: No (Defaults to 10)
|
||||
REMOVE_TIMER = 10
|
||||
|
||||
###### REMOVE_FAILED ######
|
||||
# Steers whether failed downloads with no connections are removed from the queue
|
||||
# Failed downloads are not added to the blocklist
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
REMOVE_FAILED = False
|
||||
|
||||
###### REMOVE_STALLED ######
|
||||
# Steers whether stalled downloads with no connections are removed from the queue
|
||||
# Stalled downloads are added to the blocklist, so that they are not re-requested in the future
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
REMOVE_STALLED = False
|
||||
|
||||
###### REMOVE_METADATA_MISSING ######
|
||||
# Steers whether downloads stuck obtaining meta data are removed from the queue
|
||||
# These downloads are added the blocklist, so that they are not re-requested in the future
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
REMOVE_METADATA_MISSING = False
|
||||
|
||||
###### REMOVE_ORPHANS ######
|
||||
# Steers whether orphan downloads are removed from the queue
|
||||
# Orphan downloads those that do not belong to any movie/tvshow anymore (since the movie/TV show was deleted post request)
|
||||
# Orphan downloads are not added to the block list
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
REMOVE_ORPHANS = False
|
||||
|
||||
###### REMOVE_UNMONITORED ######
|
||||
# Steers whether downloads belonging to unmonitored movies/TV shows are removed from the queue
|
||||
# Note: Will only remove from queue if all tv shows depending on the same download are unmonitored
|
||||
# Unmonitored downloads are not added to the block list
|
||||
# Type: Boolean
|
||||
# Permissible Values: True, False
|
||||
# Is Mandatory: No (Defaults to False)
|
||||
REMOVE_UNMONITORED = False
|
||||
|
||||
###### PERMITTED_ATTEMPTS ######
|
||||
# Defines how many times a download has to be caught as stalled or stuck downloading metadata before it is removed
|
||||
# Type: Integer
|
||||
# Unit: Number of scans
|
||||
# Is Mandatory: No (Defaults to 3)
|
||||
PERMITTED_ATTEMPTS= 3
|
||||
|
||||
###### NO_STALLED_REMOVAL_QBIT_TAG ######
|
||||
# Downloads in qBittorrent tagged with this tag will not be killed even if they are stalled
|
||||
# Type: String
|
||||
# Is Mandatory: No (Defaults to "Don't Kill If Stalled")
|
||||
NO_STALLED_REMOVAL_QBIT_TAG= Don't Kill If Stalled
|
||||
|
||||
################################# RADARR SECTION #################################
|
||||
[radarr]
|
||||
# Defines radarr instance on which download queue should be decluttered
|
||||
# RADARR_URL : URL under which the instance can be reached. If not defined, this instance will not be monitored.
|
||||
# RADARR_KEY : API Key (mandatory if RADARR_URL is specifidd)
|
||||
RADARR_URL = http://radarrA:7878
|
||||
RADARR_KEY = XXXXX
|
||||
|
||||
################################# SONARR SECTION #################################
|
||||
[sonarr]
|
||||
# Please see the documentation under the RADARR section - the explanations the same.
|
||||
SONARR_URL = http://sonarrA:8989
|
||||
SONARR_KEY = XXXXX
|
||||
|
||||
################################# SONARR SECTION #################################
|
||||
[sonarr]
|
||||
# Please see the documentation under the RADARR section - the explanations the same.
|
||||
SONARR_URL = http://sonarrA:8989
|
||||
SONARR_KEY = XXXXX
|
||||
|
||||
################################# QBITTORRENT SECTION #################################
|
||||
[qbittorrent]
|
||||
# Defines URL of qBittorrent
|
||||
# QBITTORRENT_URL : URL under which the instance can be reached. If not defined, the NO_STALLED_REMOVAL_QBIT_TAG takes no effect
|
||||
QBITTORRENT_URL = http://qbittorrent:8080
|
||||
114
config/config.py
Normal file
114
config/config.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
import os
|
||||
import configparser
|
||||
|
||||
########################################################################################################################
|
||||
# Check if in Docker
|
||||
IS_IN_DOCKER = os.environ.get('IS_IN_DOCKER')
|
||||
|
||||
########################################################################################################################
|
||||
def ConfigSectionMap(section):
|
||||
'Load the config file into a dictionary'
|
||||
dict1 = {}
|
||||
options = config.options(section)
|
||||
for option in options:
|
||||
try:
|
||||
dict1[option] = config.get(section, option)
|
||||
except:
|
||||
print("exception on %s!" % option)
|
||||
dict1[option] = None
|
||||
return dict1
|
||||
|
||||
def cast(value, type_):
|
||||
return type_(value)
|
||||
|
||||
def get_config_value(key, config_section, is_mandatory, datatype, default_value = None):
|
||||
'Return for each key the corresponding value from the Docker Environment or the Config File'
|
||||
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
|
||||
config_value = default_value
|
||||
|
||||
else:
|
||||
try:
|
||||
config_value = ConfigSectionMap(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
|
||||
config_value = default_value
|
||||
|
||||
# Apply data type
|
||||
try:
|
||||
if datatype == bool:
|
||||
config_value = eval(str(config_value).capitalize())
|
||||
if config_value is not None: config_value = cast(config_value, datatype)
|
||||
except:
|
||||
print(f'[ ERROR ]: The value retrieved for [{config_section}]: {key} is "{config_value}" and cannot be converted to data type {datatype}')
|
||||
sys.exit(0)
|
||||
return config_value
|
||||
|
||||
########################################################################################################################
|
||||
# Load Config File
|
||||
Config_FileName = 'config.conf'
|
||||
Config_FileFullPath = os.path.join(os.path.abspath(os.path.dirname(__file__)), Config_FileName)
|
||||
sys.tracebacklimit = 0 # dont show stack traces in prod mode
|
||||
config = configparser.ConfigParser()
|
||||
config.optionxform = str # maintain capitalization of config keys
|
||||
config.read(Config_FileFullPath)
|
||||
|
||||
########################################################################################################################
|
||||
# Load Config
|
||||
# General
|
||||
LOG_LEVEL = get_config_value('LOG_LEVEL', 'general', False, str, 'INFO')
|
||||
TEST_RUN = get_config_value('TEST_RUN', 'general', False, bool, False)
|
||||
|
||||
# Features
|
||||
REMOVE_TIMER = get_config_value('REMOVE_TIMER', 'features', False, int, 10)
|
||||
REMOVE_FAILED = get_config_value('REMOVE_FAILED', 'features', False, bool, False)
|
||||
REMOVE_STALLED = get_config_value('REMOVE_STALLED', 'features', False, bool, False)
|
||||
REMOVE_METADATA_MISSING = get_config_value('REMOVE_METADATA_MISSING', 'features', False, bool, False)
|
||||
REMOVE_ORPHANS = get_config_value('REMOVE_ORPHANS' , 'features', False, bool, False)
|
||||
REMOVE_UNMONITORED = get_config_value('REMOVE_UNMONITORED' , 'features', False, bool, False)
|
||||
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 If Stalled')
|
||||
|
||||
# Radarr
|
||||
RADARR_URL = get_config_value('RADARR_URL', 'radarr', False, str)
|
||||
RADARR_KEY = None if RADARR_URL == None else \
|
||||
get_config_value('RADARR_KEY', 'radarr', True, str)
|
||||
|
||||
# Sonarr
|
||||
SONARR_URL = get_config_value('SONARR_URL', 'sonarr', False, str)
|
||||
SONARR_KEY = None if SONARR_URL == None else \
|
||||
get_config_value('SONARR_KEY', 'sonarr', True, str)
|
||||
|
||||
# qBittorrent
|
||||
QBITTORRENT_URL = get_config_value('QBITTORRENT_URL', 'qbittorrent', False, str, '')
|
||||
|
||||
########################################################################################################################
|
||||
if not (RADARR_URL or SONARR_URL):
|
||||
print(f'[ ERROR ]: No Radarr/Sonarr URLs specified (nothing to monitor)')
|
||||
sys.exit(0)
|
||||
|
||||
########### Add API to URLs
|
||||
if RADARR_URL: RADARR_URL += '/api/v3'
|
||||
if SONARR_URL: SONARR_URL += '/api/v3'
|
||||
if QBITTORRENT_URL: QBITTORRENT_URL += '/api/v2'
|
||||
|
||||
Reference in New Issue
Block a user