From e06fff7733b12f206d763cc6e2aac2bef53c3cff Mon Sep 17 00:00:00 2001 From: Steven Kreitzer Date: Wed, 2 Oct 2024 15:18:37 -0500 Subject: [PATCH] feat(qbtools): support env variables (#74) --- qbtools/commands/reannounce.py | 22 ++++++++++++-- qbtools/commands/tagging.py | 17 ++++++----- qbtools/qbtools.py | 53 ++++++++++++++++++++++------------ qbtools/utils.py | 17 +++++++++++ 4 files changed, 80 insertions(+), 29 deletions(-) diff --git a/qbtools/commands/reannounce.py b/qbtools/commands/reannounce.py index 4d037be..0aaca0e 100755 --- a/qbtools/commands/reannounce.py +++ b/qbtools/commands/reannounce.py @@ -29,17 +29,33 @@ def process_torrents(status): peers = t.num_seeds + t.num_leechs if peers: - logger.debug("Torrent %s (%s) has %d peer(s) - not reannouncing", t.name, t.hash, peers) + logger.debug( + "Torrent %s (%s) has %d peer(s) - not reannouncing", + t.name, + t.hash, + peers, + ) continue torrent_retries = torrents_retries.get(t.hash, 0) if torrent_retries >= max_tries: - logger.debug("Torrent %s (%s) has reached %s reannounce tries - not reannouncing", t.name, t.hash, retries) + logger.debug( + "Torrent %s (%s) has reached %s reannounce tries - not reannouncing", + t.name, + t.hash, + retries, + ) continue t.reannounce() torrents_retries[t.hash] = torrent_retries + 1 - logger.info("Reannounced torrent %s (%s) %s/%s", t.name, t.hash, torrent_retries, max_tries) + logger.info( + "Reannounced torrent %s (%s) %s/%s", + t.name, + t.hash, + torrent_retries, + max_tries, + ) retries[status] = torrents_retries diff --git a/qbtools/commands/tagging.py b/qbtools/commands/tagging.py index 02cf4a4..110ef4d 100755 --- a/qbtools/commands/tagging.py +++ b/qbtools/commands/tagging.py @@ -29,8 +29,8 @@ "TRUMP", "RETITLED", "TRUNCATED", - "INFOHASH NOT FOUND", # blutopia - "TORRENT HAS BEEN DELETED", # blutopia + "INFOHASH NOT FOUND", # blutopia + "TORRENT HAS BEEN DELETED", # blutopia "DEAD", "DUPE", "COMPLETE SEASON UPLOADED", @@ -135,9 +135,13 @@ def __init__(app, logger): if app.unregistered or app.tracker_down or app.not_working: if not any(s.status == TrackerStatus.WORKING for s in filtered_trackers): tracker_messages = [z.msg.upper() for z in filtered_trackers] - if app.unregistered and any(x in msg for msg in tracker_messages for x in UNREGISTERED_MATCHES): + if app.unregistered and any( + x in msg for msg in tracker_messages for x in UNREGISTERED_MATCHES + ): tags_to_add.append("unregistered") - elif app.tracker_down and any(x in msg for msg in tracker_messages for x in MAINTENANCE_MATCHES): + elif app.tracker_down and any( + x in msg for msg in tracker_messages for x in MAINTENANCE_MATCHES + ): tags_to_add.append("tracker-down") elif app.not_working: tags_to_add.append("not-working") @@ -148,9 +152,7 @@ def __init__(app, logger): and t.ratio >= tracker["required_seed_ratio"] ): tags_to_add.append("expired") - elif tracker[ - "required_seed_days" - ] != 0 and t.seeding_time >= utils.seconds( + elif tracker["required_seed_days"] != 0 and t.seeding_time >= utils.seconds( tracker["required_seed_days"] ): tags_to_add.append("expired") @@ -229,7 +231,6 @@ def add_arguments(subparser): # Tag torrents qbtools.py tagging --exclude-category manual --added-on --expired --last-activity --sites --unregistered """ - print(__name__) parser = subparser.add_parser("tagging") parser.add_argument( "--exclude-category", diff --git a/qbtools/qbtools.py b/qbtools/qbtools.py index b7e9949..573ca80 100755 --- a/qbtools/qbtools.py +++ b/qbtools/qbtools.py @@ -14,45 +14,62 @@ def add_default_args(parser): parser.add_argument( - "-c", "--config", + "-c", + "--config", default="/config/config.yaml", - help="Path to configuration file" + help="Path to configuration file", ) parser.add_argument( - "-s", "--server", + "-s", + "--server", + action=utils.EnvDefault, + envvar="QBITTORRENT_HOST", help="qBittorrent server address", - required=True + required=True, ) parser.add_argument( - "-p", "--port", + "-p", + "--port", + action=utils.EnvDefault, + envvar="QBITTORRENT_PORT", help="qBittorrent server port", - required=True + required=True, ) parser.add_argument( - "-U", "--username", - help="Username for qBittorrent" + "-U", + "--username", + action=utils.EnvDefault, + envvar="QBITTORRENT_USER", + help="Username for qBittorrent", + required=False, ) parser.add_argument( - "-P", "--password", - help="Password for qBittorrent" + "-P", + "--password", + action=utils.EnvDefault, + envvar="QBITTORRENT_PASS", + help="Password for qBittorrent", + required=False, ) def load_commands(subparsers): - def load_command(name): + directory = "commands" + + def load_command(command): try: - mod = importlib.import_module(f"commands.{name}") + mod = importlib.import_module(f"{directory}.{command}") mod.add_arguments(subparsers) - subparser = subparsers.choices.get(name) + subparser = subparsers.choices.get(command) if subparser: add_default_args(subparser) except ImportError: - logger.error(f"Error loading module: {name}", exc_info=True) + logger.error(f"Error loading module: {command}", exc_info=True) sys.exit(1) else: - globals()[name] = mod + globals()[command] = mod - for cmd in os.listdir(f"{os.path.dirname(__file__)}/commands"): + for cmd in os.listdir(f"{os.path.dirname(__file__)}/{directory}"): if cmd.startswith("__") or not cmd.endswith(".py"): continue load_command(cmd[:-3]) @@ -102,8 +119,7 @@ def main(): parser = argparse.ArgumentParser(description="qBittorrent API Client") subparsers = parser.add_subparsers(dest="command") - load_commands(subparsers) # Load all commands - + load_commands(subparsers) # Load all commands app = parser.parse_args() if not app.command: @@ -122,5 +138,6 @@ def main(): finally: app.client.auth_log_out() + if __name__ == "__main__": main() diff --git a/qbtools/utils.py b/qbtools/utils.py index 50dd2de..d1609aa 100644 --- a/qbtools/utils.py +++ b/qbtools/utils.py @@ -1,3 +1,7 @@ +import os +import argparse + + def format_bytes(size): power = 2**10 n = 0 @@ -35,3 +39,16 @@ def dhms(total_seconds: int) -> str: days = total_hours // 24 hours = total_hours % 24 return f"{days}d{hours}h{minutes}m{seconds}s" + + +class EnvDefault(argparse.Action): + def __init__(self, envvar, required=True, default=None, **kwargs): + if envvar: + if envvar in os.environ: + default = os.environ[envvar] + if required and default: + required = False + super(EnvDefault, self).__init__(default=default, required=required, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, values)