Merge pull request #105 from StuffAnThings/develop

3.2.1
This commit is contained in:
bobokun 2022-02-07 13:56:25 -05:00 committed by GitHub
commit 1a378ecafe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 109 additions and 24 deletions

View file

@ -1 +1 @@
3.2.0
3.2.1

View file

@ -200,7 +200,7 @@ class Config:
else:
self.cross_seed_dir = self.util.check_for_attribute(self.data, "cross_seed", parent="directory", default_is_none=True)
if self.recyclebin['enabled']:
if "recycle_bin" in self.data["directory"]:
if "recycle_bin" in self.data["directory"] and self.data["directory"]["recycle_bin"] is not None:
default_recycle = os.path.join(self.remote_dir, os.path.basename(self.data['directory']['recycle_bin'].rstrip('/')))
else:
default_recycle = os.path.join(self.remote_dir, '.RecycleBin')

View file

@ -14,7 +14,7 @@ class Qbt:
def __init__(self, config, params):
self.config = config
config_handler.set_global(bar=None, receipt_text=False)
config_handler.set_global(bar=None, receipt=False)
self.host = params["host"]
self.username = params["username"]
self.password = params["password"]
@ -114,7 +114,14 @@ class Qbt:
if x.url.startswith('http'):
status = x.status
msg = x.msg.upper()
exception = ["DOWN", "UNREACHABLE", "BAD GATEWAY", "TRACKER UNAVAILABLE"]
exception = [
"DOWN",
"DOWN.",
"UNREACHABLE",
"(UNREACHABLE)",
"BAD GATEWAY",
"TRACKER UNAVAILABLE"
]
if x.status == 2:
working_tracker = True
break
@ -142,7 +149,7 @@ class Qbt:
self.torrentinfo = None
self.torrentissue = None
self.torrentvalid = None
if config.args['recheck'] or config.args['cross_seed'] or config.args['rem_unregistered'] or config.args['tag_tracker_error']:
if config.args['recheck'] or config.args['cross_seed'] or config.args['rem_unregistered'] or config.args['tag_tracker_error'] or config.args['tag_nohardlinks']:
# Get an updated torrent dictionary information of the torrents
self.torrentinfo, self.torrentissue, self.torrentvalid = get_torrent_info(self.torrent_list)
@ -358,28 +365,39 @@ class Qbt:
# loop through torrent list again for cleanup purposes
if (nohardlinks[category]['cleanup']):
for torrent in torrent_list:
if torrent.name in tdel_dict.keys() and 'noHL' in torrent.tags:
t_name = torrent.name
if t_name in tdel_dict.keys() and 'noHL' 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, root_dir) == tdel_dict[torrent.name]:
if torrent['content_path'].replace(root_dir, root_dir) == tdel_dict[t_name]:
tracker = self.config.get_tags([x.url for x in torrent.trackers if x.url.startswith('http')])
body = []
body += print_line(util.insert_space(f'Torrent Name: {torrent.name}', 3), loglevel)
body += print_line(util.insert_space(f'Torrent Name: {t_name}', 3), loglevel)
body += print_line(util.insert_space(f'Tracker: {tracker["url"]}', 8), loglevel)
body += print_line(util.insert_space("Cleanup: True [No hard links found and meets Share Limits.]", 8), loglevel)
attr = {
"function": "cleanup_tag_nohardlinks",
"title": "Removing NoHL Torrents and meets Share Limits",
"torrent_name": torrent.name,
"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, root_dir))):
del_tor_cont += 1
attr["torrents_deleted_and_contents"] = True
if not dry_run: self.tor_delete_recycle(torrent, attr)
body += print_line(util.insert_space('Deleted .torrent AND content files.', 8), loglevel)
# 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 dry_run: self.tor_delete_recycle(torrent, attr)
body += print_line(util.insert_space('Deleted .torrent but NOT content files.', 8), loglevel)
else:
del_tor_cont += 1
attr["torrents_deleted_and_contents"] = True
if not dry_run: self.tor_delete_recycle(torrent, attr)
body += print_line(util.insert_space('Deleted .torrent AND content files.', 8), loglevel)
else:
del_tor += 1
attr["torrents_deleted_and_contents"] = False
@ -387,6 +405,7 @@ class Qbt:
body += print_line(util.insert_space('Deleted .torrent but NOT content files.', 8), loglevel)
attr["body"] = "\n".join(body)
self.config.send_notifications(attr)
self.torrentinfo[t_name]['count'] -= 1
if num_tags >= 1:
print_line(f"{'Did not Tag/set' if dry_run else 'Tag/set'} share limits for {num_tags} .torrent{'s.' if num_tags > 1 else '.'}", loglevel)
else:
@ -465,6 +484,7 @@ class Qbt:
del_tor_cont += 1
attr["body"] = "\n".join(body)
self.config.send_notifications(attr)
self.torrentinfo[t_name]['count'] -= 1
if cfg_rem_unregistered or cfg_tag_error:
if cfg_tag_error: separator("Tagging Torrents with Tracker Errors", space=False, border=False)
@ -486,7 +506,9 @@ class Qbt:
'MISSING PASSKEY',
'MISSING INFO_HASH',
'PASSKEY IS INVALID',
'INVALID PASSKEY'
'INVALID PASSKEY',
'EXPECTED VALUE (LIST, DICT, INT OR STRING) IN BENCODED STRING',
'COULD NOT PARSE BENCODED DATA'
]
for torrent in self.torrentvalid:
check_tags = util.get_list(torrent.tags)
@ -534,7 +556,7 @@ class Qbt:
if 'tracker.beyond-hd.me' in tracker['url'] and self.config.BeyondHD is not None and not list_in_text(msg_up, ignore_msgs):
json = {"info_hash": torrent.hash}
response = self.config.BeyondHD.search(json)
if response['total_results'] <= 1:
if response['total_results'] == 0:
del_unregistered()
break
tag_tracker_error()
@ -866,12 +888,12 @@ class Qbt:
dest = os.path.join(recycle_path, file.replace(self.config.remote_dir, ''))
# Move files and change date modified
try:
util.move_files(src, dest, True)
toDelete = util.move_files(src, dest, True)
except FileNotFoundError:
e = print_line(f'RecycleBin Warning - FileNotFound: No such file or directory: {src} ', 'WARNING')
self.config.notify(e, 'Deleting Torrent', False)
# Delete torrent and files
torrent.delete(delete_files=False)
torrent.delete(delete_files=toDelete)
# Remove any empty directories
util.remove_empty_directories(save_path, "**/*")
else:

View file

@ -59,7 +59,7 @@ class check:
else:
text = f"{parent} sub-attribute {attribute}"
if data is None or attribute not in data:
if data is None or attribute not in data or (attribute in data and data[attribute] is None and not default_is_none):
message = f"{text} not found"
if parent and save is True:
loaded_config, _, _ = yaml.util.load_yaml_guess_indent(open(self.config.config_path))
@ -77,7 +77,7 @@ class check:
endline = f"\n{parent} sub-attribute {attribute} added to config"
if parent not in loaded_config or not loaded_config[parent]:
loaded_config[parent] = {attribute: default}
elif attribute not in loaded_config[parent]:
elif attribute not in loaded_config[parent] or (attribute in loaded_config[parent] and loaded_config[parent][attribute] is None):
loaded_config[parent][attribute] = default
else:
endline = ""
@ -306,16 +306,22 @@ def trunc_val(s, d, n=3):
# Move files from source to destination, mod variable is to change the date modified of the file being moved
def move_files(src, dest, mod=False):
dest_path = os.path.dirname(dest)
toDelete = False
if os.path.isdir(dest_path) is False:
os.makedirs(dest_path)
try:
if mod is True:
modTime = time.time()
os.utime(src, (modTime, modTime))
shutil.move(src, dest)
except PermissionError as p:
logger.warning(f"{p} : Copying files instead.")
shutil.copyfile(src, dest)
toDelete = True
except Exception as e:
print_stacktrace()
logger.error(e)
if mod is True:
modTime = time.time()
os.utime(dest, (modTime, modTime))
return toDelete
# Copy Files from source to destination

View file

@ -98,7 +98,7 @@ class Webhooks:
"run_time": run_time,
"torrents_added": stats["added"],
"torrents_deleted": stats["deleted"],
"torrents_deleted_and_contents": stats["deleted_contents"],
"torrents_deleted_and_contents_count": stats["deleted_contents"],
"torrents_resumed": stats["resumed"],
"torrents_rechecked": stats["rechecked"],
"torrents_categorized": stats["categorized"],

View file

@ -2,5 +2,5 @@ ruamel.yaml==0.17.20
qbittorrent-api>=2022.1.27
schedule==1.1.0
retrying==1.3.3
alive_progress==2.1.0
alive_progress==2.2.0
requests==2.27.1

57
scripts/mover.py Normal file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env python3
# This standalone script is used to pause torrents older than last x days, run mover (in Unraid) and start torrents again once completed
import os, sys, time
from datetime import datetime, timedelta
# --DEFINE VARIABLES--#
# Set Number of Days to stop torrents for the move
days = 2
qbt_host = 'qbittorrent:8080'
qbt_user = None
qbt_pass = None
# --DEFINE VARIABLES--#
# --START SCRIPT--#
try:
from qbittorrentapi import Client, LoginFailed, APIConnectionError
except ModuleNotFoundError:
print("Requirements Error: qbittorrentapi not installed. Please install with pip")
sys.exit(0)
current = datetime.now()
timeoffset = (current - timedelta(days=days)).timestamp()
def stop_start_torrents(torrent_list, pause=True):
for torrent in torrent_list:
if (torrent.added_on >= timeoffset):
if pause:
torrent.pause()
else:
torrent.resume()
else:
break
if __name__ == '__main__':
try:
client = Client(host=qbt_host, username=qbt_user, password=qbt_pass)
except LoginFailed:
raise("Qbittorrent Error: Failed to login. Invalid username/password.")
except APIConnectionError:
raise("Qbittorrent Error: Unable to connect to the client.")
except Exception:
raise("Qbittorrent Error: Unable to connect to the client.")
torrent_list = client.torrents.info(sort='added_on', reverse=True)
# Pause Torrents
print(f"Pausing torrents from the last {days} Days")
stop_start_torrents(torrent_list, True)
time.sleep(10)
# Start mover
print("Starting Mover")
os.system('/usr/local/sbin/mover.old start')
# Start Torrents
print(f"Resuming paused torrents from the last {days} Days")
stop_start_torrents(torrent_list, False)