diff --git a/.github/renovate.json5 b/.github/renovate.json5 index c5820bb..f45d8f1 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,4 +2,4 @@ "extends": [ "config:base" ] -} \ No newline at end of file +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4102ced..8cec9d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,7 +21,6 @@ jobs: with: images: | ghcr.io/${{ github.repository }} - ghcr.io/buroa/qbittools tags: | type=ref,event=branch type=ref,event=pr diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3c85a17..a5ce8eb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,7 +19,6 @@ jobs: with: images: | ghcr.io/${{ github.repository }} - ghcr.io/buroa/qbittools flavor: | latest=true prefix=v diff --git a/Dockerfile b/Dockerfile index 2acc551..59f8975 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,12 +8,12 @@ RUN pip install --no-cache-dir --prefix=/install -r /requirements.txt \ FROM base as app WORKDIR /app -COPY qbittools/ . +COPY qbtools/ . COPY config.yaml . -RUN python3 -m compileall qbittools.py commands/ +RUN python3 -m compileall qbtools.py commands/ FROM base as final WORKDIR /app COPY --from=pip /install /usr/local COPY --from=app /app . -ENTRYPOINT ["python3", "qbittools.py"] +ENTRYPOINT ["python3", "qbtools.py"] diff --git a/README.md b/README.md index 60c9c1b..bbce2de 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is an opinionated fork of the upstream project at https://gitlab.com/AlexKM ## Description -qbittools is a feature rich CLI for the management of torrents in qBittorrent. +qbtools is a feature rich CLI for the management of torrents in qBittorrent. ## Table of contents @@ -29,23 +29,23 @@ qbittools is a feature rich CLI for the management of torrents in qBittorrent. Run a container with access to host network: ```bash -docker run -it --rm --network host github.com/buroa/qbittools tagging --unregistered +docker run -it --rm --network host github.com/buroa/qbtools tagging --unregistered ``` ### Building ```bash # clone the repository -git clone https://github.com/buroa/qbittools.git && cd qbittools +git clone https://github.com/buroa/qbtools.git && cd qbtools # build the image -docker build -t qbittools:latest --pull . +docker build -t qbtools:latest --pull . # run a container with the resulting binary and access to host network -docker run -it --rm --network host qbittools reannounce -p 12345 +docker run -it --rm --network host qbtools reannounce -p 12345 ``` ## Configuration -You have to specify your password every time with `-P` flag unless you enable `Web UI -> Bypass authentication for clients on localhost` in qBittorrent's settings, because there is no way for qBittools to retrieve it in plaintext. +You have to specify your password every time with `-P` flag unless you enable `Web UI -> Bypass authentication for clients on localhost` in qBittorrent's settings, because there is no way for qbtools to retrieve it in plaintext. You also can specify host, port and username manually without a configuration file with `-s`, `-p` and `-U` flags accordingly. @@ -58,8 +58,8 @@ There is also a `config.yaml` file which can be overrideen to add your own index All commands have extensive help with all available options. ```bash -$ qbittools export -h -usage: qbittools.py reannounce [-h] [--pause-resume] [--process-seeding] +$ qbtools export -h +usage: qbtools.py reannounce [-h] [--pause-resume] [--process-seeding] [-c /app/config.yaml] [-p 12345] [-s 127.0.0.1] [-U username] [-P password] @@ -83,7 +83,7 @@ options: Create useful tags to group torrents by tracker domains, not working trackers, unregistered torrents and duplicates ```bash -$ qbittools tagging --duplicates --unregistered --not-working --added-on --trackers +$ qbtools tagging --duplicates --unregistered --not-working --added-on --trackers ``` #### Reannounce @@ -91,7 +91,7 @@ $ qbittools tagging --duplicates --unregistered --not-working --added-on --track Automatic reannounce on problematic trackers ```bash -$ qbittools reannounce +$ qbtools reannounce 07:40:40 PM -------------------------- 07:40:40 PM [Movie.2020.2160p.WEB-DL.H264-GROUP] is not working, active for 1s, reannouncing... 07:41:20 PM -------------------------- @@ -129,5 +129,5 @@ This is how I have my paths laid out where `/downloads/qbittorrent/complete` is ``` ```bash -$ qbittools orphaned --ignore-pattern "*_unpackerred" --ignore-pattern "*/manual/*" +$ qbtools orphaned --ignore-pattern "*_unpackerred" --ignore-pattern "*/manual/*" ``` diff --git a/qbittools/commands/orphaned.py b/qbtools/commands/orphaned.py similarity index 93% rename from qbittools/commands/orphaned.py rename to qbtools/commands/orphaned.py index 057e65d..8dbb31a 100644 --- a/qbittools/commands/orphaned.py +++ b/qbtools/commands/orphaned.py @@ -2,12 +2,12 @@ import shutil from fnmatch import fnmatch -import qbittools +import qbtools def __init__(args, logger): logger.info(f"Checking for orphaned files on disk not in qBittorrent...") - client = qbittools.qbit_client(args) + client = qbtools.qbit_client(args) completed_dir = client.application.preferences.save_path categories = [ @@ -74,12 +74,12 @@ def add_arguments(subparser): Description: Search for files on disk that are not in qBittorrent and delete them. Pair this with the prune command to delete torrents that are not in qBittorrent. Usage: - qbittools.py orphaned --help + qbtools.py orphaned --help Example: # Delete all files in the completed directory that are not in qBittorrent and don't match the exclude patterns - qbittools.py orphaned --exclude-pattern "*_unpackerred" --exclude-pattern "*/manual/*" --dry-run + qbtools.py orphaned --exclude-pattern "*_unpackerred" --exclude-pattern "*/manual/*" --dry-run """ parser = subparser.add_parser('orphaned') parser.add_argument('--exclude-pattern', nargs='*', action='append', metavar='mypattern', default=[], help='Exclude pattern, can be repeated multiple times', required=False) parser.add_argument('--dry-run', action='store_true', help='Do not delete any data on disk', default=False, required=False) - qbittools.add_default_args(parser) + qbtools.add_default_args(parser) diff --git a/qbittools/commands/prune.py b/qbtools/commands/prune.py similarity index 90% rename from qbittools/commands/prune.py rename to qbtools/commands/prune.py index 5978fcd..55f25d7 100644 --- a/qbittools/commands/prune.py +++ b/qbtools/commands/prune.py @@ -1,9 +1,9 @@ from fnmatch import fnmatch -import qbittools +import qbtools def __init__(args, logger): - client = qbittools.qbit_client(args) + client = qbtools.qbit_client(args) categories = list(client.torrent_categories.categories.keys()) if len(args.include_category) > 0: @@ -35,7 +35,7 @@ def __init__(args, logger): logger.info(f"Pruning torrents with tags [{' AND '.join(include_tags)}] but does not contain tags [{' OR '.join(exclude_tags)}]...") for t in filtered_torrents: - logger.info(f"Pruned torrent {t['name']} with category [{t.category}] and tags [{t.tags}] and ratio [{round(t['ratio'], 2)}] and seeding time [{qbittools.utils.dhms(t['seeding_time'])}]") + logger.info(f"Pruned torrent {t['name']} with category [{t.category}] and tags [{t.tags}] and ratio [{round(t['ratio'], 2)}] and seeding time [{qbtools.utils.dhms(t['seeding_time'])}]") if not args.dry_run: t.delete(delete_files=args.with_data) @@ -46,10 +46,10 @@ def add_arguments(subparser): Description: Prune torrents that have matching tags. Pair this with the tagging command to tag torrents. Usage: - qbittools.py prune --help + qbtools.py prune --help Example: # Delete torrents with the tag 'expired' and 'added:30d', however if the torrent also has the tags 'site:oink' or 'site:whatcd' exclude it from being deleted - qbittools.py prune --include-tag expired --include-tag added:30d --exclude-tag site:oink --exclude-tag site:whatcd --dry-run + qbtools.py prune --include-tag expired --include-tag added:30d --exclude-tag site:oink --exclude-tag site:whatcd --dry-run """ parser = subparser.add_parser('prune') parser.add_argument('--include-tag', nargs='*', action='append', metavar='mytag', default=[], help='Include torrents containing all of these tags, can be repeated multiple times', required=True) @@ -58,4 +58,4 @@ def add_arguments(subparser): parser.add_argument('--exclude-category', nargs='*', action='append', metavar='mycategory', default=[], help='Exclude torrents from category that matches this pattern, can be repeated multiple times', required=False) parser.add_argument('--dry-run', action='store_true', help='Do not delete torrents', default=False, required=False) parser.add_argument('--with-data', action='store_true', help='Delete torrents with data', default=False, required=False) - qbittools.add_default_args(parser) + qbtools.add_default_args(parser) diff --git a/qbittools/commands/reannounce.py b/qbtools/commands/reannounce.py similarity index 96% rename from qbittools/commands/reannounce.py rename to qbtools/commands/reannounce.py index 6247719..4c88cef 100755 --- a/qbittools/commands/reannounce.py +++ b/qbtools/commands/reannounce.py @@ -1,8 +1,8 @@ import time -import qbittools +import qbtools def __init__(args, logger): - client = qbittools.qbit_client(args) + client = qbtools.qbit_client(args) iterations = 0 timeout = 5 @@ -73,4 +73,4 @@ def add_arguments(subparser): parser = subparser.add_parser("reannounce") parser.add_argument("--pause-resume", action="store_true", help="Pause+resume torrents that are invalid.") parser.add_argument("--process-seeding", action="store_true", help="Include seeding torrents for reannouncements.") - qbittools.add_default_args(parser) + qbtools.add_default_args(parser) diff --git a/qbittools/commands/tagging.py b/qbtools/commands/tagging.py similarity index 93% rename from qbittools/commands/tagging.py rename to qbtools/commands/tagging.py index 05ed9bc..719de72 100755 --- a/qbittools/commands/tagging.py +++ b/qbtools/commands/tagging.py @@ -2,7 +2,7 @@ import collections from datetime import datetime -import qbittools +import qbtools DEFAULT_TAGS = [ 'activity:', @@ -56,8 +56,8 @@ def __init__(args, logger): logger.info(f"Tagging torrents in qBittorrent...") - client = qbittools.qbit_client(args) - trackers = qbittools.get_config(args, "trackers", []) + client = qbtools.qbit_client(args) + trackers = qbtools.get_config(args, "trackers", []) extractTLD = tldextract.TLDExtract(cache_dir=None) today = datetime.today() @@ -80,7 +80,7 @@ def __init__(args, logger): # TODO: Optimize - this slows down the script a lot filtered_trackers = list(filter(lambda s: not s.url in DHT_MATCHES, t.trackers)) domain = extractTLD(sorted(filtered_trackers, key=lambda x: x.url)[0].url).registered_domain - tracker = qbittools.utils.filter_tracker_by_domain(domain, trackers) + tracker = qbtools.utils.filter_tracker_by_domain(domain, trackers) if args.added_on: added_on = datetime.fromtimestamp(t.added_on) @@ -135,7 +135,7 @@ def __init__(args, logger): if args.expired and tracker and t.state_enum.is_complete: if tracker['required_seed_ratio'] != 0 and t.ratio >= tracker['required_seed_ratio']: tags_to_add.append('expired') - elif tracker['required_seed_days'] != 0 and t.seeding_time >= qbittools.utils.seconds(tracker['required_seed_days']): + elif tracker['required_seed_days'] != 0 and t.seeding_time >= qbtools.utils.seconds(tracker['required_seed_days']): tags_to_add.append('expired') if args.duplicates: @@ -148,7 +148,7 @@ def __init__(args, logger): content_paths.append((t.hash, t.content_path, t.size)) - if args.not_linked and not qbittools.utils.is_linked(t.content_path): + if args.not_linked and not qbtools.utils.is_linked(t.content_path): tags_to_add.append('not-linked') for tag in tags_to_add: @@ -173,7 +173,7 @@ def __init__(args, logger): # Apply tags for tag in tag_hashes: if args.size: - size = qbittools.utils.format_bytes(tag_sizes[tag]) + size = qbtools.utils.format_bytes(tag_sizes[tag]) client.torrents_add_tags(tags=f"{tag} [{size}]", torrent_hashes=tag_hashes[tag]) else: client.torrents_add_tags(tags=tag, torrent_hashes=tag_hashes[tag]) @@ -185,10 +185,10 @@ def add_arguments(subparser): Description: Tag torrents. This command can be used to tag torrents with various tags, such as torrents that have not been active for a while, torrents that have not been working for a while, torrents that have expired an ratio or seeding time, torrents that have the same content path, etc. Usage: - qbittools.py tagging --help + qbtools.py tagging --help Example: # Tag torrents - qbittools.py tagging --exclude-category manual --added-on --expired --last-activity --sites --unregistered + qbtools.py tagging --exclude-category manual --added-on --expired --last-activity --sites --unregistered """ parser = subparser.add_parser('tagging') parser.add_argument('--exclude-category', nargs='*', action='append', metavar='mycategory', default=[], help='Exclude all torrent with this category, can be repeated multiple times', required=False) @@ -204,4 +204,4 @@ def add_arguments(subparser): parser.add_argument('--size', action='store_true', help='Add size of tagged torrents to created tags') parser.add_argument('--tracker-down', action='store_true', help='Tag torrents with temporarily down trackers') parser.add_argument('--unregistered', action='store_true', help='Tag torrents with unregistered tracker status message') - qbittools.add_default_args(parser) + qbtools.add_default_args(parser) diff --git a/qbittools/qbittools.py b/qbtools/qbtools.py similarity index 100% rename from qbittools/qbittools.py rename to qbtools/qbtools.py diff --git a/qbittools/utils.py b/qbtools/utils.py similarity index 100% rename from qbittools/utils.py rename to qbtools/utils.py