From 2e15e35ef5f8b7705407d223ed7ea157cdffdd87 Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 21 Aug 2022 14:40:57 -0400 Subject: [PATCH] Adds running commands via config #149 --- config/config.yml.sample | 13 +++++++++++ modules/config.py | 48 +++++++++++++++++++++++++++++++++++----- modules/qbittorrent.py | 36 +++++++++++++++--------------- modules/webhooks.py | 4 ++-- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/config/config.yml.sample b/config/config.yml.sample index ebebcaa..83b246d 100644 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -3,6 +3,19 @@ # Please refer to the link below for more details on how to set up the configuration file # https://github.com/StuffAnThings/qbit_manage/wiki/Config-Setup +commands: + # The commands defined below will IGNORE any commands used in command line and docker env variables. + dry_run: True + cross_seed: False + recheck: False + cat_update: False + tag_update: False + rem_unregistered: False + tag_tracker_error: False + rem_orphaned: False + tag_nohardlinks: False + skip_recycle: False + qbt: # qBittorrent parameters host: "localhost:8080" diff --git a/modules/config.py b/modules/config.py index 8d41788..3b7bb1d 100644 --- a/modules/config.py +++ b/modules/config.py @@ -31,6 +31,42 @@ class Config: loaded_yaml = YAML(self.config_path) self.data = loaded_yaml.data + + # Replace env variables with config commands + if "commands" in self.data: + if self.data["commands"] is not None: + logger.info(f"Commands found in {config_file}, ignoring env variables and using config commands instead.") + self.commands = self.data.pop("commands") + if 'dry_run' not in self.commands: + self.commands['dry_run'] = args['dry_run'] if 'dry_run' in args else False + # Add default any missing commands as False + for v in [ + 'cross_seed', + 'recheck', + 'cat_update', + 'tag_update', + 'rem_unregistered', + 'tag_tracker_error', + 'rem_orphaned', + 'tag_nohardlinks', + 'skip_recycle', + ]: + if v not in self.commands: + self.commands[v] = False + + logger.debug(f" --cross-seed (QBT_CROSS_SEED): {self.commands['cross_seed']}") + logger.debug(f" --recheck (QBT_RECHECK): {self.commands['recheck']}") + logger.debug(f" --cat-update (QBT_CAT_UPDATE): {self.commands['cat_update']}") + logger.debug(f" --tag-update (QBT_TAG_UPDATE): {self.commands['tag_update']}") + logger.debug(f" --rem-unregistered (QBT_REM_UNREGISTERED): {self.commands['rem_unregistered']}") + logger.debug(f" --tag-tracker-error (QBT_TAG_TRACKER_ERROR): {self.commands['tag_tracker_error']}") + logger.debug(f" --rem-orphaned (QBT_REM_ORPHANED): {self.commands['rem_orphaned']}") + logger.debug(f" --tag-nohardlinks (QBT_TAG_NOHARDLINKS): {self.commands['tag_nohardlinks']}") + logger.debug(f" --skip-recycle (QBT_SKIP_RECYCLE): {self.commands['skip_recycle']}") + logger.debug(f" --dry-run (QBT_DRY_RUN): {self.commands['dry_run']}") + else: + self.commands = args + if "qbt" in self.data: self.data["qbt"] = self.data.pop("qbt") self.data["settings"] = self.data.pop("settings") if "settings" in self.data else {} if "directory" in self.data: self.data["directory"] = self.data.pop("directory") @@ -149,7 +185,7 @@ class Config: # nohardlinks self.nohardlinks = None - if "nohardlinks" in self.data and self.args['tag_nohardlinks']: + if "nohardlinks" in self.data and self.commands['tag_nohardlinks']: self.nohardlinks = {} for cat in self.data["nohardlinks"]: if cat in list(self.data["cat"].keys()): @@ -170,7 +206,7 @@ class Config: self.notify(e, 'Config') raise Failed(e) else: - if self.args["tag_nohardlinks"]: + if self.commands["tag_nohardlinks"]: e = "Config Error: nohardlinks attribute not found" self.notify(e, 'Config') raise Failed(e) @@ -186,12 +222,12 @@ class Config: if "directory" in self.data: self.root_dir = os.path.join(self.util.check_for_attribute(self.data, "root_dir", parent="directory", default_is_none=True), '') self.remote_dir = os.path.join(self.util.check_for_attribute(self.data, "remote_dir", parent="directory", default=self.root_dir), '') - if (self.args["cross_seed"] or self.args["tag_nohardlinks"] or self.args["rem_orphaned"]): + if (self.commands["cross_seed"] or self.commands["tag_nohardlinks"] or self.commands["rem_orphaned"]): self.remote_dir = self.util.check_for_attribute(self.data, "remote_dir", parent="directory", var_type="path", default=self.root_dir) else: if self.recyclebin['enabled']: self.remote_dir = self.util.check_for_attribute(self.data, "remote_dir", parent="directory", var_type="path", default=self.root_dir) - if self.args["cross_seed"]: + if self.commands["cross_seed"]: self.cross_seed_dir = self.util.check_for_attribute(self.data, "cross_seed", parent="directory", var_type="path") else: self.cross_seed_dir = self.util.check_for_attribute(self.data, "cross_seed", parent="directory", default_is_none=True) @@ -325,12 +361,12 @@ class Config: # Empty the recycle bin def empty_recycle(self): - dry_run = self.args['dry_run'] + dry_run = self.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' num_del = 0 files = [] size_bytes = 0 - if not self.args["skip_recycle"]: + if not self.commands["skip_recycle"]: if self.recyclebin['enabled'] and self.recyclebin['empty_after_x_days']: if self.recyclebin['split_by_category']: if "cat" in self.data and self.data["cat"] is not None: diff --git a/modules/qbittorrent.py b/modules/qbittorrent.py index a5a7b09..2170b81 100644 --- a/modules/qbittorrent.py +++ b/modules/qbittorrent.py @@ -69,7 +69,7 @@ class Qbt: # is_complete = Returns the state of torrent (Returns True if at least one of the torrent with the State is categorized as Complete.) # first_hash = Returns the hash number of the original torrent (Assuming the torrent list is sorted by date added (Asc)) def get_torrent_info(torrent_list): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' torrentdict = {} t_obj_unreg = [] @@ -152,7 +152,7 @@ class Qbt: self.torrentinfo = None self.torrentissue = None self.torrentvalid = None - if config.args['recheck'] or config.args['cross_seed'] or config.args['rem_unregistered'] or config.args['tag_tracker_error'] or config.args['tag_nohardlinks']: + if config.commands['recheck'] or config.commands['cross_seed'] or config.commands['rem_unregistered'] or config.commands['tag_tracker_error'] or config.commands['tag_nohardlinks']: # Get an updated torrent dictionary information of the torrents self.torrentinfo, self.torrentissue, self.torrentvalid = get_torrent_info(self.torrent_list) @@ -160,7 +160,7 @@ class Qbt: return self.client.torrents.info(**params) def category(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' num_cat = 0 @@ -199,7 +199,7 @@ class Qbt: self.config.send_notifications(attr) num_cat += 1 - if self.config.args['cat_update']: + if self.config.commands['cat_update']: logger.separator("Updating Categories", space=False, border=False) torrent_list = self.get_torrents({'category': '', 'filter': 'completed'}) for torrent in torrent_list: @@ -221,11 +221,11 @@ class Qbt: return num_cat def tags(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' num_tags = 0 ignore_tags = self.config.settings['ignoreTags_OnUpdate'] - if self.config.args['tag_update']: + if self.config.commands['tag_update']: logger.separator("Updating Tags", space=False, border=False) for torrent in self.torrent_list: check_tags = util.get_list(torrent.tags) @@ -260,7 +260,7 @@ class Qbt: return num_tags def set_tags_and_limits(self, torrent, max_ratio, max_seeding_time, limit_upload_speed=None, tags=None, restore=False): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' body = [] # Print Logs @@ -296,14 +296,14 @@ class Qbt: return body def tag_nohardlinks(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' num_tags = 0 # counter for the number of torrents that has no hard links del_tor = 0 # counter for the number of torrents that has no hard links and meets the criteria for ratio limit/seed limit for deletion del_tor_cont = 0 # counter for the number of torrents that has no hard links and meets the criteria for ratio limit/seed limit for deletion including contents num_untag = 0 # counter for number of torrents that previously had no hard links but now have hard links - if self.config.args['tag_nohardlinks']: + if self.config.commands['tag_nohardlinks']: logger.separator("Tagging Torrents with No Hardlinks", space=False, border=False) nohardlinks = self.config.nohardlinks tdel_dict = {} # dictionary to track the torrent names and content path that meet the deletion criteria @@ -440,7 +440,7 @@ class Qbt: return num_tags, num_untag, del_tor, del_tor_cont def rem_unregistered(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' del_tor = 0 del_tor_cont = 0 @@ -448,8 +448,8 @@ class Qbt: num_untag = 0 tor_error_summary = '' tag_error = self.config.settings['tracker_error_tag'] - cfg_rem_unregistered = self.config.args['rem_unregistered'] - cfg_tag_error = self.config.args['tag_tracker_error'] + cfg_rem_unregistered = self.config.commands['rem_unregistered'] + cfg_tag_error = self.config.commands['tag_tracker_error'] def tag_tracker_error(): nonlocal dry_run, t_name, msg_up, msg, tracker, t_cat, torrent, tag_error, tor_error_summary, num_tor_error @@ -609,11 +609,11 @@ class Qbt: # Function used to move any torrents from the cross seed directory to the correct save directory def cross_seed(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' added = 0 # Keep track of total torrents tagged tagged = 0 # Track # of torrents tagged that are not cross-seeded - if self.config.args['cross_seed']: + if self.config.commands['cross_seed']: logger.separator("Checking for Cross-Seed Torrents", space=False, border=False) # List of categories for all torrents moved categories = [] @@ -697,11 +697,11 @@ class Qbt: # Function used to recheck paused torrents sorted by size and resume torrents that are completed def recheck(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' resumed = 0 rechecked = 0 - if self.config.args['recheck']: + if self.config.commands['recheck']: logger.separator("Rechecking Paused Torrents", space=False, border=False) # sort by size and paused torrent_list = self.get_torrents({'status_filter': 'paused', 'sort': 'size'}) @@ -764,10 +764,10 @@ class Qbt: return resumed, rechecked def rem_orphaned(self): - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] loglevel = 'DRYRUN' if dry_run else 'INFO' orphaned = 0 - if self.config.args['rem_orphaned']: + if self.config.commands['rem_orphaned']: logger.separator("Checking for Orphaned Files", space=False, border=False) torrent_files = [] root_files = [] diff --git a/modules/webhooks.py b/modules/webhooks.py index acdcc95..9dc70b6 100644 --- a/modules/webhooks.py +++ b/modules/webhooks.py @@ -72,7 +72,7 @@ class Webhooks: def start_time_hooks(self, start_time): if self.run_start_webhooks: - dry_run = self.config.args['dry_run'] + dry_run = self.config.commands['dry_run'] if dry_run: start_type = "Dry-" else: @@ -82,7 +82,7 @@ class Webhooks: "title": None, "body": f"Starting {start_type}Run", "start_time": start_time.strftime("%Y-%m-%d %H:%M:%S"), - "dry_run": self.config.args['dry_run'] + "dry_run": self.config.commands['dry_run'] }) def end_time_hooks(self, start_time, end_time, run_time, next_run, stats, body):