From dfbb1c6c5d540db11eada234f83e186210df05cf Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Fri, 9 Jun 2023 17:11:50 -0400 Subject: [PATCH 01/13] Allow defining multiple announce urls for one tracker --- modules/config.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/config.py b/modules/config.py index ecad9539..64d4aee2 100755 --- a/modules/config.py +++ b/modules/config.py @@ -100,7 +100,12 @@ def __init__(self, default_dir, args): if "cat_change" in self.data: self.data["cat_change"] = self.data.pop("cat_change") if "tracker" in self.data: - self.data["tracker"] = self.data.pop("tracker") + trackers = self.data.pop("tracker") + self.data["tracker"] = {} + # Splits tracker urls at pipes, useful for trackers with multiple announce urls + for tracker_urls, data in trackers.items(): + for tracker_url in tracker_urls.split("|"): + self.data["tracker"][tracker_url] = data else: self.data["tracker"] = {} if "nohardlinks" in self.data: From b93eec766522ac55e17fe6d11f0c0c93179243fc Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 13:20:48 -0400 Subject: [PATCH 02/13] cross-seed moves file to error folder if it fails to inject torrent --- CHANGELOG | 22 +++++----------------- VERSION | 2 +- modules/core/cross_seed.py | 8 ++++++-- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bcf014ed..09739e40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,23 +1,11 @@ # Requirements Updated -- Updates ruamel.yaml to 0.17.31 -- Updates qbitorrent-api to 2023.5.48 -- Separate out dev requirements into requirements-dev.txt + # Breaking Changes -- `tag_nohardlinks` only updates/removes `noHL` tag. **It does not modify or cleanup share_limits anymore.** -- `tag_update` only adds tracker tags to torrent. **It does not modify or cleanup share_limits anymore.** -- Please remove any references to share_limits from your configuration in the tracker/nohardlinks section -- Migration guide can be followed here: [V4 Migration Guide](https://github.com/StuffAnThings/qbit_manage/wiki/v4-Migration-Guide) -- Webhook payloads changed (See [webhooks](https://github.com/StuffAnThings/qbit_manage/wiki/Config-Setup#webhooks) for updated payload) # New Features -- Adds new command `share_limits`, `--share-limits` , `QBT_SHARE_LIMITS=True` to update share limits based on tags/categories specified per group (Closes #88, Closes #306, Closes #259, Closes #308, Closes #137) -- See [Config Setup - share_limits](https://github.com/StuffAnThings/qbit_manage/wiki/Config-Setup#share_limits) for more details -- Adds new command `skip_qb_version_check`, `--skip-qb-version-check`, `QBT_SKIP_QB_VERSION_CHECK` to bypass qbitorrent compatibility check (unsupported - Thanks to @ftc2 #307) -- Updates to webhook notifications to group notifications when a function updates more than 10 Torrents. -- Adds new webhooks for `share_limits` -- Adds rate limit to webhook notifications (1 msg/sec) +- cross-seed will move torrent to an error folder if it fails to inject torrent + # Bug Fixes -- Fixes #302 -- Fixes #317 -**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v3.6.4...v4.0.0 + +**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.0...v4.0.1 diff --git a/VERSION b/VERSION index fcdb2e10..4db799f1 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.0 +4.0.1-develop1 diff --git a/modules/core/cross_seed.py b/modules/core/cross_seed.py index 7baef976..3f0d0e5d 100644 --- a/modules/core/cross_seed.py +++ b/modules/core/cross_seed.py @@ -31,18 +31,21 @@ def cross_seed(self): dir_cs = self.config.cross_seed_dir dir_cs_out = os.path.join(dir_cs, "qbit_manage_added") os.makedirs(dir_cs_out, exist_ok=True) + dir_cs_err = os.path.join(dir_cs, "qbit_manage_error") + os.makedirs(dir_cs_err, exist_ok=True) for file in cs_files: tr_name = file.split("]", 2)[2].split(".torrent")[0] t_tracker = file.split("]", 2)[1][1:] # Substring Key match in dictionary (used because t_name might not match exactly with self.qbt.torrentinfo key) # Returned the dictionary of filtered item torrentdict_file = dict(filter(lambda item: tr_name in item[0], self.qbt.torrentinfo.items())) + src = os.path.join(dir_cs, file) + dir_cs_out = os.path.join(dir_cs_out, file) + dir_cs_err = os.path.join(dir_cs_err, file) if torrentdict_file: # Get the exact torrent match name from self.qbt.torrentinfo t_name = next(iter(torrentdict_file)) dest = os.path.join(self.qbt.torrentinfo[t_name]["save_path"], "") - src = os.path.join(dir_cs, file) - dir_cs_out = os.path.join(dir_cs, "qbit_manage_added", file) category = self.qbt.torrentinfo[t_name].get("Category", self.qbt.get_category(dest)) # Only add cross-seed torrent if original torrent is complete if self.qbt.torrentinfo[t_name]["is_complete"]: @@ -98,6 +101,7 @@ def cross_seed(self): logger.print_line(error, self.config.loglevel) else: logger.print_line(error, "WARNING") + util.move_files(src, dir_cs_err) self.config.notify(error, "cross-seed", False) self.config.webhooks_factory.notify(self.torrents_updated, self.notify_attr, group_by="category") From d0421f2b7d6ab40cd3b4f6c838cb5cc11c192865 Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 15:33:09 -0400 Subject: [PATCH 03/13] fixes #329 (updates missing share_limits tag when share_limits are satisfied) Modifies logic for share_limits_suffix_tag to become share_limits_tag. This change adds share_limits_tag to become prefix rather than suffix and adds additional priority information --- CHANGELOG | 16 ++++++++++++++-- VERSION | 2 +- config/config.yml.sample | 2 +- modules/config.py | 11 ++++++++--- modules/core/share_limits.py | 35 +++++++++++++++++++++++++++-------- modules/core/tags.py | 4 ++-- modules/util.py | 7 +++++++ 7 files changed, 60 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 09739e40..f7cf940c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,23 @@ # Requirements Updated -# Breaking Changes - # New Features - cross-seed will move torrent to an error folder if it fails to inject torrent # Bug Fixes +- Fixes #329 (Updates missing share_limits tag even when share_limits are satisfied) + +# Enhancements +- Logic for `share_limits_suffix_tag` changed to become a prefix tag instead along with adding the priority of the group. The reason for this change is so it's easier to see the share limit groups togethered in qbitorrent ordered by priority. + - `share_limits_suffix_tag` key is now `share_limits_tag` + - No config changes are required as the qbm will automatically change the previous `share_limits_suffix_tag` key to `share_limits_tag` + +Example based on config.sample: +| old tag (v4.0.0) | new tag (v4.0.1) | +| ----------- | ----------- | +| noHL.share_limits | share_limits_1.noHL | +| cross-seed.share_limits | share_limits_2.cross-seed | +| PTP.share_limits | share_limits_3.PTP | +| default.share_limits | share_limits_999.default | **Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.0...v4.0.1 diff --git a/VERSION b/VERSION index 4db799f1..03790529 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop1 +4.0.1-develop2 diff --git a/config/config.yml.sample b/config/config.yml.sample index 40b8e5de..76c6a2ab 100755 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -28,7 +28,7 @@ settings: force_auto_tmm: False # Will force qBittorrent to enable Automatic Torrent Management for each torrent. tracker_error_tag: issue # Will set the tag of any torrents that do not have a working tracker. nohardlinks_tag: noHL # Will set the tag of any torrents with no hardlinks. - share_limits_suffix_tag: share_limit # Will add this suffix to the grouping separated by '.' to the tag of any torrents with share limits. + share_limits_tag: share_limit # Will add this tag when applying share limits to provide an easy way to filter torrents by share limit group/priority for each torrent ignoreTags_OnUpdate: # When running tag-update function, it will update torrent tags for a given torrent even if the torrent has at least one or more of the tags defined here. Otherwise torrents will not be tagged if tags exist. - noHL - issue diff --git a/modules/config.py b/modules/config.py index ecad9539..4e6dfa19 100755 --- a/modules/config.py +++ b/modules/config.py @@ -146,6 +146,8 @@ def hooks(attr): self.loglevel = "DRYRUN" if self.dry_run else "INFO" self.session = requests.Session() + share_limits_tag = self.data["settings"].get("share_limits_suffix_tag", "share_limits") + self.settings = { "force_auto_tmm": self.util.check_for_attribute( self.data, "force_auto_tmm", parent="settings", var_type="bool", default=False @@ -154,19 +156,22 @@ def hooks(attr): self.data, "tracker_error_tag", parent="settings", default="issue" ), "nohardlinks_tag": self.util.check_for_attribute(self.data, "nohardlinks_tag", parent="settings", default="noHL"), - "share_limits_suffix_tag": self.util.check_for_attribute( - self.data, "share_limits_suffix_tag", parent="settings", default="share_limit" + "share_limits_tag": self.util.check_for_attribute( + self.data, "share_limits_tag", parent="settings", default=share_limits_tag ), } self.tracker_error_tag = self.settings["tracker_error_tag"] self.nohardlinks_tag = self.settings["nohardlinks_tag"] - self.share_limits_suffix_tag = "." + self.settings["share_limits_suffix_tag"] + self.share_limits_tag = self.settings["share_limits_tag"] default_ignore_tags = [self.nohardlinks_tag, self.tracker_error_tag, "cross-seed"] self.settings["ignoreTags_OnUpdate"] = self.util.check_for_attribute( self.data, "ignoreTags_OnUpdate", parent="settings", default=default_ignore_tags, var_type="list" ) + "Migrate settings from v4.0.0 to v4.0.1 and beyond. Convert 'share_limits_suffix_tag' to 'share_limits_tag'" + if "share_limits_suffix_tag" in self.data["settings"]: + self.util.overwrite_attributes(self.settings, "settings") default_function = { "cross_seed": None, diff --git a/modules/core/share_limits.py b/modules/core/share_limits.py index 2e5b6159..9fb092a4 100644 --- a/modules/core/share_limits.py +++ b/modules/core/share_limits.py @@ -24,10 +24,11 @@ def __init__(self, qbit_manager): self.share_limits_config = qbit_manager.config.share_limits # configuration of share limits self.torrents_updated = [] # list of torrents that have been updated self.torrent_hash_checked = [] # list of torrent hashes that have been checked for share limits - self.share_limits_suffix_tag = qbit_manager.config.share_limits_suffix_tag # suffix tag for share limits + self.share_limits_tag = qbit_manager.config.share_limits_tag # tag for share limits self.group_tag = None # tag for the share limit group self.update_share_limits() + self.delete_share_limits_suffix_tag() def update_share_limits(self): """Updates share limits for torrents based on grouping""" @@ -166,6 +167,9 @@ def update_share_limits_for_group(self, group_name, group_config, torrents): for torrent in torrents: t_name = torrent.name t_hash = torrent.hash + self.group_tag = ( + f"{self.share_limits_tag}_{group_config['priority']}.{group_name}" if group_config["add_group_to_tag"] else None + ) tracker = self.qbt.get_tags(torrent.trackers) check_max_ratio = group_config["max_ratio"] != torrent.max_ratio check_max_seeding_time = group_config["max_seeding_time"] != torrent.max_seeding_time @@ -175,6 +179,7 @@ def update_share_limits_for_group(self, group_name, group_config, torrents): group_config["limit_upload_speed"] = -1 check_limit_upload_speed = group_config["limit_upload_speed"] != torrent_upload_limit hash_not_prev_checked = t_hash not in self.torrent_hash_checked + share_limits_not_yet_tagged = True if self.group_tag and self.group_tag not in torrent.tags else False logger.trace(f"Torrent: {t_name} [Hash: {t_hash}]") logger.trace(f"Torrent Category: {torrent.category}") logger.trace(f"Torrent Tags: {torrent.tags}") @@ -192,9 +197,11 @@ def update_share_limits_for_group(self, group_name, group_config, torrents): ) logger.trace(f"check_limit_upload_speed: {check_limit_upload_speed}") logger.trace(f"hash_not_prev_checked: {hash_not_prev_checked}") - if (check_max_ratio or check_max_seeding_time or check_limit_upload_speed) and hash_not_prev_checked: + logger.trace(f"share_limits_not_yet_tagged: {share_limits_not_yet_tagged}") + if ( + check_max_ratio or check_max_seeding_time or check_limit_upload_speed or share_limits_not_yet_tagged + ) and hash_not_prev_checked: if "MinSeedTimeNotReached" not in torrent.tags: - self.group_tag = f"{group_name}{self.share_limits_suffix_tag}" if group_config["add_group_to_tag"] else None logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel) logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel) if self.group_tag: @@ -226,7 +233,7 @@ def tag_and_update_share_limits_for_torrent(self, torrent, group_config): # Remove previous share_limits tag tags = util.get_list(torrent.tags) for tag in tags: - if self.share_limits_suffix_tag in tag: + if self.share_limits_tag in tag: torrent.remove_tags(tag) # Will tag the torrent with the group name if add_group_to_tag is True and set the share limits @@ -371,8 +378,11 @@ def _has_reached_min_seeding_time_limit(): print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel) print_log += logger.print_line( logger.insert_space( - f"Min seed time not met: {timedelta(seconds=torrent.seeding_time)} <= " - f"{timedelta(minutes=min_seeding_time)}. Removing Share Limits so qBittorrent can continue seeding.", + ( + f"Min seed time not met: {timedelta(seconds=torrent.seeding_time)} <=" + f" {timedelta(minutes=min_seeding_time)}. Removing Share Limits so qBittorrent can continue" + " seeding." + ), 8, ), self.config.loglevel, @@ -401,8 +411,10 @@ def _has_reached_seeding_time_limit(): if seeding_time_limit: if (torrent.seeding_time >= seeding_time_limit * 60) and _has_reached_min_seeding_time_limit(): body += logger.insert_space( - f"Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} >= " - f"{timedelta(minutes=seeding_time_limit)}", + ( + f"Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} >= " + f"{timedelta(minutes=seeding_time_limit)}" + ), 8, ) return True @@ -422,3 +434,10 @@ def _has_reached_seeding_time_limit(): if _has_reached_seeding_time_limit(): return body return False + + def delete_share_limits_suffix_tag(self): + """ "Delete Share Limits Suffix Tag from version 4.0.0""" + tags = self.client.torrent_tags.tags + for tag in tags: + if tag.endswith(f".{self.share_limits_tag}"): + self.client.torrent_tags.delete_tags(tag) diff --git a/modules/core/tags.py b/modules/core/tags.py index 540571f8..7eaf2436 100644 --- a/modules/core/tags.py +++ b/modules/core/tags.py @@ -9,7 +9,7 @@ def __init__(self, qbit_manager): self.config = qbit_manager.config self.client = qbit_manager.client self.stats = 0 - self.share_limits_suffix_tag = qbit_manager.config.share_limits_suffix_tag # suffix tag for share limits + self.share_limits_tag = qbit_manager.config.share_limits_tag # suffix tag for share limits self.torrents_updated = [] # List of torrents updated self.notify_attr = [] # List of single torrent attributes to send to notifiarr @@ -21,7 +21,7 @@ def tags(self): ignore_tags = self.config.settings["ignoreTags_OnUpdate"] logger.separator("Updating Tags", space=False, border=False) for torrent in self.qbt.torrent_list: - check_tags = [tag for tag in util.get_list(torrent.tags) if self.share_limits_suffix_tag not in tag] + check_tags = [tag for tag in util.get_list(torrent.tags) if self.share_limits_tag not in tag] if torrent.tags == "" or (len([trk for trk in check_tags if trk not in ignore_tags]) == 0): tracker = self.qbt.get_tags(torrent.trackers) diff --git a/modules/util.py b/modules/util.py index 97a47962..109e619c 100755 --- a/modules/util.py +++ b/modules/util.py @@ -136,6 +136,13 @@ class check: def __init__(self, config): self.config = config + def overwrite_attributes(self, data, attribute): + """Overwrite attributes in config.""" + yaml = YAML(self.config.config_path) + if data is not None and attribute in yaml.data: + yaml.data[attribute] = data + yaml.save() + def check_for_attribute( self, data, From 2f779e1960d3419e990df0d18ea03eded3c5032f Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 15:54:52 -0400 Subject: [PATCH 04/13] black formatting + precommit updates --- .pre-commit-config.yaml | 2 +- Makefile | 10 +++++- VERSION | 2 +- modules/config.py | 30 ++++++++++------- modules/core/recheck.py | 8 +++-- modules/core/remove_orphaned.py | 6 ++-- modules/core/remove_unregistered.py | 18 +++++++---- modules/core/tag_nohardlinks.py | 16 +++++---- modules/webhooks.py | 18 +++++++---- qbit_manage.py | 34 +++++++++++++------- scripts/delete_torrents_on_low_disk_space.py | 4 +-- setup.py | 6 ++-- tox.ini | 20 +++++++++--- 13 files changed, 117 insertions(+), 57 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 92c8631f..ecc89990 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,7 +45,7 @@ repos: hooks: - id: black language_version: python3 - args: [--line-length, '130'] + args: [--line-length, '130', --preview] - repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks: diff --git a/Makefile b/Makefile index 603b50fb..788e1853 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,11 @@ venv: requirements.txt setup.py tox.ini .PHONY: test test: - tox + tox -e tests + +.PHONY: pre-commit +pre-commit: + tox -e pre-commit .PHONY: clean clean: @@ -14,3 +18,7 @@ clean: find -name '__pycache__' -delete rm -rf .tox rm -rf venv + +.PHONY: install-hooks +install-hooks: + tox -e install-hooks diff --git a/VERSION b/VERSION index 03790529..a1b24da8 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop2 +4.0.1-develop3 diff --git a/modules/config.py b/modules/config.py index 4e6dfa19..07bef42a 100755 --- a/modules/config.py +++ b/modules/config.py @@ -308,7 +308,7 @@ def _sort_share_limits(share_limits): else: priority = max(priorities) + 1 logger.warning( - f"Priority not defined for the grouping '{key}' in share_limits. " f"Setting priority to {priority}" + f"Priority not defined for the grouping '{key}' in share_limits. Setting priority to {priority}" ) value["priority"] = self.util.check_for_attribute( self.data, @@ -543,14 +543,18 @@ def _sort_share_limits(share_limits): ) if self.commands["rem_orphaned"]: exclude_orphaned = f"**{os.sep}{os.path.basename(self.orphaned_dir.rstrip(os.sep))}{os.sep}*" - self.orphaned["exclude_patterns"].append(exclude_orphaned) if exclude_orphaned not in self.orphaned[ - "exclude_patterns" - ] else self.orphaned["exclude_patterns"] + ( + self.orphaned["exclude_patterns"].append(exclude_orphaned) + if exclude_orphaned not in self.orphaned["exclude_patterns"] + else self.orphaned["exclude_patterns"] + ) if self.recyclebin["enabled"]: exclude_recycle = f"**{os.sep}{os.path.basename(self.recycle_dir.rstrip(os.sep))}{os.sep}*" - self.orphaned["exclude_patterns"].append(exclude_recycle) if exclude_recycle not in self.orphaned[ - "exclude_patterns" - ] else self.orphaned["exclude_patterns"] + ( + self.orphaned["exclude_patterns"].append(exclude_recycle) + if exclude_recycle not in self.orphaned["exclude_patterns"] + else self.orphaned["exclude_patterns"] + ) # Connect to Qbittorrent self.qbt = None @@ -640,8 +644,10 @@ def cleanup_dirs(self, location): if empty_after_x_days <= days: num_del += 1 body += logger.print_line( - f"{'Did not delete' if self.dry_run else 'Deleted'} " - f"{filename} from {folder} (Last modified {round(days)} days ago).", + ( + f"{'Did not delete' if self.dry_run else 'Deleted'} " + f"{filename} from {folder} (Last modified {round(days)} days ago)." + ), self.loglevel, ) files += [str(filename)] @@ -654,8 +660,10 @@ def cleanup_dirs(self, location): for path in location_path_list: util.remove_empty_directories(path, "**/*") body += logger.print_line( - f"{'Did not delete' if self.dry_run else 'Deleted'} {num_del} files " - f"({util.human_readable_size(size_bytes)}) from the {location}.", + ( + f"{'Did not delete' if self.dry_run else 'Deleted'} {num_del} files " + f"({util.human_readable_size(size_bytes)}) from the {location}." + ), self.loglevel, ) attr = { diff --git a/modules/core/recheck.py b/modules/core/recheck.py index 2ac70320..ab0a48ee 100644 --- a/modules/core/recheck.py +++ b/modules/core/recheck.py @@ -64,8 +64,10 @@ def recheck(self): ) logger.debug( logger.insert_space( - f"-- Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} < " - f"{timedelta(minutes=torrent.max_seeding_time)}", + ( + f"-- Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} < " + f"{timedelta(minutes=torrent.max_seeding_time)}" + ), 4, ) ) @@ -85,7 +87,7 @@ def recheck(self): ): self.stats_resumed += 1 body = logger.print_line( - f"{'Not Resuming' if self.config.dry_run else 'Resuming'} [{tracker['tag']}] - " f"{t_name}", + f"{'Not Resuming' if self.config.dry_run else 'Resuming'} [{tracker['tag']}] - {t_name}", self.config.loglevel, ) attr = { diff --git a/modules/core/remove_orphaned.py b/modules/core/remove_orphaned.py index 93dfb4de..bbbac2fd 100644 --- a/modules/core/remove_orphaned.py +++ b/modules/core/remove_orphaned.py @@ -67,8 +67,10 @@ def rem_orphaned(self): logger.print_line(f"{num_orphaned} Orphaned files found", self.config.loglevel) body += logger.print_line("\n".join(orphaned_files), self.config.loglevel) body += logger.print_line( - f"{'Not moving' if self.config.dry_run else 'Moving'} {num_orphaned} Orphaned files " - f"to {self.orphaned_dir.replace(self.remote_dir,self.root_dir)}", + ( + f"{'Not moving' if self.config.dry_run else 'Moving'} {num_orphaned} Orphaned files " + f"to {self.orphaned_dir.replace(self.remote_dir,self.root_dir)}" + ), self.config.loglevel, ) diff --git a/modules/core/remove_unregistered.py b/modules/core/remove_unregistered.py index d11d6f47..a58db72f 100644 --- a/modules/core/remove_unregistered.py +++ b/modules/core/remove_unregistered.py @@ -145,22 +145,28 @@ def rem_unregistered(self): if self.stats_deleted >= 1 or self.stats_deleted_contents >= 1: if self.stats_deleted >= 1: logger.print_line( - f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted} " - f".torrent{'s' if self.stats_deleted > 1 else ''} but not content files.", + ( + f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted} " + f".torrent{'s' if self.stats_deleted > 1 else ''} but not content files." + ), self.config.loglevel, ) if self.stats_deleted_contents >= 1: logger.print_line( - f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted_contents} " - f".torrent{'s' if self.stats_deleted_contents > 1 else ''} AND content files.", + ( + f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted_contents} " + f".torrent{'s' if self.stats_deleted_contents > 1 else ''} AND content files." + ), self.config.loglevel, ) else: logger.print_line("No unregistered torrents found.", self.config.loglevel) if self.stats_untagged >= 1: logger.print_line( - f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.tag_error} tags for {self.stats_untagged} " - f".torrent{'s.' if self.stats_untagged > 1 else '.'}", + ( + f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.tag_error} tags for {self.stats_untagged} " + f".torrent{'s.' if self.stats_untagged > 1 else '.'}" + ), self.config.loglevel, ) if self.stats_tagged >= 1: diff --git a/modules/core/tag_nohardlinks.py b/modules/core/tag_nohardlinks.py index 21af9155..c6d4db67 100644 --- a/modules/core/tag_nohardlinks.py +++ b/modules/core/tag_nohardlinks.py @@ -62,7 +62,7 @@ def check_previous_nohardlinks_tagged_torrents(self, has_nohardlinks, torrent, t self.stats_untagged += 1 body = [] body += logger.print_line( - f"Previous Tagged {self.nohardlinks_tag} " f"Torrent Name: {torrent.name} has hardlinks found now.", + f"Previous Tagged {self.nohardlinks_tag} Torrent Name: {torrent.name} has hardlinks found now.", self.config.loglevel, ) body += logger.print_line(logger.insert_space(f"Removed Tag: {self.nohardlinks_tag}", 6), self.config.loglevel) @@ -121,16 +121,20 @@ def tag_nohardlinks(self): self.check_previous_nohardlinks_tagged_torrents(has_nohardlinks, torrent, tracker, category) if self.stats_tagged >= 1: logger.print_line( - f"{'Did not Tag' if self.config.dry_run else 'Added Tag'} for {self.stats_tagged} " - f".torrent{'s.' if self.stats_tagged > 1 else '.'}", + ( + f"{'Did not Tag' if self.config.dry_run else 'Added Tag'} for {self.stats_tagged} " + f".torrent{'s.' if self.stats_tagged > 1 else '.'}" + ), self.config.loglevel, ) else: logger.print_line("No torrents to tag with no hardlinks.", self.config.loglevel) if self.stats_untagged >= 1: logger.print_line( - f"{'Did not delete' if self.config.dry_run else 'Deleted'} " - f"{self.nohardlinks_tag} tags for {self.stats_untagged} " - f".torrent{'s.' if self.stats_untagged > 1 else '.'}", + ( + f"{'Did not delete' if self.config.dry_run else 'Deleted'} " + f"{self.nohardlinks_tag} tags for {self.stats_untagged} " + f".torrent{'s.' if self.stats_untagged > 1 else '.'}" + ), self.config.loglevel, ) diff --git a/modules/webhooks.py b/modules/webhooks.py index dcbd14d2..5d80245d 100755 --- a/modules/webhooks.py +++ b/modules/webhooks.py @@ -171,8 +171,10 @@ def function_hooks(self, webhook, json): def notify(self, torrents_updated=[], payload={}, group_by=""): if len(torrents_updated) > GROUP_NOTIFICATION_LIMIT: logger.trace( - f"Number of torrents updated > {GROUP_NOTIFICATION_LIMIT}, grouping notifications" - f"{f' by {group_by}' if group_by else ''}", + ( + f"Number of torrents updated > {GROUP_NOTIFICATION_LIMIT}, grouping notifications" + f"{f' by {group_by}' if group_by else ''}" + ), ) if group_by == "category": group_attr = group_notifications_by_key(payload, "torrent_category") @@ -189,10 +191,14 @@ def notify(self, torrents_updated=[], payload={}, group_by=""): attr = { "function": group_attr[group]["function"], "title": f"{group_attr[group]['title']} for {group}", - "body": group_attr[group]["body"] - if only_one_torrent_updated - else f"Updated {num_torrents_updated} " - f"{'torrent' if only_one_torrent_updated else 'torrents'} with {group_by} '{group}'", + "body": ( + group_attr[group]["body"] + if only_one_torrent_updated + else ( + f"Updated {num_torrents_updated} " + f"{'torrent' if only_one_torrent_updated else 'torrents'} with {group_by} '{group}'" + ) + ), "torrents": group_attr[group]["torrents"], } if group_by == "category": diff --git a/qbit_manage.py b/qbit_manage.py index a6e0dd9f..cf03ec6b 100755 --- a/qbit_manage.py +++ b/qbit_manage.py @@ -61,8 +61,10 @@ action="store", default="config.yml", type=str, - help="This is used if you want to use a different name for your config.yml or if you want to load multiple" - "config files using *. Example: tv.yml or config*.yml", + help=( + "This is used if you want to use a different name for your config.yml or if you want to load multiple" + "config files using *. Example: tv.yml or config*.yml" + ), ) parser.add_argument( "-lf", @@ -103,8 +105,10 @@ dest="tag_update", action="store_true", default=False, - help="Use this if you would like to update your tags and/or set seed goals/limit upload speed by tag." - " (Only adds tags to untagged torrents)", + help=( + "Use this if you would like to update your tags and/or set seed goals/limit upload speed by tag." + " (Only adds tags to untagged torrents)" + ), ) parser.add_argument( "-ru", @@ -136,10 +140,12 @@ dest="tag_nohardlinks", action="store_true", default=False, - help="Use this to tag any torrents that do not have any hard links associated with any of the files. " - "This is useful for those that use Sonarr/Radarr which hard link your media files with the torrents for seeding. " - "When files get upgraded they no longer become linked with your media therefore will be tagged with a new tag noHL. " - "You can then safely delete/remove these torrents to free up any extra space that is not being used by your media folder.", + help=( + "Use this to tag any torrents that do not have any hard links associated with any of the files. " + "This is useful for those that use Sonarr/Radarr which hard link your media files with the torrents for seeding. " + "When files get upgraded they no longer become linked with your media therefore will be tagged with a new tag noHL. " + "You can then safely delete/remove these torrents to free up any extra space that is not being used by your media folder." + ), ) parser.add_argument( "-sl", @@ -147,9 +153,11 @@ dest="share_limits", action="store_true", default=False, - help="Use this to help apply and manage your torrent share limits based on your tags/categories." - "This can apply a max ratio, seed time limits to your torrents or limit your torrent upload speed as well." - "Share limits are applied in the order of priority specified.", + help=( + "Use this to help apply and manage your torrent share limits based on your tags/categories." + "This can apply a max ratio, seed time limits to your torrents or limit your torrent upload speed as well." + "Share limits are applied in the order of priority specified." + ), ) parser.add_argument( "-sc", @@ -422,7 +430,9 @@ def finished_run(): next_run = nxt_run["next_run"] body = logger.separator( f"Finished Run\n{os.linesep.join(stats_summary) if len(stats_summary)>0 else ''}" - f"\nRun Time: {run_time}\n{next_run_str if len(next_run_str)>0 else ''}".replace("\n\n", "\n").rstrip() + f"\nRun Time: {run_time}\n{next_run_str if len(next_run_str)>0 else ''}".replace( + "\n\n", "\n" + ).rstrip() )[0] return next_run, body diff --git a/scripts/delete_torrents_on_low_disk_space.py b/scripts/delete_torrents_on_low_disk_space.py index 8fe4eef1..463cae77 100755 --- a/scripts/delete_torrents_on_low_disk_space.py +++ b/scripts/delete_torrents_on_low_disk_space.py @@ -184,8 +184,8 @@ def main(): print( f"--- Torrent ages are below threshold of '{MIN_TORRENT_AGE} days'\n" f"--- Torrent seed ratios are below threshold of '{MIN_TORRENT_SHARE_RATIO}'\n" - f"--- Torrents have multiple hard links\n" - f"--- No torrents exists!" + "--- Torrents have multiple hard links\n" + "--- No torrents exists!" ) quit_program(0) diff --git a/setup.py b/setup.py index 20bc3007..127dfe51 100755 --- a/setup.py +++ b/setup.py @@ -30,8 +30,10 @@ # repository. For example: MIT license="MIT", # Short description of your library - description="This tool will help manage tedious tasks in qBittorrent and automate them. " - "Tag, categorize, remove Orphaned data, remove unregistered torrents and much much more.", + description=( + "This tool will help manage tedious tasks in qBittorrent and automate them. " + "Tag, categorize, remove Orphaned data, remove unregistered torrents and much much more." + ), # Long description of your library long_description=long_description, long_description_content_type="text/markdown", diff --git a/tox.ini b/tox.ini index 130362af..7359ccfd 100755 --- a/tox.ini +++ b/tox.ini @@ -5,16 +5,28 @@ tox_pip_extensions_ext_pip_custom_platform = true tox_pip_extensions_ext_venv_update = true [testenv] -deps = -r{toxinidir}/requirements.txt +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/requirements-dev.txt passenv = HOME SSH_AUTH_SOCK USER -commands = - pre-commit install -f --install-hooks - pre-commit run --all-files --show-diff-on-failure [testenv:venv] envdir = venv commands = +[testenv:install-hooks] +deps = pre-commit +commands = pre-commit install -f --install-hooks + +[testenv:pre-commit] +deps = pre-commit +commands = pre-commit run --all-files + +[testenv:tests] +commands = + pre-commit install -f --install-hooks + pre-commit run --all-files + [flake8] max-line-length = 130 From 73f11ec437165588d6649bc35e346c26d5dde133 Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 16:31:59 -0400 Subject: [PATCH 05/13] change default share_limits_tag to start with `~` in order to add the tag at the end to declutter webUI --- CHANGELOG | 9 +++++---- VERSION | 2 +- config/config.yml.sample | 2 +- modules/config.py | 5 ++++- modules/core/share_limits.py | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f7cf940c..13ba5883 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,14 +10,15 @@ - Logic for `share_limits_suffix_tag` changed to become a prefix tag instead along with adding the priority of the group. The reason for this change is so it's easier to see the share limit groups togethered in qbitorrent ordered by priority. - `share_limits_suffix_tag` key is now `share_limits_tag` - No config changes are required as the qbm will automatically change the previous `share_limits_suffix_tag` key to `share_limits_tag` + - Changes the default value of `share_limits_tag` to `~share_limit`. The `~` is used to sort the `share_limits_tag` in the qbt webUI to the bottom for less clutter Example based on config.sample: | old tag (v4.0.0) | new tag (v4.0.1) | | ----------- | ----------- | -| noHL.share_limits | share_limits_1.noHL | -| cross-seed.share_limits | share_limits_2.cross-seed | -| PTP.share_limits | share_limits_3.PTP | -| default.share_limits | share_limits_999.default | +| noHL.share_limit | ~share_limit_1.noHL | +| cross-seed.share_limit | ~share_limit_2.cross-seed | +| PTP.share_limit | ~share_limit_3.PTP | +| default.share_limit | ~share_limit_999.default | **Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.0...v4.0.1 diff --git a/VERSION b/VERSION index a1b24da8..e5a2f123 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop3 +4.0.1-develop4 diff --git a/config/config.yml.sample b/config/config.yml.sample index 76c6a2ab..120b42a0 100755 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -28,7 +28,7 @@ settings: force_auto_tmm: False # Will force qBittorrent to enable Automatic Torrent Management for each torrent. tracker_error_tag: issue # Will set the tag of any torrents that do not have a working tracker. nohardlinks_tag: noHL # Will set the tag of any torrents with no hardlinks. - share_limits_tag: share_limit # Will add this tag when applying share limits to provide an easy way to filter torrents by share limit group/priority for each torrent + share_limits_tag: ~share_limit # Will add this tag when applying share limits to provide an easy way to filter torrents by share limit group/priority for each torrent ignoreTags_OnUpdate: # When running tag-update function, it will update torrent tags for a given torrent even if the torrent has at least one or more of the tags defined here. Otherwise torrents will not be tagged if tags exist. - noHL - issue diff --git a/modules/config.py b/modules/config.py index 07bef42a..8f7e40dd 100755 --- a/modules/config.py +++ b/modules/config.py @@ -146,7 +146,10 @@ def hooks(attr): self.loglevel = "DRYRUN" if self.dry_run else "INFO" self.session = requests.Session() - share_limits_tag = self.data["settings"].get("share_limits_suffix_tag", "share_limits") + share_limits_tag = self.data["settings"].get("share_limits_suffix_tag", "~share_limit") + # Convert previous share_limits_suffix_tag to new default share_limits_tag + if share_limits_tag == "share_limit": + share_limits_tag = "~share_limit" self.settings = { "force_auto_tmm": self.util.check_for_attribute( diff --git a/modules/core/share_limits.py b/modules/core/share_limits.py index 9fb092a4..c67f133a 100644 --- a/modules/core/share_limits.py +++ b/modules/core/share_limits.py @@ -438,6 +438,7 @@ def _has_reached_seeding_time_limit(): def delete_share_limits_suffix_tag(self): """ "Delete Share Limits Suffix Tag from version 4.0.0""" tags = self.client.torrent_tags.tags + old_share_limits_tag = self.share_limits_tag[1:] if self.share_limits_tag.startswith("~") else self.share_limits_tag for tag in tags: - if tag.endswith(f".{self.share_limits_tag}"): + if tag.endswith(f".{old_share_limits_tag}"): self.client.torrent_tags.delete_tags(tag) From 4181c9390c52fc2908f449935a037479ed32f5e0 Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 17:19:43 -0400 Subject: [PATCH 06/13] couple minor bug fixes + documentation --- CHANGELOG | 1 + VERSION | 2 +- config/config.yml.sample | 7 ++----- modules/config.py | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 13ba5883..a305f63e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ # New Features - cross-seed will move torrent to an error folder if it fails to inject torrent +- Define multiple announce urls for one tracker (#328 Thanks to @buthed010203 for the PR) # Bug Fixes - Fixes #329 (Updates missing share_limits tag even when share_limits are satisfied) diff --git a/VERSION b/VERSION index e5a2f123..d4edeac3 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop4 +4.0.1-develop5 diff --git a/config/config.yml.sample b/config/config.yml.sample index 5d08cbe3..10a36735 100755 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -67,7 +67,7 @@ cat_change: tracker: # Mandatory # Tag Parameters - # : # This is the keyword in the tracker url + # : # This is the keyword in the tracker url. You can define multiple tracker urls by splitting with `|` delimiter # Set tag name. Can be a list of tags or a single tag # tag: # Set this to the notifiarr react name. This is used to add indexer reactions to the notifications sent by Notifiarr @@ -107,13 +107,10 @@ tracker: privatehd: tag: PrivateHD notifiarr: - tleechreload: - tag: TorrentLeech - notifiarr: torrentleech torrentdb: tag: TorrentDB notifiarr: torrentdb - torrentleech: + torrentleech|tleechreload: tag: TorrentLeech notifiarr: torrentleech tv-vault: diff --git a/modules/config.py b/modules/config.py index 0ac0753c..459f153c 100755 --- a/modules/config.py +++ b/modules/config.py @@ -105,7 +105,7 @@ def __init__(self, default_dir, args): # Splits tracker urls at pipes, useful for trackers with multiple announce urls for tracker_urls, data in trackers.items(): for tracker_url in tracker_urls.split("|"): - self.data["tracker"][tracker_url] = data + self.data["tracker"][tracker_url.strip()] = data else: self.data["tracker"] = {} if "nohardlinks" in self.data: From 158c3c5f078539098c70a427d82ef93685b1c1a8 Mon Sep 17 00:00:00 2001 From: bobokun Date: Sun, 11 Jun 2023 17:25:39 -0400 Subject: [PATCH 07/13] updates README to include share_limits --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 356ceae9..d8589975 100755 --- a/README.md +++ b/README.md @@ -10,13 +10,14 @@ This is a program used to manage your qBittorrent instance such as: -* Tag torrents based on tracker URL and set seed goals/limit upload speed by tag (only tag torrents that have no tags) +* Tag torrents based on tracker URLs * Update categories based on save directory * Remove unregistered torrents (delete data & torrent if it is not being cross-seeded, otherwise it will just remove the torrent) * Automatically add [cross-seed](https://github.com/mmgoodnow/cross-seed) torrents in paused state. **\*Note: cross-seed now allows for torrent injections directly to qBit, making this feature obsolete.\*** * Recheck paused torrents sorted by lowest size and resume if completed * Remove orphaned files from your root directory that are not referenced by qBittorrent -* Tag any torrents that have no hard links outisde the root folder and allows optional cleanup to delete these torrents and contents based on maximum ratio and/or time seeded +* Tag any torrents that have no hard links outisde the root folder +* Apply share limits based on groups filtered by tags/categories and allows optional cleanup to delete these torrents and contents based on maximum ratio and/or time seeded * RecycleBin function to move files into a RecycleBin folder instead of deleting the data directly when deleting a torrent * Built-in scheduler to run the script every x minutes. (Can use `--run` command to run without the scheduler) * Webhook notifications with [Notifiarr](https://notifiarr.com/) and [Apprise API](https://github.com/caronc/apprise-api) integration From 16a3662830d21fcbc386c1674e53330522b77d4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:23:14 +0000 Subject: [PATCH 08/13] Bump qbittorrent-api from 2023.5.48 to 2023.6.49 Bumps [qbittorrent-api](https://github.com/rmartin16/qbittorrent-api) from 2023.5.48 to 2023.6.49. - [Release notes](https://github.com/rmartin16/qbittorrent-api/releases) - [Changelog](https://github.com/rmartin16/qbittorrent-api/blob/main/CHANGELOG.md) - [Commits](https://github.com/rmartin16/qbittorrent-api/compare/v2023.5.48...v2023.6.49) --- updated-dependencies: - dependency-name: qbittorrent-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6d26c32a..b91b0018 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ bencodepy==0.9.5 GitPython==3.1.31 -qbittorrent-api==2023.5.48 +qbittorrent-api==2023.6.49 requests==2.31.0 retrying==1.3.4 ruamel.yaml==0.17.31 From 948d289fafea48f2f5075a1fadb22bb3667ec2e4 Mon Sep 17 00:00:00 2001 From: bobokun Date: Mon, 12 Jun 2023 20:05:11 -0400 Subject: [PATCH 09/13] Fixes #327 --- CHANGELOG | 3 ++- VERSION | 2 +- modules/core/share_limits.py | 38 ++++++++++++++++----------------- modules/core/tag_nohardlinks.py | 3 +-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a305f63e..a6653860 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,12 @@ # Requirements Updated - +- qbitorrent-api updated to 2023.6.49 # New Features - cross-seed will move torrent to an error folder if it fails to inject torrent - Define multiple announce urls for one tracker (#328 Thanks to @buthed010203 for the PR) # Bug Fixes - Fixes #329 (Updates missing share_limits tag even when share_limits are satisfied) +- Fixes #327 (Zero value share limits not being applied correctly) # Enhancements - Logic for `share_limits_suffix_tag` changed to become a prefix tag instead along with adding the priority of the group. The reason for this change is so it's easier to see the share limit groups togethered in qbitorrent ordered by priority. diff --git a/VERSION b/VERSION index d4edeac3..fd82f41c 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop5 +4.0.1-develop6 diff --git a/modules/core/share_limits.py b/modules/core/share_limits.py index c67f133a..291a6aa7 100644 --- a/modules/core/share_limits.py +++ b/modules/core/share_limits.py @@ -231,10 +231,11 @@ def update_share_limits_for_group(self, group_name, group_config, torrents): def tag_and_update_share_limits_for_torrent(self, torrent, group_config): """Removes previous share limits tag, updates tag and share limits for a torrent, and resumes the torrent""" # Remove previous share_limits tag - tags = util.get_list(torrent.tags) - for tag in tags: - if self.share_limits_tag in tag: - torrent.remove_tags(tag) + if not self.config.dry_run: + tags = util.get_list(torrent.tags) + for tag in tags: + if self.share_limits_tag in tag: + torrent.remove_tags(tag) # Will tag the torrent with the group name if add_group_to_tag is True and set the share limits self.set_tags_and_limits( @@ -300,39 +301,37 @@ def check_category(self, category, categories): return False return True - def set_tags_and_limits( - self, torrent, max_ratio, max_seeding_time, limit_upload_speed=None, tags=None, restore=False, do_print=True - ): + def set_tags_and_limits(self, torrent, max_ratio, max_seeding_time, limit_upload_speed=None, tags=None, do_print=True): """Set tags and limits for a torrent""" body = [] - if limit_upload_speed: + if limit_upload_speed is not None: if limit_upload_speed != -1: msg = logger.insert_space(f"Limit UL Speed: {limit_upload_speed} kB/s", 1) if do_print: body += logger.print_line(msg, self.config.loglevel) else: body.append(msg) - if max_ratio or max_seeding_time: - if (max_ratio == -2 and max_seeding_time == -2) and not restore: + if max_ratio is not None or max_seeding_time is not None: + if max_ratio == -2 and max_seeding_time == -2: msg = logger.insert_space("Share Limit: Use Global Share Limit", 4) if do_print: body += logger.print_line(msg, self.config.loglevel) else: body.append(msg) - elif (max_ratio == -1 and max_seeding_time == -1) and not restore: + elif max_ratio == -1 and max_seeding_time == -1: msg = logger.insert_space("Share Limit: Set No Share Limit", 4) if do_print: body += logger.print_line(msg, self.config.loglevel) else: body.append(msg) else: - if max_ratio != torrent.max_ratio and (not max_seeding_time or max_seeding_time < 0): + if max_ratio != torrent.max_ratio and (max_seeding_time is None or max_seeding_time < 0): msg = logger.insert_space(f"Share Limit: Max Ratio = {max_ratio}", 4) if do_print: body += logger.print_line(msg, self.config.loglevel) else: body.append(msg) - elif max_seeding_time != torrent.max_seeding_time and (not max_ratio or max_ratio < 0): + elif max_seeding_time != torrent.max_seeding_time and (max_ratio is None or max_ratio < 0): msg = logger.insert_space(f"Share Limit: Max Seed Time = {max_seeding_time} min", 4) if do_print: body += logger.print_line(msg, self.config.loglevel) @@ -348,14 +347,14 @@ def set_tags_and_limits( if not self.config.dry_run: if tags and tags not in torrent.tags: torrent.add_tags(tags) - if limit_upload_speed: + if limit_upload_speed is not None: if limit_upload_speed == -1: torrent.set_upload_limit(-1) else: torrent.set_upload_limit(limit_upload_speed * 1024) - if not max_ratio: + if max_ratio is not None: max_ratio = torrent.max_ratio - if not max_seeding_time: + if max_seeding_time is not None: max_seeding_time = torrent.max_seeding_time if "MinSeedTimeNotReached" in torrent.tags: return [] @@ -370,7 +369,8 @@ def _has_reached_min_seeding_time_limit(): print_log = [] if torrent.seeding_time >= min_seeding_time * 60: if "MinSeedTimeNotReached" in torrent.tags: - torrent.remove_tags(tags="MinSeedTimeNotReached") + if not self.config.dry_run: + torrent.remove_tags(tags="MinSeedTimeNotReached") return True else: if "MinSeedTimeNotReached" not in torrent.tags: @@ -400,7 +400,7 @@ def _has_reached_min_seeding_time_limit(): def _has_reached_seeding_time_limit(): nonlocal body seeding_time_limit = None - if not max_seeding_time: + if max_seeding_time is None: return False if max_seeding_time >= 0: seeding_time_limit = max_seeding_time @@ -420,7 +420,7 @@ def _has_reached_seeding_time_limit(): return True return False - if max_ratio: + if max_ratio is not None: if max_ratio >= 0: if torrent.ratio >= max_ratio and _has_reached_min_seeding_time_limit(): body += logger.insert_space(f"Ratio vs Max Ratio: {torrent.ratio:.2f} >= {max_ratio:.2f}", 8) diff --git a/modules/core/tag_nohardlinks.py b/modules/core/tag_nohardlinks.py index c6d4db67..8859ab63 100644 --- a/modules/core/tag_nohardlinks.py +++ b/modules/core/tag_nohardlinks.py @@ -55,8 +55,7 @@ def add_tag_no_hl(self, torrent, tracker, category): def check_previous_nohardlinks_tagged_torrents(self, has_nohardlinks, torrent, tracker, category): """ Checks for any previous torrents that were tagged with the nohardlinks tag and have since had hardlinks added. - If any are found, the nohardlinks tag is removed from the torrent and the tracker or global share limits are restored. - If the torrent is complete and the option to resume after untagging is enabled, the torrent is resumed. + If any are found, the nohardlinks tag is removed """ if not (has_nohardlinks) and (self.nohardlinks_tag in torrent.tags): self.stats_untagged += 1 From 581dfec19ac37e5a33ef2d331165e24f607fb16d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 01:06:42 +0000 Subject: [PATCH 10/13] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.4.0 → v3.6.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 92c8631f..db5462b5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: hooks: - id: reorder-python-imports - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.6.0 hooks: - id: pyupgrade args: [--py3-plus] From 5704f6e52dca5a0ad2988e35360126a990eb1f5d Mon Sep 17 00:00:00 2001 From: bobokun Date: Tue, 13 Jun 2023 09:48:23 -0400 Subject: [PATCH 11/13] fix bug applying share limits --- VERSION | 2 +- modules/core/share_limits.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index fd82f41c..b60b2e78 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop6 +4.0.1-develop7 diff --git a/modules/core/share_limits.py b/modules/core/share_limits.py index 291a6aa7..cba0dc1d 100644 --- a/modules/core/share_limits.py +++ b/modules/core/share_limits.py @@ -347,14 +347,15 @@ def set_tags_and_limits(self, torrent, max_ratio, max_seeding_time, limit_upload if not self.config.dry_run: if tags and tags not in torrent.tags: torrent.add_tags(tags) - if limit_upload_speed is not None: + torrent_upload_limit = -1 if round(torrent.up_limit / 1024) == 0 else round(torrent.up_limit / 1024) + if limit_upload_speed is not None and limit_upload_speed != torrent_upload_limit: if limit_upload_speed == -1: torrent.set_upload_limit(-1) else: torrent.set_upload_limit(limit_upload_speed * 1024) - if max_ratio is not None: + if max_ratio is None: max_ratio = torrent.max_ratio - if max_seeding_time is not None: + if max_seeding_time is None: max_seeding_time = torrent.max_seeding_time if "MinSeedTimeNotReached" in torrent.tags: return [] From ef5756b603c3783651058a04aaf665c87b37936c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:00:46 +0000 Subject: [PATCH 12/13] Bump pre-commit from 3.3.2 to 3.3.3 Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v3.3.2...v3.3.3) --- updated-dependencies: - dependency-name: pre-commit dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 589bfee1..1a1b3ca2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,2 +1,2 @@ flake8==6.0.0 -pre-commit==3.3.2 +pre-commit==3.3.3 From a42db58911245af4ce85a9b40614b6e1a2a571de Mon Sep 17 00:00:00 2001 From: bobokun Date: Fri, 16 Jun 2023 08:49:27 -0400 Subject: [PATCH 13/13] 4.0.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b60b2e78..1454f6ed 100755 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.1-develop7 +4.0.1