mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2025-12-17 22:28:27 +08:00
Fixes #359 using save_files
This commit is contained in:
parent
c1ace03502
commit
1e3a45df86
5 changed files with 81 additions and 20 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
4.0.9-develop11
|
4.0.9-develop12
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,6 @@ class CrossSeed:
|
||||||
self.client.torrents.add(
|
self.client.torrents.add(
|
||||||
torrent_files=src, save_path=dest, category=category, tags=self.cross_seed_tag, is_paused=True
|
torrent_files=src, save_path=dest, category=category, tags=self.cross_seed_tag, is_paused=True
|
||||||
)
|
)
|
||||||
self.qbt.torrentinfo[t_name]["count"] += 1
|
|
||||||
try:
|
try:
|
||||||
torrent_hash_generator = TorrentHashGenerator(src)
|
torrent_hash_generator = TorrentHashGenerator(src)
|
||||||
torrent_hash = torrent_hash_generator.generate_torrent_hash()
|
torrent_hash = torrent_hash_generator.generate_torrent_hash()
|
||||||
|
|
@ -90,6 +89,7 @@ class CrossSeed:
|
||||||
logger.warning(f"Unable to find hash {torrent_hash} in qbt: {e}")
|
logger.warning(f"Unable to find hash {torrent_hash} in qbt: {e}")
|
||||||
if torrent_info:
|
if torrent_info:
|
||||||
torrent = torrent_info[0]
|
torrent = torrent_info[0]
|
||||||
|
self.qbt.add_torrent_files(torrent.hash, torrent.files)
|
||||||
self.qbt.torrentvalid.append(torrent)
|
self.qbt.torrentvalid.append(torrent)
|
||||||
self.qbt.torrentinfo[t_name]["torrents"].append(torrent)
|
self.qbt.torrentinfo[t_name]["torrents"].append(torrent)
|
||||||
self.qbt.torrent_list.append(torrent)
|
self.qbt.torrent_list.append(torrent)
|
||||||
|
|
@ -114,8 +114,7 @@ class CrossSeed:
|
||||||
t_cat = torrent.category
|
t_cat = torrent.category
|
||||||
if (
|
if (
|
||||||
not util.is_tag_in_torrent(self.cross_seed_tag, torrent.tags)
|
not util.is_tag_in_torrent(self.cross_seed_tag, torrent.tags)
|
||||||
and self.qbt.torrentinfo[t_name]["count"] > 1
|
and self.qbt.is_cross_seed(torrent)
|
||||||
and self.qbt.torrentinfo[t_name]["first_hash"] != torrent.hash
|
|
||||||
and torrent.downloaded == 0
|
and torrent.downloaded == 0
|
||||||
and torrent.seeding_time > 0
|
and torrent.seeding_time > 0
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ class RemoveUnregistered:
|
||||||
"torrent_tracker": tracker["url"],
|
"torrent_tracker": tracker["url"],
|
||||||
"notifiarr_indexer": tracker["notifiarr"],
|
"notifiarr_indexer": tracker["notifiarr"],
|
||||||
}
|
}
|
||||||
if self.qbt.torrentinfo[self.t_name]["count"] > 1:
|
if self.qbt.has_cross_seed(torrent):
|
||||||
# Checks if any of the original torrents are working
|
# Checks if any of the original torrents are working
|
||||||
if "" in self.t_msg or 2 in self.t_status:
|
if "" in self.t_msg or 2 in self.t_status:
|
||||||
attr["torrents_deleted_and_contents"] = False
|
attr["torrents_deleted_and_contents"] = False
|
||||||
|
|
@ -232,4 +232,3 @@ class RemoveUnregistered:
|
||||||
attr["body"] = "\n".join(body)
|
attr["body"] = "\n".join(body)
|
||||||
self.torrents_updated_unreg.append(self.t_name)
|
self.torrents_updated_unreg.append(self.t_name)
|
||||||
self.notify_attr_unreg.append(attr)
|
self.notify_attr_unreg.append(attr)
|
||||||
self.qbt.torrentinfo[self.t_name]["count"] -= 1
|
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ class ShareLimits:
|
||||||
for torrent_hash, torrent_dict in self.tdel_dict.items():
|
for torrent_hash, torrent_dict in self.tdel_dict.items():
|
||||||
torrent = torrent_dict["torrent"]
|
torrent = torrent_dict["torrent"]
|
||||||
t_name = torrent.name
|
t_name = torrent.name
|
||||||
t_count = self.qbt.torrentinfo[t_name]["count"]
|
|
||||||
t_msg = self.qbt.torrentinfo[t_name]["msg"]
|
t_msg = self.qbt.torrentinfo[t_name]["msg"]
|
||||||
t_status = self.qbt.torrentinfo[t_name]["status"]
|
t_status = self.qbt.torrentinfo[t_name]["status"]
|
||||||
# Double check that the content path is the same before we delete anything
|
# Double check that the content path is the same before we delete anything
|
||||||
|
|
@ -106,7 +105,7 @@ class ShareLimits:
|
||||||
}
|
}
|
||||||
if os.path.exists(torrent["content_path"].replace(self.root_dir, self.remote_dir)):
|
if os.path.exists(torrent["content_path"].replace(self.root_dir, self.remote_dir)):
|
||||||
# Checks if any of the original torrents are working
|
# Checks if any of the original torrents are working
|
||||||
if t_count > 1 and ("" in t_msg or 2 in t_status):
|
if self.qbt.has_cross_seed(torrent) and ("" in t_msg or 2 in t_status):
|
||||||
self.stats_deleted += 1
|
self.stats_deleted += 1
|
||||||
attr["torrents_deleted_and_contents"] = False
|
attr["torrents_deleted_and_contents"] = False
|
||||||
t_deleted.add(t_name)
|
t_deleted.add(t_name)
|
||||||
|
|
@ -137,7 +136,6 @@ class ShareLimits:
|
||||||
attr["body"] = "\n".join(body)
|
attr["body"] = "\n".join(body)
|
||||||
if not group_notifications:
|
if not group_notifications:
|
||||||
self.config.send_notifications(attr)
|
self.config.send_notifications(attr)
|
||||||
self.qbt.torrentinfo[t_name]["count"] -= 1
|
|
||||||
if group_notifications:
|
if group_notifications:
|
||||||
if t_deleted:
|
if t_deleted:
|
||||||
attr = {
|
attr = {
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ class Qbt:
|
||||||
raise Failed(exc)
|
raise Failed(exc)
|
||||||
logger.separator("Getting Torrent List", space=False, border=False)
|
logger.separator("Getting Torrent List", space=False, border=False)
|
||||||
self.torrent_list = self.get_torrents({"sort": "added_on"})
|
self.torrent_list = self.get_torrents({"sort": "added_on"})
|
||||||
|
self.torrentfiles = {} # a map of torrent files to track cross-seeds
|
||||||
|
|
||||||
self.global_max_ratio_enabled = self.client.app.preferences.max_ratio_enabled
|
self.global_max_ratio_enabled = self.client.app.preferences.max_ratio_enabled
|
||||||
self.global_max_ratio = self.client.app.preferences.max_ratio
|
self.global_max_ratio = self.client.app.preferences.max_ratio
|
||||||
|
|
@ -97,12 +98,11 @@ class Qbt:
|
||||||
def get_torrent_info(self):
|
def get_torrent_info(self):
|
||||||
"""
|
"""
|
||||||
Will create a 2D Dictionary with the torrent name as the key
|
Will create a 2D Dictionary with the torrent name as the key
|
||||||
self.torrentinfo = {'TorrentName1' : {'Category':'TV', 'save_path':'/data/torrents/TV', 'count':1, 'msg':'[]'...},
|
self.torrentinfo = {'TorrentName1' : {'Category':'TV', 'save_path':'/data/torrents/TV', 'msg':'[]'...},
|
||||||
'TorrentName2' : {'Category':'Movies', 'save_path':'/data/torrents/Movies'}, 'count':2, 'msg':'[]'...}
|
'TorrentName2' : {'Category':'Movies', 'save_path':'/data/torrents/Movies'}, 'msg':'[]'...}
|
||||||
List of dictionary key definitions
|
List of dictionary key definitions
|
||||||
Category = Returns category of the torrent (str)
|
Category = Returns category of the torrent (str)
|
||||||
save_path = Returns the save path of the torrent (str)
|
save_path = Returns the save path of the torrent (str)
|
||||||
count = Returns a count of the total number of torrents with the same name (int)
|
|
||||||
msg = Returns a list of torrent messages by name (list of str)
|
msg = Returns a list of torrent messages by name (list of str)
|
||||||
status = Returns the list of status numbers of the torrent by name
|
status = Returns the list of status numbers of the torrent by name
|
||||||
(0: Tracker is disabled (used for DHT, PeX, and LSD),
|
(0: Tracker is disabled (used for DHT, PeX, and LSD),
|
||||||
|
|
@ -112,8 +112,6 @@ class Qbt:
|
||||||
4: Tracker has been contacted, but it is not working (or doesn't send proper replies)
|
4: Tracker has been contacted, but it is not working (or doesn't send proper replies)
|
||||||
is_complete = Returns the state of torrent
|
is_complete = Returns the state of torrent
|
||||||
(Returns True if at least one of the torrent with the State is categorized as Complete.)
|
(Returns True if at least one of the torrent with the State is categorized as Complete.)
|
||||||
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
|
|
||||||
"""
|
"""
|
||||||
self.torrentinfo = {}
|
self.torrentinfo = {}
|
||||||
self.torrentissue = [] # list of unregistered torrent objects
|
self.torrentissue = [] # list of unregistered torrent objects
|
||||||
|
|
@ -141,23 +139,20 @@ class Qbt:
|
||||||
save_path = torrent.save_path
|
save_path = torrent.save_path
|
||||||
category = torrent.category
|
category = torrent.category
|
||||||
torrent_trackers = torrent.trackers
|
torrent_trackers = torrent.trackers
|
||||||
|
self.add_torrent_files(torrent_hash, torrent.files)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.config.notify(ex, "Get Torrent Info", False)
|
self.config.notify(ex, "Get Torrent Info", False)
|
||||||
logger.warning(ex)
|
logger.warning(ex)
|
||||||
if torrent_name in self.torrentinfo:
|
if torrent_name in self.torrentinfo:
|
||||||
t_obj_list.append(torrent)
|
t_obj_list.append(torrent)
|
||||||
t_count = self.torrentinfo[torrent_name]["count"] + 1
|
|
||||||
msg_list = self.torrentinfo[torrent_name]["msg"]
|
msg_list = self.torrentinfo[torrent_name]["msg"]
|
||||||
status_list = self.torrentinfo[torrent_name]["status"]
|
status_list = self.torrentinfo[torrent_name]["status"]
|
||||||
is_complete = True if self.torrentinfo[torrent_name]["is_complete"] is True else torrent_is_complete
|
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:
|
else:
|
||||||
t_obj_list = [torrent]
|
t_obj_list = [torrent]
|
||||||
t_count = 1
|
|
||||||
msg_list = []
|
msg_list = []
|
||||||
status_list = []
|
status_list = []
|
||||||
is_complete = torrent_is_complete
|
is_complete = torrent_is_complete
|
||||||
first_hash = torrent_hash
|
|
||||||
for trk in torrent_trackers:
|
for trk in torrent_trackers:
|
||||||
if trk.url.startswith("http"):
|
if trk.url.startswith("http"):
|
||||||
status = trk.status
|
status = trk.status
|
||||||
|
|
@ -188,14 +183,80 @@ class Qbt:
|
||||||
"torrents": t_obj_list,
|
"torrents": t_obj_list,
|
||||||
"Category": category,
|
"Category": category,
|
||||||
"save_path": save_path,
|
"save_path": save_path,
|
||||||
"count": t_count,
|
|
||||||
"msg": msg_list,
|
"msg": msg_list,
|
||||||
"status": status_list,
|
"status": status_list,
|
||||||
"is_complete": is_complete,
|
"is_complete": is_complete,
|
||||||
"first_hash": first_hash,
|
|
||||||
}
|
}
|
||||||
self.torrentinfo[torrent_name] = torrentattr
|
self.torrentinfo[torrent_name] = torrentattr
|
||||||
|
|
||||||
|
def add_torrent_files(self, torrent_hash, torrent_files):
|
||||||
|
"""Process torrent files by adding the hash to the appropriate torrent_files list.
|
||||||
|
Example structure:
|
||||||
|
torrent_files = {
|
||||||
|
"folder1/file1.txt": {"original": torrent_hash1, "cross_seed": ["torrent_hash2", "torrent_hash3"]},
|
||||||
|
"folder1/file2.txt": {"original": torrent_hash1, "cross_seed": ["torrent_hash2"]},
|
||||||
|
"folder2/file1.txt": {"original": torrent_hash2, "cross_seed": []},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
for file in torrent_files:
|
||||||
|
file_name = file.name
|
||||||
|
if file_name not in self.torrentfiles:
|
||||||
|
self.torrentfiles[file_name] = {"original": torrent_hash, "cross_seed": []}
|
||||||
|
else:
|
||||||
|
self.torrentfiles[file_name]["cross_seed"].append(torrent_hash)
|
||||||
|
|
||||||
|
def is_cross_seed(self, torrent):
|
||||||
|
"""Check if the torrent is a cross seed if it has one or more files that are cross seeded."""
|
||||||
|
t_hash = torrent.hash
|
||||||
|
t_name = torrent.name
|
||||||
|
if torrent.downloaded != 0:
|
||||||
|
logger.trace(f"Torrent: {t_name} [Hash: {t_hash}] is not a cross seeded torrent. Download is > 0.")
|
||||||
|
return False
|
||||||
|
cross_seed = True
|
||||||
|
for file in torrent.files:
|
||||||
|
file_name = file.name
|
||||||
|
if self.torrentfiles[file_name]["original"] == t_hash or t_hash not in self.torrentfiles[file_name]["cross_seed"]:
|
||||||
|
logger.trace(f"File: [{file_name}] is found in Torrent: {t_name} [Hash: {t_hash}] as the original torrent")
|
||||||
|
cross_seed = False
|
||||||
|
break
|
||||||
|
elif self.torrentfiles[file_name]["original"] is None:
|
||||||
|
cross_seed = False
|
||||||
|
break
|
||||||
|
logger.trace(f"Torrent: {t_name} [Hash: {t_hash}] {'is' if cross_seed else 'is not'} a cross seed torrent.")
|
||||||
|
return cross_seed
|
||||||
|
|
||||||
|
def has_cross_seed(self, torrent):
|
||||||
|
"""Check if the torrent has a cross seed"""
|
||||||
|
cross_seed = False
|
||||||
|
t_hash = torrent.hash
|
||||||
|
t_name = torrent.name
|
||||||
|
for file in torrent.files:
|
||||||
|
file_name = file.name
|
||||||
|
if len(self.torrentfiles[file_name]["cross_seed"]) > 0:
|
||||||
|
logger.trace(f"{file_name} has cross seeds: {self.torrentfiles[file_name]['cross_seed']}")
|
||||||
|
cross_seed = True
|
||||||
|
break
|
||||||
|
logger.trace(f"Torrent: {t_name} [Hash: {t_hash}] {'has' if cross_seed else 'has no'} cross seeds.")
|
||||||
|
return cross_seed
|
||||||
|
|
||||||
|
def remove_torrent_files(self, torrent):
|
||||||
|
"""Update the torrent_files list after a torrent is deleted"""
|
||||||
|
torrent_hash = torrent.hash
|
||||||
|
for file in torrent.files:
|
||||||
|
file_name = file.name
|
||||||
|
if self.torrentfiles[file_name]["original"] == torrent_hash:
|
||||||
|
if len(self.torrentfiles[file_name]["cross_seed"]) > 0:
|
||||||
|
self.torrentfiles[file_name]["original"] = self.torrentfiles[file_name]["cross_seed"].pop(0)
|
||||||
|
logger.trace(f"Updated {file_name} original to {self.torrentfiles[file_name]['original']}")
|
||||||
|
else:
|
||||||
|
self.torrentfiles[file_name]["original"] = None
|
||||||
|
else:
|
||||||
|
if torrent_hash in self.torrentfiles[file_name]["cross_seed"]:
|
||||||
|
self.torrentfiles[file_name]["cross_seed"].remove(torrent_hash)
|
||||||
|
logger.trace(f"Removed {torrent_hash} from {file_name} cross seeds")
|
||||||
|
logger.trace(f"{file_name} original: {self.torrentfiles[file_name]['original']}")
|
||||||
|
logger.trace(f"{file_name} cross seeds: {self.torrentfiles[file_name]['cross_seed']}")
|
||||||
|
|
||||||
def get_torrents(self, params):
|
def get_torrents(self, params):
|
||||||
"""Get torrents from qBittorrent"""
|
"""Get torrents from qBittorrent"""
|
||||||
return self.client.torrents.info(**params)
|
return self.client.torrents.info(**params)
|
||||||
|
|
@ -424,3 +485,7 @@ class Qbt:
|
||||||
self.torrent_list.remove(torrent)
|
self.torrent_list.remove(torrent)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
logger.debug(f"Torrent {torrent.name} has already been deleted from torrent list.")
|
logger.debug(f"Torrent {torrent.name} has already been deleted from torrent list.")
|
||||||
|
try:
|
||||||
|
self.remove_torrent_files(torrent)
|
||||||
|
except ValueError:
|
||||||
|
logger.debug(f"Torrent {torrent.name} has already been removed from torrent files.")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue