mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2026-01-07 00:34:34 +08:00
refactor nohardlinks
This commit is contained in:
parent
3c1410f8af
commit
5cfb0cd4b3
3 changed files with 358 additions and 291 deletions
350
modules/core/tag_nohardlinks.py
Normal file
350
modules/core/tag_nohardlinks.py
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
import os
|
||||
|
||||
from modules import util
|
||||
|
||||
logger = util.logger
|
||||
|
||||
|
||||
class TagNoHardLinks:
|
||||
def __init__(self, qbit_manager):
|
||||
self.qbt = qbit_manager
|
||||
self.config = qbit_manager.config
|
||||
self.client = qbit_manager.client
|
||||
self.stats_tagged = 0 # counter for the number of torrents that has no hardlinks
|
||||
self.stats_untagged = 0 # counter for number of torrents that previously had no hardlinks but now have hardlinks
|
||||
self.stats_deleted = 0 # counter for the number of torrents that has no hardlinks and \
|
||||
# meets the criteria for ratio limit/seed limit for deletion
|
||||
self.stats_deleted_contents = 0 # counter for the number of torrents that has no hardlinks and \
|
||||
# meets the criteria for ratio limit/seed limit for deletion including contents
|
||||
|
||||
self.tdel_dict = {} # dictionary to track the torrent names and content path that meet the deletion criteria
|
||||
self.root_dir = qbit_manager.config.root_dir
|
||||
self.remote_dir = qbit_manager.config.remote_dir
|
||||
self.nohardlinks = qbit_manager.config.nohardlinks
|
||||
self.nohardlinks_tag = qbit_manager.config.nohardlinks_tag
|
||||
|
||||
self.tag_nohardlinks()
|
||||
|
||||
def add_tag_no_hl(self, torrent, tracker, category, max_ratio, max_seeding_time, add_tag=True):
|
||||
"""Add tag nohardlinks_tag to torrents with no hardlinks"""
|
||||
body = []
|
||||
body.append(logger.insert_space(f"Torrent Name: {torrent.name}", 3))
|
||||
if add_tag:
|
||||
body.append(logger.insert_space(f"Added Tag: {self.nohardlinks_tag}", 6))
|
||||
title = "Tagging Torrents with No Hardlinks"
|
||||
else:
|
||||
title = "Changing Share Ratio of Torrents with No Hardlinks"
|
||||
body.append(logger.insert_space(f'Tracker: {tracker["url"]}', 8))
|
||||
body_tags_and_limits = self.qbt.set_tags_and_limits(
|
||||
torrent,
|
||||
max_ratio,
|
||||
max_seeding_time,
|
||||
self.nohardlinks[category]["limit_upload_speed"],
|
||||
tags=self.nohardlinks_tag,
|
||||
do_print=False,
|
||||
)
|
||||
if body_tags_and_limits or add_tag:
|
||||
self.stats_tagged += 1
|
||||
# Resume torrent if it was paused now that the share limit has changed
|
||||
if torrent.state_enum.is_complete and self.nohardlinks[category]["resume_torrent_after_untagging_noHL"]:
|
||||
if not self.config.dry_run:
|
||||
torrent.resume()
|
||||
body.extend(body_tags_and_limits)
|
||||
for rcd in body:
|
||||
logger.print_line(rcd, self.config.loglevel)
|
||||
attr = {
|
||||
"function": "tag_nohardlinks",
|
||||
"title": title,
|
||||
"body": "\n".join(body),
|
||||
"torrent_name": torrent.name,
|
||||
"torrent_category": torrent.category,
|
||||
"torrent_tag": self.nohardlinks_tag,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
"torrent_max_ratio": max_ratio,
|
||||
"torrent_max_seeding_time": max_seeding_time,
|
||||
"torrent_limit_upload_speed": self.nohardlinks[category]["limit_upload_speed"],
|
||||
}
|
||||
self.config.send_notifications(attr)
|
||||
|
||||
def cleanup_tagged_torrents_with_no_hardlinks(self, category):
|
||||
"""Delete any tagged torrents that meet noHL criteria"""
|
||||
# loop through torrent list again for cleanup purposes
|
||||
if self.nohardlinks[category]["cleanup"]:
|
||||
torrent_list = self.qbt.get_torrents({"category": category, "status_filter": "completed"})
|
||||
for torrent in torrent_list:
|
||||
t_name = torrent.name
|
||||
t_hash = torrent.hash
|
||||
if t_hash in self.tdel_dict and self.nohardlinks_tag in torrent.tags:
|
||||
t_count = self.qbt.torrentinfo[t_name]["count"]
|
||||
t_msg = self.qbt.torrentinfo[t_name]["msg"]
|
||||
t_status = self.qbt.torrentinfo[t_name]["status"]
|
||||
# Double check that the content path is the same before we delete anything
|
||||
if torrent["content_path"].replace(self.root_dir, self.remote_dir) == self.tdel_dict[t_hash]["content_path"]:
|
||||
tracker = self.qbt.get_tags(torrent.trackers)
|
||||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(self.tdel_dict[t_hash]["body"], self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Cleanup: True [No hardlinks found and meets Share Limits.]", 8),
|
||||
self.config.loglevel,
|
||||
)
|
||||
attr = {
|
||||
"function": "cleanup_tag_nohardlinks",
|
||||
"title": "Removing NoHL Torrents and meets Share Limits",
|
||||
"torrent_name": t_name,
|
||||
"torrent_category": torrent.category,
|
||||
"cleanup": "True",
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
}
|
||||
if os.path.exists(torrent["content_path"].replace(self.root_dir, self.remote_dir)):
|
||||
# Checks if any of the original torrents are working
|
||||
if t_count > 1 and ("" in t_msg or 2 in t_status):
|
||||
self.stats_deleted += 1
|
||||
attr["torrents_deleted_and_contents"] = False
|
||||
if not self.config.dry_run:
|
||||
self.qbt.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent but NOT content files.", 8),
|
||||
self.config.loglevel,
|
||||
)
|
||||
else:
|
||||
self.stats_deleted_contents += 1
|
||||
attr["torrents_deleted_and_contents"] = True
|
||||
if not self.config.dry_run:
|
||||
self.qbt.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent AND content files.", 8), self.config.loglevel
|
||||
)
|
||||
else:
|
||||
self.stats_deleted += 1
|
||||
attr["torrents_deleted_and_contents"] = False
|
||||
if not self.config.dry_run:
|
||||
self.qbt.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent but NOT content files.", 8), self.config.loglevel
|
||||
)
|
||||
attr["body"] = "\n".join(body)
|
||||
self.config.send_notifications(attr)
|
||||
self.qbt.torrentinfo[t_name]["count"] -= 1
|
||||
|
||||
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 not (has_nohardlinks) and (self.nohardlinks_tag in torrent.tags):
|
||||
self.stats_untagged += 1
|
||||
body = []
|
||||
body += logger.print_line(
|
||||
f"Previous Tagged {self.nohardlinks_tag} " f"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)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
f"{'Not Reverting' if self.config.dry_run else 'Reverting'} to tracker or Global share limits.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
restore_max_ratio = tracker["max_ratio"]
|
||||
restore_max_seeding_time = tracker["max_seeding_time"]
|
||||
restore_limit_upload_speed = tracker["limit_upload_speed"]
|
||||
if restore_max_ratio is None:
|
||||
restore_max_ratio = -2
|
||||
if restore_max_seeding_time is None:
|
||||
restore_max_seeding_time = -2
|
||||
if restore_limit_upload_speed is None:
|
||||
restore_limit_upload_speed = -1
|
||||
if not self.config.dry_run:
|
||||
torrent.remove_tags(tags=self.nohardlinks_tag)
|
||||
body.extend(
|
||||
self.qbt.set_tags_and_limits(
|
||||
torrent, restore_max_ratio, restore_max_seeding_time, restore_limit_upload_speed, restore=True
|
||||
)
|
||||
)
|
||||
if torrent.state_enum.is_complete and self.nohardlinks[category]["resume_torrent_after_untagging_noHL"]:
|
||||
torrent.resume()
|
||||
attr = {
|
||||
"function": "untag_nohardlinks",
|
||||
"title": "Untagging Previous Torrents that now have hardlinks",
|
||||
"body": "\n".join(body),
|
||||
"torrent_name": torrent.name,
|
||||
"torrent_category": torrent.category,
|
||||
"torrent_tag": self.nohardlinks_tag,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
"torrent_max_ratio": restore_max_ratio,
|
||||
"torrent_max_seeding_time": restore_max_seeding_time,
|
||||
"torrent_limit_upload_speed": restore_limit_upload_speed,
|
||||
}
|
||||
self.config.send_notifications(attr)
|
||||
|
||||
def tag_nohardlinks(self):
|
||||
"""Tag torrents with no hardlinks"""
|
||||
logger.separator("Tagging Torrents with No Hardlinks", space=False, border=False)
|
||||
nohardlinks = self.nohardlinks
|
||||
for category in nohardlinks:
|
||||
torrent_list = self.qbt.get_torrents({"category": category, "status_filter": "completed"})
|
||||
if len(torrent_list) == 0:
|
||||
ex = (
|
||||
"No torrents found in the category ("
|
||||
+ category
|
||||
+ ") defined under nohardlinks attribute in the config. "
|
||||
+ "Please check if this matches with any category in qbittorrent and has 1 or more torrents."
|
||||
)
|
||||
logger.warning(ex)
|
||||
continue
|
||||
for torrent in torrent_list:
|
||||
tracker = self.qbt.get_tags(torrent.trackers)
|
||||
has_nohardlinks = util.nohardlink(
|
||||
torrent["content_path"].replace(self.root_dir, self.remote_dir), self.config.notify
|
||||
)
|
||||
if any(tag in torrent.tags for tag in nohardlinks[category]["exclude_tags"]):
|
||||
# Skip to the next torrent if we find any torrents that are in the exclude tag
|
||||
continue
|
||||
else:
|
||||
# Checks for any hardlinks and not already tagged
|
||||
# Cleans up previously tagged nohardlinks_tag torrents that no longer have hardlinks
|
||||
if has_nohardlinks:
|
||||
tracker = self.qbt.get_tags(torrent.trackers)
|
||||
# Determine min_seeding_time.
|
||||
# If only tracker setting is set, use tracker's min_seeding_time
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's min_seeding_time
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use 0 (no limit)
|
||||
min_seeding_time = 0
|
||||
logger.trace(f'tracker["min_seeding_time"] is {tracker["min_seeding_time"]}')
|
||||
logger.trace(f'nohardlinks[category]["min_seeding_time"] is {nohardlinks[category]["min_seeding_time"]}')
|
||||
if tracker["min_seeding_time"] is not None and nohardlinks[category]["min_seeding_time"] is not None:
|
||||
if tracker["min_seeding_time"] >= nohardlinks[category]["min_seeding_time"]:
|
||||
min_seeding_time = tracker["min_seeding_time"]
|
||||
logger.trace(f'Using tracker["min_seeding_time"] {min_seeding_time}')
|
||||
else:
|
||||
min_seeding_time = nohardlinks[category]["min_seeding_time"]
|
||||
logger.trace(f'Using nohardlinks[category]["min_seeding_time"] {min_seeding_time}')
|
||||
elif nohardlinks[category]["min_seeding_time"]:
|
||||
min_seeding_time = nohardlinks[category]["min_seeding_time"]
|
||||
logger.trace(f'Using nohardlinks[category]["min_seeding_time"] {min_seeding_time}')
|
||||
elif tracker["min_seeding_time"]:
|
||||
min_seeding_time = tracker["min_seeding_time"]
|
||||
logger.trace(f'Using tracker["min_seeding_time"] {min_seeding_time}')
|
||||
else:
|
||||
logger.trace(f"Using default min_seeding_time {min_seeding_time}")
|
||||
# Determine max_ratio.
|
||||
# If only tracker setting is set, use tracker's max_ratio
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's max_ratio
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use -1 (no limit)
|
||||
max_ratio = -1
|
||||
logger.trace(f'tracker["max_ratio"] is {tracker["max_ratio"]}')
|
||||
logger.trace(f'nohardlinks[category]["max_ratio"] is {nohardlinks[category]["max_ratio"]}')
|
||||
if tracker["max_ratio"] is not None and nohardlinks[category]["max_ratio"] is not None:
|
||||
if tracker["max_ratio"] >= nohardlinks[category]["max_ratio"]:
|
||||
max_ratio = tracker["max_ratio"]
|
||||
logger.trace(f'Using (tracker["max_ratio"]) {max_ratio}')
|
||||
else:
|
||||
max_ratio = nohardlinks[category]["max_ratio"]
|
||||
logger.trace(f'Using (nohardlinks[category]["max_ratio"]) {max_ratio}')
|
||||
elif nohardlinks[category]["max_ratio"]:
|
||||
max_ratio = nohardlinks[category]["max_ratio"]
|
||||
logger.trace(f'Using (nohardlinks[category]["max_ratio"]) {max_ratio}')
|
||||
elif tracker["max_ratio"]:
|
||||
max_ratio = tracker["max_ratio"]
|
||||
logger.trace(f'Using (tracker["max_ratio"]) {max_ratio}')
|
||||
else:
|
||||
logger.trace(f"Using default (max_ratio) {max_ratio}")
|
||||
# Determine max_seeding_time.
|
||||
# If only tracker setting is set, use tracker's max_seeding_time
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's max_seeding_time
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use -1 (no limit)
|
||||
max_seeding_time = -1
|
||||
logger.trace(f'tracker["max_seeding_time"] is {tracker["max_seeding_time"]}')
|
||||
logger.trace(f'nohardlinks[category]["max_seeding_time"] is {nohardlinks[category]["max_seeding_time"]}')
|
||||
if tracker["max_seeding_time"] is not None and nohardlinks[category]["max_seeding_time"] is not None:
|
||||
if tracker["max_seeding_time"] >= nohardlinks[category]["max_seeding_time"]:
|
||||
max_seeding_time = tracker["max_seeding_time"]
|
||||
logger.trace(f'Using (tracker["max_seeding_time"]) {max_seeding_time}')
|
||||
else:
|
||||
max_seeding_time = nohardlinks[category]["max_seeding_time"]
|
||||
logger.trace(f'Using (nohardlinks[category]["max_seeding_time"]) {max_seeding_time}')
|
||||
elif nohardlinks[category]["max_seeding_time"]:
|
||||
max_seeding_time = nohardlinks[category]["max_seeding_time"]
|
||||
logger.trace(f'Using (nohardlinks[category]["max_seeding_time"]) {max_seeding_time}')
|
||||
elif tracker["max_seeding_time"]:
|
||||
max_seeding_time = tracker["max_seeding_time"]
|
||||
logger.trace(f'Using (tracker["max_seeding_time"]) {max_seeding_time}')
|
||||
else:
|
||||
logger.trace(f"Using default (max_seeding_time) {max_seeding_time}")
|
||||
# Will only tag new torrents that don't have nohardlinks_tag tag
|
||||
if self.nohardlinks_tag not in torrent.tags:
|
||||
self.add_tag_no_hl(
|
||||
torrent=torrent,
|
||||
tracker=tracker,
|
||||
category=category,
|
||||
max_ratio=max_ratio,
|
||||
max_seeding_time=max_seeding_time,
|
||||
add_tag=True,
|
||||
)
|
||||
|
||||
# Deletes torrent with data if cleanup is set to true and meets the ratio/seeding requirements
|
||||
if nohardlinks[category]["cleanup"] and len(nohardlinks[category]) > 0:
|
||||
tor_reach_seed_limit = self.qbt.has_reached_seed_limit(
|
||||
torrent,
|
||||
max_ratio,
|
||||
max_seeding_time,
|
||||
min_seeding_time,
|
||||
nohardlinks[category]["resume_torrent_after_untagging_noHL"],
|
||||
tracker["url"],
|
||||
)
|
||||
if tor_reach_seed_limit:
|
||||
if torrent.hash not in self.tdel_dict:
|
||||
self.tdel_dict[torrent.hash] = {}
|
||||
self.tdel_dict[torrent.hash]["content_path"] = torrent["content_path"].replace(
|
||||
self.root_dir, self.remote_dir
|
||||
)
|
||||
self.tdel_dict[torrent.hash]["body"] = tor_reach_seed_limit
|
||||
else:
|
||||
# Updates torrent to see if "MinSeedTimeNotReached" tag has been added
|
||||
torrent = self.qbt.get_torrents({"torrent_hashes": [torrent.hash]}).data[0]
|
||||
# Checks to see if previously nohardlinks_tag share limits have changed.
|
||||
self.add_tag_no_hl(
|
||||
torrent=torrent,
|
||||
tracker=tracker,
|
||||
category=category,
|
||||
max_ratio=max_ratio,
|
||||
max_seeding_time=max_seeding_time,
|
||||
add_tag=False,
|
||||
)
|
||||
self.check_previous_nohardlinks_tagged_torrents(has_nohardlinks, torrent, tracker, category)
|
||||
self.cleanup_tagged_torrents_with_no_hardlinks(category)
|
||||
if self.stats_tagged >= 1:
|
||||
logger.print_line(
|
||||
f"{'Did not Tag/set' if self.config.dry_run else 'Tag/set'} share limits 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 / share limits for {self.stats_untagged} "
|
||||
f".torrent{'s.' if self.stats_untagged > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
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.",
|
||||
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.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
|
|
@ -564,291 +564,6 @@ class Qbt:
|
|||
logger.warning(e)
|
||||
return category
|
||||
|
||||
def tag_nohardlinks(self):
|
||||
"""Tag torrents with no hardlinks"""
|
||||
num_tags = 0 # counter for the number of torrents that has no hardlinks
|
||||
del_tor = 0 # counter for the number of torrents that has no hardlinks and \
|
||||
# meets the criteria for ratio limit/seed limit for deletion
|
||||
del_tor_cont = 0 # counter for the number of torrents that has no hardlinks 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 hardlinks but now have hardlinks
|
||||
|
||||
def add_tag_no_hl(add_tag=True):
|
||||
"""Add tag nohardlinks_tag to torrents with no hardlinks"""
|
||||
nonlocal num_tags, torrent, tracker, nohardlinks, category, max_ratio, max_seeding_time
|
||||
body = []
|
||||
body.append(logger.insert_space(f"Torrent Name: {torrent.name}", 3))
|
||||
if add_tag:
|
||||
body.append(logger.insert_space(f"Added Tag: {self.config.nohardlinks_tag}", 6))
|
||||
title = "Tagging Torrents with No Hardlinks"
|
||||
else:
|
||||
title = "Changing Share Ratio of Torrents with No Hardlinks"
|
||||
body.append(logger.insert_space(f'Tracker: {tracker["url"]}', 8))
|
||||
body_tags_and_limits = self.set_tags_and_limits(
|
||||
torrent,
|
||||
max_ratio,
|
||||
max_seeding_time,
|
||||
nohardlinks[category]["limit_upload_speed"],
|
||||
tags=self.config.nohardlinks_tag,
|
||||
do_print=False,
|
||||
)
|
||||
if body_tags_and_limits or add_tag:
|
||||
num_tags += 1
|
||||
# Resume torrent if it was paused now that the share limit has changed
|
||||
if torrent.state_enum.is_complete and nohardlinks[category]["resume_torrent_after_untagging_noHL"]:
|
||||
if not self.config.dry_run:
|
||||
torrent.resume()
|
||||
body.extend(body_tags_and_limits)
|
||||
for rcd in body:
|
||||
logger.print_line(rcd, self.config.loglevel)
|
||||
attr = {
|
||||
"function": "tag_nohardlinks",
|
||||
"title": title,
|
||||
"body": "\n".join(body),
|
||||
"torrent_name": torrent.name,
|
||||
"torrent_category": torrent.category,
|
||||
"torrent_tag": self.config.nohardlinks_tag,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
"torrent_max_ratio": max_ratio,
|
||||
"torrent_max_seeding_time": max_seeding_time,
|
||||
"torrent_limit_upload_speed": nohardlinks[category]["limit_upload_speed"],
|
||||
}
|
||||
self.config.send_notifications(attr)
|
||||
|
||||
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
|
||||
root_dir = self.config.root_dir
|
||||
remote_dir = self.config.remote_dir
|
||||
for category in nohardlinks:
|
||||
torrent_list = self.get_torrents({"category": category, "status_filter": "completed"})
|
||||
if len(torrent_list) == 0:
|
||||
ex = (
|
||||
"No torrents found in the category ("
|
||||
+ category
|
||||
+ ") defined under nohardlinks attribute in the config. "
|
||||
+ "Please check if this matches with any category in qbittorrent and has 1 or more torrents."
|
||||
)
|
||||
logger.warning(ex)
|
||||
continue
|
||||
for torrent in torrent_list:
|
||||
tracker = self.get_tags(torrent.trackers)
|
||||
has_nohardlinks = util.nohardlink(torrent["content_path"].replace(root_dir, remote_dir), self.config.notify)
|
||||
if any(tag in torrent.tags for tag in nohardlinks[category]["exclude_tags"]):
|
||||
# Skip to the next torrent if we find any torrents that are in the exclude tag
|
||||
continue
|
||||
else:
|
||||
# Checks for any hardlinks and not already tagged
|
||||
# Cleans up previously tagged nohardlinks_tag torrents that no longer have hardlinks
|
||||
if has_nohardlinks:
|
||||
tracker = self.get_tags(torrent.trackers)
|
||||
# Determine min_seeding_time.
|
||||
# If only tracker setting is set, use tracker's min_seeding_time
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's min_seeding_time
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use 0 (no limit)
|
||||
min_seeding_time = 0
|
||||
if tracker["min_seeding_time"] is not None and nohardlinks[category]["min_seeding_time"] is not None:
|
||||
if tracker["min_seeding_time"] >= nohardlinks[category]["min_seeding_time"]:
|
||||
min_seeding_time = tracker["min_seeding_time"]
|
||||
elif nohardlinks[category]["min_seeding_time"]:
|
||||
min_seeding_time = nohardlinks[category]["min_seeding_time"]
|
||||
elif tracker["min_seeding_time"]:
|
||||
min_seeding_time = tracker["min_seeding_time"]
|
||||
# Determine max_ratio.
|
||||
# If only tracker setting is set, use tracker's max_ratio
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's max_ratio
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use -1 (no limit)
|
||||
max_ratio = -1
|
||||
if tracker["max_ratio"] is not None and nohardlinks[category]["max_ratio"] is not None:
|
||||
if tracker["max_ratio"] >= nohardlinks[category]["max_ratio"]:
|
||||
max_ratio = tracker["max_ratio"]
|
||||
elif nohardlinks[category]["max_ratio"]:
|
||||
max_ratio = nohardlinks[category]["max_ratio"]
|
||||
elif tracker["max_ratio"]:
|
||||
max_ratio = tracker["max_ratio"]
|
||||
# Determine max_seeding_time.
|
||||
# If only tracker setting is set, use tracker's max_seeding_time
|
||||
# If only nohardlinks category setting is set, use nohardlinks category's max_seeding_time
|
||||
# If both tracker and nohardlinks category setting is set, use the larger of the two
|
||||
# If neither set, use -1 (no limit)
|
||||
max_seeding_time = -1
|
||||
if tracker["max_seeding_time"] is not None and nohardlinks[category]["max_seeding_time"] is not None:
|
||||
if tracker["max_seeding_time"] >= nohardlinks[category]["max_seeding_time"]:
|
||||
max_seeding_time = tracker["max_seeding_time"]
|
||||
elif nohardlinks[category]["max_seeding_time"]:
|
||||
max_seeding_time = nohardlinks[category]["max_seeding_time"]
|
||||
elif tracker["max_seeding_time"]:
|
||||
max_seeding_time = tracker["max_seeding_time"]
|
||||
# Will only tag new torrents that don't have nohardlinks_tag tag
|
||||
if self.config.nohardlinks_tag not in torrent.tags:
|
||||
add_tag_no_hl(add_tag=True)
|
||||
|
||||
# Deletes torrent with data if cleanup is set to true and meets the ratio/seeding requirements
|
||||
if nohardlinks[category]["cleanup"] and len(nohardlinks[category]) > 0:
|
||||
tor_reach_seed_limit = self.has_reached_seed_limit(
|
||||
torrent,
|
||||
max_ratio,
|
||||
max_seeding_time,
|
||||
min_seeding_time,
|
||||
nohardlinks[category]["resume_torrent_after_untagging_noHL"],
|
||||
tracker["url"],
|
||||
)
|
||||
if tor_reach_seed_limit:
|
||||
if torrent.hash not in tdel_dict:
|
||||
tdel_dict[torrent.hash] = {}
|
||||
tdel_dict[torrent.hash]["content_path"] = torrent["content_path"].replace(
|
||||
root_dir, remote_dir
|
||||
)
|
||||
tdel_dict[torrent.hash]["body"] = tor_reach_seed_limit
|
||||
else:
|
||||
# Updates torrent to see if "MinSeedTimeNotReached" tag has been added
|
||||
torrent = self.get_torrents({"torrent_hashes": [torrent.hash]}).data[0]
|
||||
# Checks to see if previously nohardlinks_tag share limits have changed.
|
||||
add_tag_no_hl(add_tag=False)
|
||||
# Checks to see if previous nohardlinks_tag tagged torrents now have hardlinks.
|
||||
if not (has_nohardlinks) and (self.config.nohardlinks_tag in torrent.tags):
|
||||
num_untag += 1
|
||||
body = []
|
||||
body += logger.print_line(
|
||||
f"Previous Tagged {self.config.nohardlinks_tag} "
|
||||
f"Torrent Name: {torrent.name} has hardlinks found now.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
body += logger.print_line(
|
||||
logger.insert_space(f"Removed Tag: {self.config.nohardlinks_tag}", 6), self.config.loglevel
|
||||
)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
f"{'Not Reverting' if self.config.dry_run else 'Reverting'} to tracker or Global share limits.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
restore_max_ratio = tracker["max_ratio"]
|
||||
restore_max_seeding_time = tracker["max_seeding_time"]
|
||||
restore_limit_upload_speed = tracker["limit_upload_speed"]
|
||||
if restore_max_ratio is None:
|
||||
restore_max_ratio = -2
|
||||
if restore_max_seeding_time is None:
|
||||
restore_max_seeding_time = -2
|
||||
if restore_limit_upload_speed is None:
|
||||
restore_limit_upload_speed = -1
|
||||
if not self.config.dry_run:
|
||||
torrent.remove_tags(tags=self.config.nohardlinks_tag)
|
||||
body.extend(
|
||||
self.set_tags_and_limits(
|
||||
torrent, restore_max_ratio, restore_max_seeding_time, restore_limit_upload_speed, restore=True
|
||||
)
|
||||
)
|
||||
if torrent.state_enum.is_complete and nohardlinks[category]["resume_torrent_after_untagging_noHL"]:
|
||||
torrent.resume()
|
||||
attr = {
|
||||
"function": "untag_nohardlinks",
|
||||
"title": "Untagging Previous Torrents that now have hardlinks",
|
||||
"body": "\n".join(body),
|
||||
"torrent_name": torrent.name,
|
||||
"torrent_category": torrent.category,
|
||||
"torrent_tag": self.config.nohardlinks_tag,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
"torrent_max_ratio": restore_max_ratio,
|
||||
"torrent_max_seeding_time": restore_max_seeding_time,
|
||||
"torrent_limit_upload_speed": restore_limit_upload_speed,
|
||||
}
|
||||
self.config.send_notifications(attr)
|
||||
# loop through torrent list again for cleanup purposes
|
||||
if nohardlinks[category]["cleanup"]:
|
||||
torrent_list = self.get_torrents({"category": category, "status_filter": "completed"})
|
||||
for torrent in torrent_list:
|
||||
t_name = torrent.name
|
||||
t_hash = torrent.hash
|
||||
if t_hash in tdel_dict and self.config.nohardlinks_tag in torrent.tags:
|
||||
t_count = self.torrentinfo[t_name]["count"]
|
||||
t_msg = self.torrentinfo[t_name]["msg"]
|
||||
t_status = self.torrentinfo[t_name]["status"]
|
||||
# Double check that the content path is the same before we delete anything
|
||||
if torrent["content_path"].replace(root_dir, remote_dir) == tdel_dict[t_hash]["content_path"]:
|
||||
tracker = self.get_tags(torrent.trackers)
|
||||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel
|
||||
)
|
||||
body += logger.print_line(tdel_dict[t_hash]["body"], self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Cleanup: True [No hardlinks found and meets Share Limits.]", 8),
|
||||
self.config.loglevel,
|
||||
)
|
||||
attr = {
|
||||
"function": "cleanup_tag_nohardlinks",
|
||||
"title": "Removing NoHL Torrents and meets Share Limits",
|
||||
"torrent_name": t_name,
|
||||
"torrent_category": torrent.category,
|
||||
"cleanup": "True",
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
}
|
||||
if os.path.exists(torrent["content_path"].replace(root_dir, remote_dir)):
|
||||
# Checks if any of the original torrents are working
|
||||
if t_count > 1 and ("" in t_msg or 2 in t_status):
|
||||
del_tor += 1
|
||||
attr["torrents_deleted_and_contents"] = False
|
||||
if not self.config.dry_run:
|
||||
self.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent but NOT content files.", 8),
|
||||
self.config.loglevel,
|
||||
)
|
||||
else:
|
||||
del_tor_cont += 1
|
||||
attr["torrents_deleted_and_contents"] = True
|
||||
if not self.config.dry_run:
|
||||
self.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent AND content files.", 8), self.config.loglevel
|
||||
)
|
||||
else:
|
||||
del_tor += 1
|
||||
attr["torrents_deleted_and_contents"] = False
|
||||
if not self.config.dry_run:
|
||||
self.tor_delete_recycle(torrent, attr)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Deleted .torrent but NOT content files.", 8), self.config.loglevel
|
||||
)
|
||||
attr["body"] = "\n".join(body)
|
||||
self.config.send_notifications(attr)
|
||||
self.torrentinfo[t_name]["count"] -= 1
|
||||
if num_tags >= 1:
|
||||
logger.print_line(
|
||||
f"{'Did not Tag/set' if self.config.dry_run else 'Tag/set'} share limits for {num_tags} "
|
||||
f".torrent{'s.' if num_tags > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
else:
|
||||
logger.print_line("No torrents to tag with no hardlinks.", self.config.loglevel)
|
||||
if num_untag >= 1:
|
||||
logger.print_line(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} "
|
||||
f"{self.config.nohardlinks_tag} tags / share limits for {num_untag} "
|
||||
f".torrent{'s.' if num_untag > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
if del_tor >= 1:
|
||||
logger.print_line(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} {del_tor} "
|
||||
f".torrent{'s' if del_tor > 1 else ''} but not content files.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
if del_tor_cont >= 1:
|
||||
logger.print_line(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} {del_tor_cont} "
|
||||
f".torrent{'s' if del_tor_cont > 1 else ''} AND content files.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
return num_tags, num_untag, del_tor, del_tor_cont
|
||||
|
||||
def rem_orphaned(self):
|
||||
"""Remove orphaned files from remote directory"""
|
||||
orphaned = 0
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ from modules.core.tags import Tags # noqa
|
|||
from modules.core.remove_unregistered import RemoveUnregistered # noqa
|
||||
from modules.core.cross_seed import CrossSeed # noqa
|
||||
from modules.core.recheck import ReCheck # noqa
|
||||
from modules.core.tag_nohardlinks import TagNoHardLinks # noqa
|
||||
|
||||
|
||||
def my_except_hook(exctype, value, tbi):
|
||||
|
|
@ -418,12 +419,13 @@ def start():
|
|||
stats["rechecked"] += recheck.stats_rechecked
|
||||
|
||||
# Tag NoHardLinks
|
||||
num_tagged, num_untagged, num_deleted, num_deleted_contents = cfg.qbt.tag_nohardlinks()
|
||||
stats["tagged"] += num_tagged
|
||||
stats["tagged_noHL"] += num_tagged
|
||||
stats["untagged_noHL"] += num_untagged
|
||||
stats["deleted"] += num_deleted
|
||||
stats["deleted_contents"] += num_deleted_contents
|
||||
if cfg.commands["tag_nohardlinks"]:
|
||||
no_hardlinks = TagNoHardLinks(qbit_manager)
|
||||
stats["tagged"] += no_hardlinks.stats_tagged
|
||||
stats["tagged_noHL"] += no_hardlinks.stats_tagged
|
||||
stats["untagged_noHL"] += no_hardlinks.stats_untagged
|
||||
stats["deleted"] += no_hardlinks.stats_deleted
|
||||
stats["deleted_contents"] += no_hardlinks.stats_deleted_contents
|
||||
|
||||
# Remove Orphaned Files
|
||||
num_orphaned = cfg.qbt.rem_orphaned()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue