refactor category and tags

This commit is contained in:
bobokun 2023-04-10 11:52:51 -04:00
parent c180eba390
commit bece9526e3
No known key found for this signature in database
GPG key ID: B73932169607D927
7 changed files with 532 additions and 494 deletions

20
modules/__init__.py Normal file
View file

@ -0,0 +1,20 @@
import os
# Define an empty version_info tuple
__version_info__ = ()
# Get the path to the project directory
project_dir = os.path.dirname(os.path.abspath(__file__))
# Get the path to the VERSION file
version_file_path = os.path.join(project_dir, "..", "VERSION")
# Read the version from the file
with open(version_file_path) as f:
version_str = f.read().strip()
# Convert the version string to a tuple of integers
__version_info__ = tuple(map(int, version_str.split(".")))
# Define the version string using the version_info tuple
__version__ = ".".join(str(i) for i in __version_info__)

View file

@ -474,240 +474,6 @@ class Config:
self.notify(e, "Config")
raise Failed(e)
# Get tags from config file based on keyword
def get_tags(self, trackers):
urls = [x.url for x in trackers if x.url.startswith("http")]
tracker = {}
tracker["tag"] = None
tracker["max_ratio"] = None
tracker["min_seeding_time"] = None
tracker["max_seeding_time"] = None
tracker["limit_upload_speed"] = None
tracker["notifiarr"] = None
tracker["url"] = None
tracker_other_tag = self.util.check_for_attribute(
self.data, "tag", parent="tracker", subparent="other", default_is_none=True, var_type="list", save=False
)
try:
tracker["url"] = util.trunc_val(urls[0], os.sep)
except IndexError as e:
tracker["url"] = None
if not urls:
urls = []
if not tracker_other_tag:
tracker_other_tag = ["other"]
tracker["url"] = "No http URL found"
else:
logger.debug(f"Tracker Url:{urls}")
logger.debug(e)
if "tracker" in self.data and self.data["tracker"] is not None:
tag_values = self.data["tracker"]
for tag_url, tag_details in tag_values.items():
for url in urls:
if tag_url in url:
if tracker["url"] is None:
default_tag = tracker_other_tag
else:
try:
tracker["url"] = util.trunc_val(url, os.sep)
default_tag = tracker["url"].split(os.sep)[2].split(":")[0]
except IndexError as e:
logger.debug(f"Tracker Url:{url}")
logger.debug(e)
# Tracker Format 1 deprecated.
if isinstance(tag_details, str):
e = (
"Config Error: Tracker format invalid. Please see config.yml.sample for correct format and fix "
f"`{tag_details}` in the Tracker section of the config."
)
self.notify(e, "Config")
raise Failed(e)
# Using new Format
else:
tracker["tag"] = self.util.check_for_attribute(
self.data, "tag", parent="tracker", subparent=tag_url, default=tag_url, var_type="list"
)
if tracker["tag"] == [tag_url]:
self.data["tracker"][tag_url]["tag"] = [tag_url]
if isinstance(tracker["tag"], str):
tracker["tag"] = [tracker["tag"]]
is_max_ratio_defined = self.data["tracker"].get("max_ratio")
is_max_seeding_time_defined = self.data["tracker"].get("max_seeding_time")
if is_max_ratio_defined or is_max_seeding_time_defined:
tracker["max_ratio"] = self.util.check_for_attribute(
self.data,
"max_ratio",
parent="tracker",
subparent=tag_url,
var_type="float",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["max_seeding_time"] = self.util.check_for_attribute(
self.data,
"max_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
else:
tracker["max_ratio"] = self.util.check_for_attribute(
self.data,
"max_ratio",
parent="tracker",
subparent=tag_url,
var_type="float",
min_int=-2,
do_print=False,
default_is_none=True,
save=False,
)
tracker["max_seeding_time"] = self.util.check_for_attribute(
self.data,
"max_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-2,
do_print=False,
default_is_none=True,
save=False,
)
tracker["min_seeding_time"] = self.util.check_for_attribute(
self.data,
"min_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=0,
do_print=False,
default=0,
save=False,
)
tracker["limit_upload_speed"] = self.util.check_for_attribute(
self.data,
"limit_upload_speed",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-1,
do_print=False,
default=0,
save=False,
)
tracker["notifiarr"] = self.util.check_for_attribute(
self.data,
"notifiarr",
parent="tracker",
subparent=tag_url,
default_is_none=True,
do_print=False,
save=False,
)
return tracker
if tracker_other_tag:
tracker["tag"] = tracker_other_tag
tracker["max_ratio"] = self.util.check_for_attribute(
self.data,
"max_ratio",
parent="tracker",
subparent="other",
var_type="float",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["min_seeding_time"] = self.util.check_for_attribute(
self.data,
"min_seeding_time",
parent="tracker",
subparent="other",
var_type="int",
min_int=0,
do_print=False,
default=-1,
save=False,
)
tracker["max_seeding_time"] = self.util.check_for_attribute(
self.data,
"max_seeding_time",
parent="tracker",
subparent="other",
var_type="int",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["limit_upload_speed"] = self.util.check_for_attribute(
self.data,
"limit_upload_speed",
parent="tracker",
subparent="other",
var_type="int",
min_int=-1,
do_print=False,
default=0,
save=False,
)
tracker["notifiarr"] = self.util.check_for_attribute(
self.data,
"notifiarr",
parent="tracker",
subparent="other",
default_is_none=True,
do_print=False,
save=False,
)
return tracker
if tracker["url"]:
logger.trace(f"tracker url: {tracker['url']}")
if tracker_other_tag:
default_tag = tracker_other_tag
else:
default_tag = tracker["url"].split(os.sep)[2].split(":")[0]
tracker["tag"] = self.util.check_for_attribute(
self.data, "tag", parent="tracker", subparent=default_tag, default=default_tag, var_type="list"
)
if isinstance(tracker["tag"], str):
tracker["tag"] = [tracker["tag"]]
try:
self.data["tracker"][default_tag]["tag"] = [default_tag]
except Exception:
self.data["tracker"][default_tag] = {"tag": [default_tag]}
e = f'No tags matched for {tracker["url"]}. Please check your config.yml file. Setting tag to {default_tag}'
self.notify(e, "Tag", False)
logger.warning(e)
return tracker
# Get category from config file based on path provided
def get_category(self, path):
category = ""
path = os.path.join(path, "")
if "cat" in self.data and self.data["cat"] is not None:
cat_path = self.data["cat"]
for cat, save_path in cat_path.items():
if os.path.join(save_path, "") == path:
category = cat
break
if not category:
default_cat = path.split(os.sep)[-2]
category = str(default_cat)
self.util.check_for_attribute(self.data, default_cat, parent="cat", default=path)
self.data["cat"][str(default_cat)] = path
e = f"No categories matched for the save path {path}. Check your config.yml file. - Setting category to {default_cat}"
self.notify(e, "Category", False)
logger.warning(e)
return category
# Empty old files from recycle bin or orphaned
def cleanup_dirs(self, location):
num_del = 0

3
modules/core/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
modules.core contains all the core functions of qbit_manage such as updating categories/tags etc..
"""

78
modules/core/category.py Normal file
View file

@ -0,0 +1,78 @@
from qbittorrentapi import Conflict409Error
from modules import util
logger = util.logger
class Category:
def __init__(self, qbit_manager):
self.qbt = qbit_manager
self.config = qbit_manager.config
self.client = qbit_manager.client
self.stats = 0
self.category()
def category(self):
"""Update category for torrents that don't have any category defined and returns total number categories updated"""
self.stats = 0
logger.separator("Updating Categories", space=False, border=False)
torrent_list = self.qbt.get_torrents({"category": "", "status_filter": "completed"})
for torrent in torrent_list:
new_cat = self.qbt.get_category(torrent.save_path)
self.update_cat(torrent, new_cat, False)
# Change categories
if self.config.cat_change:
for old_cat in self.config.cat_change:
torrent_list = self.qbt.get_torrents({"category": old_cat, "status_filter": "completed"})
for torrent in torrent_list:
new_cat = self.config.cat_change[old_cat]
self.update_cat(torrent, new_cat, True)
if self.stats >= 1:
logger.print_line(
f"{'Did not update' if self.config.dry_run else 'Updated'} {self.stats} new categories.", self.config.loglevel
)
else:
logger.print_line("No new torrents to categorize.", self.config.loglevel)
def update_cat(self, torrent, new_cat, cat_change):
"""Update category based on the torrent information"""
tracker = self.qbt.get_tags(torrent.trackers)
old_cat = torrent.category
if not self.config.dry_run:
try:
torrent.set_category(category=new_cat)
if torrent.auto_tmm is False and self.config.settings["force_auto_tmm"]:
torrent.set_auto_management(True)
except Conflict409Error:
ex = logger.print_line(
f'Existing category "{new_cat}" not found for save path {torrent.save_path}, category will be created.',
self.config.loglevel,
)
self.config.notify(ex, "Update Category", False)
self.client.torrent_categories.create_category(name=new_cat, save_path=torrent.save_path)
torrent.set_category(category=new_cat)
body = []
body += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
if cat_change:
body += logger.print_line(logger.insert_space(f"Old Category: {old_cat}", 3), self.config.loglevel)
title = "Moving Categories"
else:
title = "Updating Categories"
body += logger.print_line(logger.insert_space(f"New Category: {new_cat}", 3), self.config.loglevel)
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
attr = {
"function": "cat_update",
"title": title,
"body": "\n".join(body),
"torrent_name": torrent.name,
"torrent_category": new_cat,
"torrent_tracker": tracker["url"],
"notifiarr_indexer": tracker["notifiarr"],
}
self.config.send_notifications(attr)
self.stats += 1

62
modules/core/tags.py Normal file
View file

@ -0,0 +1,62 @@
from modules import util
logger = util.logger
class Tags:
def __init__(self, qbit_manager):
self.qbt = qbit_manager
self.config = qbit_manager.config
self.client = qbit_manager.client
self.stats = 0
self.tags()
def tags(self):
"""Update tags for torrents"""
self.stats = 0
ignore_tags = self.config.settings["ignoreTags_OnUpdate"]
logger.separator("Updating Tags", space=False, border=False)
for torrent in self.qbt.torrent_list:
check_tags = util.get_list(torrent.tags)
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)
if tracker["tag"]:
self.stats += len(tracker["tag"])
body = []
body += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
body += logger.print_line(
logger.insert_space(f'New Tag{"s" if len(tracker["tag"]) > 1 else ""}: {", ".join(tracker["tag"])}', 8),
self.config.loglevel,
)
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
body.extend(
self.qbt.set_tags_and_limits(
torrent,
tracker["max_ratio"],
tracker["max_seeding_time"],
tracker["limit_upload_speed"],
tracker["tag"],
)
)
category = self.qbt.get_category(torrent.save_path) if torrent.category == "" else torrent.category
attr = {
"function": "tag_update",
"title": "Updating Tags",
"body": "\n".join(body),
"torrent_name": torrent.name,
"torrent_category": category,
"torrent_tag": ", ".join(tracker["tag"]),
"torrent_tracker": tracker["url"],
"notifiarr_indexer": tracker["notifiarr"],
"torrent_max_ratio": tracker["max_ratio"],
"torrent_max_seeding_time": tracker["max_seeding_time"],
"torrent_limit_upload_speed": tracker["limit_upload_speed"],
}
self.config.send_notifications(attr)
if self.stats >= 1:
logger.print_line(
f"{'Did not update' if self.config.dry_run else 'Updated'} {self.stats} new tags.", self.config.loglevel
)
else:
logger.print_line("No new torrents to tag.", self.config.loglevel)

View file

@ -7,7 +7,6 @@ from fnmatch import fnmatch
from qbittorrentapi import APIConnectionError
from qbittorrentapi import Client
from qbittorrentapi import Conflict409Error
from qbittorrentapi import LoginFailed
from qbittorrentapi import NotFound404Error
from qbittorrentapi import Version
@ -26,6 +25,7 @@ class Qbt:
SUPPORTED_VERSION = Version.latest_supported_app_version()
MIN_SUPPORTED_VERSION = "v4.3.0"
TORRENT_DICT_COMMANDS = ["recheck", "cross_seed", "rem_unregistered", "tag_tracker_error", "tag_nohardlinks"]
def __init__(self, config, params):
self.config = config
@ -83,10 +83,18 @@ class Qbt:
self.global_max_seeding_time_enabled = self.client.app.preferences.max_seeding_time_enabled
self.global_max_seeding_time = self.client.app.preferences.max_seeding_time
def get_torrent_info(torrent_list):
if any(config.commands.get(command, False) for command in self.TORRENT_DICT_COMMANDS):
# Get an updated torrent dictionary information of the torrents
self.get_torrent_info()
else:
self.torrentinfo = None
self.torrentissue = None
self.torrentvalid = None
def get_torrent_info(self):
"""
Will create a 2D Dictionary with the torrent name as the key
torrentdict = {'TorrentName1' : {'Category':'TV', 'save_path':'/data/torrents/TV', 'count':1, 'msg':'[]'...},
self.torrentinfo = {'TorrentName1' : {'Category':'TV', 'save_path':'/data/torrents/TV', 'count':1, 'msg':'[]'...},
'TorrentName2' : {'Category':'Movies', 'save_path':'/data/torrents/Movies'}, 'count':2, 'msg':'[]'...}
List of dictionary key definitions
Category = Returns category of the torrent (str)
@ -104,9 +112,9 @@ class Qbt:
first_hash = Returns the hash number of the original torrent (Assuming the torrent list is sorted by date added (Asc))
Takes in a number n, returns the square of n
"""
torrentdict = {}
t_obj_unreg = [] # list of unregistered torrent objects
t_obj_valid = [] # list of working torrents
self.torrentinfo = {}
self.torrentissue = [] # list of unregistered torrent objects
self.torrentvalid = [] # list of working torrents
t_obj_list = [] # list of all torrent objects
settings = self.config.settings
logger.separator("Checking Settings", space=False, border=False)
@ -115,18 +123,13 @@ class Qbt:
"force_auto_tmm set to True. Will force Auto Torrent Management for all torrents.", self.config.loglevel
)
logger.separator("Gathering Torrent Information", space=True, border=True)
for torrent in torrent_list:
for torrent in self.torrent_list:
is_complete = False
msg = None
status = None
working_tracker = None
issue = {"potential": False}
if (
torrent.auto_tmm is False
and settings["force_auto_tmm"]
and torrent.category != ""
and not self.config.dry_run
):
if torrent.auto_tmm is False and settings["force_auto_tmm"] and torrent.category != "" and not self.config.dry_run:
torrent.set_auto_management(True)
try:
torrent_name = torrent.name
@ -138,13 +141,13 @@ class Qbt:
except Exception as ex:
self.config.notify(ex, "Get Torrent Info", False)
logger.warning(ex)
if torrent_name in torrentdict:
if torrent_name in self.torrentinfo:
t_obj_list.append(torrent)
t_count = torrentdict[torrent_name]["count"] + 1
msg_list = torrentdict[torrent_name]["msg"]
status_list = torrentdict[torrent_name]["status"]
is_complete = True if torrentdict[torrent_name]["is_complete"] is True else torrent_is_complete
first_hash = torrentdict[torrent_name]["first_hash"]
t_count = self.torrentinfo[torrent_name]["count"] + 1
msg_list = self.torrentinfo[torrent_name]["msg"]
status_list = self.torrentinfo[torrent_name]["status"]
is_complete = True if self.torrentinfo[torrent_name]["is_complete"] is True else torrent_is_complete
first_hash = self.torrentinfo[torrent_name]["first_hash"]
else:
t_obj_list = [torrent]
t_count = 1
@ -176,11 +179,11 @@ class Qbt:
if working_tracker:
status = 2
msg = ""
t_obj_valid.append(torrent)
self.torrentvalid.append(torrent)
elif issue["potential"]:
status = issue["status"]
msg = issue["msg"]
t_obj_unreg.append(torrent)
self.torrentissue.append(torrent)
if msg is not None:
msg_list.append(msg)
if status is not None:
@ -195,144 +198,12 @@ class Qbt:
"is_complete": is_complete,
"first_hash": first_hash,
}
torrentdict[torrent_name] = torrentattr
return torrentdict, t_obj_unreg, t_obj_valid
self.torrentinfo = None
self.torrentissue = None
self.torrentvalid = None
if (
config.commands["recheck"]
or config.commands["cross_seed"]
or config.commands["rem_unregistered"]
or config.commands["tag_tracker_error"]
or config.commands["tag_nohardlinks"]
):
# Get an updated torrent dictionary information of the torrents
self.torrentinfo, self.torrentissue, self.torrentvalid = get_torrent_info(self.torrent_list)
self.torrentinfo[torrent_name] = torrentattr
def get_torrents(self, params):
"""Get torrents from qBittorrent"""
return self.client.torrents.info(**params)
def category(self):
"""Update category for torrents"""
num_cat = 0
def update_cat(new_cat, cat_change):
nonlocal torrent, num_cat
tracker = self.config.get_tags(torrent.trackers)
old_cat = torrent.category
if not self.config.dry_run:
try:
torrent.set_category(category=new_cat)
if torrent.auto_tmm is False and self.config.settings["force_auto_tmm"]:
torrent.set_auto_management(True)
except Conflict409Error:
ex = logger.print_line(
f'Existing category "{new_cat}" not found for save path {torrent.save_path}, category will be created.',
self.config.loglevel,
)
self.config.notify(ex, "Update Category", False)
self.client.torrent_categories.create_category(name=new_cat, save_path=torrent.save_path)
torrent.set_category(category=new_cat)
body = []
body += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
if cat_change:
body += logger.print_line(logger.insert_space(f"Old Category: {old_cat}", 3), self.config.loglevel)
title = "Moving Categories"
else:
title = "Updating Categories"
body += logger.print_line(logger.insert_space(f"New Category: {new_cat}", 3), self.config.loglevel)
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
attr = {
"function": "cat_update",
"title": title,
"body": "\n".join(body),
"torrent_name": torrent.name,
"torrent_category": new_cat,
"torrent_tracker": tracker["url"],
"notifiarr_indexer": tracker["notifiarr"],
}
self.config.send_notifications(attr)
num_cat += 1
if self.config.commands["cat_update"]:
logger.separator("Updating Categories", space=False, border=False)
torrent_list = self.get_torrents({"category": "", "status_filter": "completed"})
for torrent in torrent_list:
new_cat = self.config.get_category(torrent.save_path)
update_cat(new_cat, False)
# Change categories
if self.config.cat_change:
for old_cat in self.config.cat_change:
torrent_list = self.get_torrents({"category": old_cat, "status_filter": "completed"})
for torrent in torrent_list:
new_cat = self.config.cat_change[old_cat]
update_cat(new_cat, True)
if num_cat >= 1:
logger.print_line(
f"{'Did not update' if self.config.dry_run else 'Updated'} {num_cat} new categories.", self.config.loglevel
)
else:
logger.print_line("No new torrents to categorize.", self.config.loglevel)
return num_cat
def tags(self):
"""Update tags for torrents"""
num_tags = 0
ignore_tags = self.config.settings["ignoreTags_OnUpdate"]
if self.config.commands["tag_update"]:
logger.separator("Updating Tags", space=False, border=False)
for torrent in self.torrent_list:
check_tags = util.get_list(torrent.tags)
if torrent.tags == "" or (len([trk for trk in check_tags if trk not in ignore_tags]) == 0):
tracker = self.config.get_tags(torrent.trackers)
if tracker["tag"]:
num_tags += len(tracker["tag"])
body = []
body += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
body += logger.print_line(
logger.insert_space(
f'New Tag{"s" if len(tracker["tag"]) > 1 else ""}: {", ".join(tracker["tag"])}', 8
),
self.config.loglevel,
)
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
body.extend(
self.set_tags_and_limits(
torrent,
tracker["max_ratio"],
tracker["max_seeding_time"],
tracker["limit_upload_speed"],
tracker["tag"],
)
)
category = self.config.get_category(torrent.save_path) if torrent.category == "" else torrent.category
attr = {
"function": "tag_update",
"title": "Updating Tags",
"body": "\n".join(body),
"torrent_name": torrent.name,
"torrent_category": category,
"torrent_tag": ", ".join(tracker["tag"]),
"torrent_tracker": tracker["url"],
"notifiarr_indexer": tracker["notifiarr"],
"torrent_max_ratio": tracker["max_ratio"],
"torrent_max_seeding_time": tracker["max_seeding_time"],
"torrent_limit_upload_speed": tracker["limit_upload_speed"],
}
self.config.send_notifications(attr)
if num_tags >= 1:
logger.print_line(
f"{'Did not update' if self.config.dry_run else 'Updated'} {num_tags} new tags.", self.config.loglevel
)
else:
logger.print_line("No new torrents to tag.", self.config.loglevel)
return num_tags
def set_tags_and_limits(
self, torrent, max_ratio, max_seeding_time, limit_upload_speed=None, tags=None, restore=False, do_print=True
):
@ -460,6 +331,240 @@ class Qbt:
return body
return False
def get_tags(self, trackers):
"""Get tags from config file based on keyword"""
urls = [x.url for x in trackers if x.url.startswith("http")]
tracker = {}
tracker["tag"] = None
tracker["max_ratio"] = None
tracker["min_seeding_time"] = None
tracker["max_seeding_time"] = None
tracker["limit_upload_speed"] = None
tracker["notifiarr"] = None
tracker["url"] = None
tracker_other_tag = self.config.util.check_for_attribute(
self.config.data, "tag", parent="tracker", subparent="other", default_is_none=True, var_type="list", save=False
)
try:
tracker["url"] = util.trunc_val(urls[0], os.sep)
except IndexError as e:
tracker["url"] = None
if not urls:
urls = []
if not tracker_other_tag:
tracker_other_tag = ["other"]
tracker["url"] = "No http URL found"
else:
logger.debug(f"Tracker Url:{urls}")
logger.debug(e)
if "tracker" in self.config.data and self.config.data["tracker"] is not None:
tag_values = self.config.data["tracker"]
for tag_url, tag_details in tag_values.items():
for url in urls:
if tag_url in url:
if tracker["url"] is None:
default_tag = tracker_other_tag
else:
try:
tracker["url"] = util.trunc_val(url, os.sep)
default_tag = tracker["url"].split(os.sep)[2].split(":")[0]
except IndexError as e:
logger.debug(f"Tracker Url:{url}")
logger.debug(e)
# Tracker Format 1 deprecated.
if isinstance(tag_details, str):
e = (
"Config Error: Tracker format invalid. Please see config.yml.sample for correct format and fix "
f"`{tag_details}` in the Tracker section of the config."
)
self.config.notify(e, "Config")
raise Failed(e)
# Using new Format
else:
tracker["tag"] = self.config.util.check_for_attribute(
self.config.data, "tag", parent="tracker", subparent=tag_url, default=tag_url, var_type="list"
)
if tracker["tag"] == [tag_url]:
self.config.data["tracker"][tag_url]["tag"] = [tag_url]
if isinstance(tracker["tag"], str):
tracker["tag"] = [tracker["tag"]]
is_max_ratio_defined = self.config.data["tracker"].get("max_ratio")
is_max_seeding_time_defined = self.config.data["tracker"].get("max_seeding_time")
if is_max_ratio_defined or is_max_seeding_time_defined:
tracker["max_ratio"] = self.config.util.check_for_attribute(
self.config.data,
"max_ratio",
parent="tracker",
subparent=tag_url,
var_type="float",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["max_seeding_time"] = self.config.util.check_for_attribute(
self.config.data,
"max_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
else:
tracker["max_ratio"] = self.config.util.check_for_attribute(
self.config.data,
"max_ratio",
parent="tracker",
subparent=tag_url,
var_type="float",
min_int=-2,
do_print=False,
default_is_none=True,
save=False,
)
tracker["max_seeding_time"] = self.config.util.check_for_attribute(
self.config.data,
"max_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-2,
do_print=False,
default_is_none=True,
save=False,
)
tracker["min_seeding_time"] = self.config.util.check_for_attribute(
self.config.data,
"min_seeding_time",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=0,
do_print=False,
default=0,
save=False,
)
tracker["limit_upload_speed"] = self.config.util.check_for_attribute(
self.config.data,
"limit_upload_speed",
parent="tracker",
subparent=tag_url,
var_type="int",
min_int=-1,
do_print=False,
default=0,
save=False,
)
tracker["notifiarr"] = self.config.util.check_for_attribute(
self.config.data,
"notifiarr",
parent="tracker",
subparent=tag_url,
default_is_none=True,
do_print=False,
save=False,
)
return tracker
if tracker_other_tag:
tracker["tag"] = tracker_other_tag
tracker["max_ratio"] = self.config.util.check_for_attribute(
self.config.data,
"max_ratio",
parent="tracker",
subparent="other",
var_type="float",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["min_seeding_time"] = self.config.util.check_for_attribute(
self.config.data,
"min_seeding_time",
parent="tracker",
subparent="other",
var_type="int",
min_int=0,
do_print=False,
default=-1,
save=False,
)
tracker["max_seeding_time"] = self.config.util.check_for_attribute(
self.config.data,
"max_seeding_time",
parent="tracker",
subparent="other",
var_type="int",
min_int=-2,
do_print=False,
default=-1,
save=False,
)
tracker["limit_upload_speed"] = self.config.util.check_for_attribute(
self.config.data,
"limit_upload_speed",
parent="tracker",
subparent="other",
var_type="int",
min_int=-1,
do_print=False,
default=0,
save=False,
)
tracker["notifiarr"] = self.config.util.check_for_attribute(
self.config.data,
"notifiarr",
parent="tracker",
subparent="other",
default_is_none=True,
do_print=False,
save=False,
)
return tracker
if tracker["url"]:
logger.trace(f"tracker url: {tracker['url']}")
if tracker_other_tag:
default_tag = tracker_other_tag
else:
default_tag = tracker["url"].split(os.sep)[2].split(":")[0]
tracker["tag"] = self.config.util.check_for_attribute(
self.config.data, "tag", parent="tracker", subparent=default_tag, default=default_tag, var_type="list"
)
if isinstance(tracker["tag"], str):
tracker["tag"] = [tracker["tag"]]
try:
self.config.data["tracker"][default_tag]["tag"] = [default_tag]
except Exception:
self.config.data["tracker"][default_tag] = {"tag": [default_tag]}
e = f'No tags matched for {tracker["url"]}. Please check your config.yml file. Setting tag to {default_tag}'
self.config.notify(e, "Tag", False)
logger.warning(e)
return tracker
def get_category(self, path):
"""Get category from config file based on path provided"""
category = ""
path = os.path.join(path, "")
if "cat" in self.config.data and self.config.data["cat"] is not None:
cat_path = self.config.data["cat"]
for cat, save_path in cat_path.items():
if os.path.join(save_path, "") == path:
category = cat
break
if not category:
default_cat = path.split(os.sep)[-2]
category = str(default_cat)
self.config.util.check_for_attribute(self.config.data, default_cat, parent="cat", default=path)
self.config.data["cat"][str(default_cat)] = path
e = f"No categories matched for the save path {path}. Check your config.yml file. - Setting category to {default_cat}"
self.config.notify(e, "Category", False)
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
@ -530,7 +635,7 @@ class Qbt:
logger.warning(ex)
continue
for torrent in torrent_list:
tracker = self.config.get_tags(torrent.trackers)
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
@ -539,7 +644,7 @@ class Qbt:
# 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.config.get_tags(torrent.trackers)
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
@ -666,7 +771,7 @@ class Qbt:
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.config.get_tags(torrent.trackers)
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(
@ -852,7 +957,7 @@ class Qbt:
check_tags = util.get_list(torrent.tags)
# Remove any error torrents Tags that are no longer unreachable.
if tag_error in check_tags:
tracker = self.config.get_tags(torrent.trackers)
tracker = self.get_tags(torrent.trackers)
num_untag += 1
body = []
body += logger.print_line(
@ -884,7 +989,7 @@ class Qbt:
try:
for trk in torrent.trackers:
if trk.url.startswith("http"):
tracker = self.config.get_tags([trk])
tracker = self.get_tags([trk])
msg_up = trk.msg.upper()
msg = trk.msg
# Tag any error torrents
@ -964,16 +1069,16 @@ class Qbt:
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 torrentdict key)
# Substring Key match in dictionary (used because t_name might not match exactly with self.torrentinfo key)
# Returned the dictionary of filtered item
torrentdict_file = dict(filter(lambda item: tr_name in item[0], self.torrentinfo.items()))
if torrentdict_file:
# Get the exact torrent match name from torrentdict
# Get the exact torrent match name from self.torrentinfo
t_name = next(iter(torrentdict_file))
dest = os.path.join(self.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.config.get_category(dest)
category = self.get_category(dest)
# Only add cross-seed torrent if original torrent is complete
if self.torrentinfo[t_name]["is_complete"]:
categories.append(category)
@ -1023,7 +1128,7 @@ class Qbt:
and self.torrentinfo[t_name]["count"] > 1
and self.torrentinfo[t_name]["first_hash"] != torrent.hash
):
tracker = self.config.get_tags(torrent.trackers)
tracker = self.get_tags(torrent.trackers)
tagged += 1
body = logger.print_line(
f"{'Not Adding' if self.config.dry_run else 'Adding'} 'cross-seed' tag to {t_name}", self.config.loglevel
@ -1069,7 +1174,7 @@ class Qbt:
torrent_list = self.get_torrents({"status_filter": "paused", "sort": "size"})
if torrent_list:
for torrent in torrent_list:
tracker = self.config.get_tags(torrent.trackers)
tracker = self.get_tags(torrent.trackers)
# Resume torrent if completed
if torrent.progress == 1:
if torrent.max_ratio < 0 and torrent.max_seeding_time < 0:

View file

@ -287,6 +287,8 @@ util.logger = logger
from modules.config import Config # noqa
from modules.util import GracefulKiller # noqa
from modules.util import Failed # noqa
from modules.core.category import Category # noqa
from modules.core.tags import Tags # noqa
def my_except_hook(exctype, value, tbi):
@ -369,6 +371,8 @@ def start():
try:
cfg = Config(default_dir, args)
qbit_manager = cfg.qbt
except Exception as ex:
if "Qbittorrent Error" in ex.args[0]:
logger.print_line(ex, "CRITICAL")
@ -379,14 +383,14 @@ def start():
logger.stacktrace()
logger.print_line(ex, "CRITICAL")
if cfg:
if qbit_manager:
# Set Category
num_categorized = cfg.qbt.category()
stats["categorized"] += num_categorized
if cfg.commands["cat_update"]:
stats["categorized"] += Category(qbit_manager).stats
# Set Tags
num_tagged = cfg.qbt.tags()
stats["tagged"] += num_tagged
if cfg.commands["tag_update"]:
stats["tagged"] += Tags(qbit_manager).stats
# Remove Unregistered Torrents
num_deleted, num_deleted_contents, num_tagged, num_untagged = cfg.qbt.rem_unregistered()