commit ef21adc1322b5d35f66eddde4f63dd9197456eef Author: jblu Date: Thu Jul 14 19:03:06 2022 -0500 copied from private repo diff --git a/README.md b/README.md new file mode 100644 index 0000000..04dfa37 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# qbit-maid + +The objective is to filter torrents based on the following criteria: +- tracker domain name +- age +- ratio +- state + +The first file shall contain an client to the qbit api and the processing of the torrents. +qbit-clean.py + +The second file shall contain functions to build out a list of torrents. +qlist.py + +The third file shall contain logging and email communication. +qlogging.py + +The fourth file shall be logic to process torrents. +qprocess.py \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..fdb8198 --- /dev/null +++ b/config.json.example @@ -0,0 +1,9 @@ +{ + "host": "192.168.1.1", + "port": 8080, + "username": "admin", + "password": "admin", + "loglevel": "INFO", + "logpath": "./qc.log", + "age": 2630000 +} \ No newline at end of file diff --git a/qbit-maid.py b/qbit-maid.py new file mode 100644 index 0000000..cf1a5bb --- /dev/null +++ b/qbit-maid.py @@ -0,0 +1,52 @@ +#The first file shall contain an client to the qbit api and the processing of the torrents. +import qbittorrentapi +from json import load +from qlist import * +from qlogging import * +from qprocess import * +import time +import logging + +class Qbt: + def __init__(self): + """Main object, should be calling functions from qlist.py, qlogging.py and qprocess.py""" + # Open the config. Needs a json file with the data in config.json.example + c = open('./config.json') + self.config = load(c) + # Create the api object + self.qbt_client = qbittorrentapi.Client( + host=self.config["host"], + port=self.config["port"], + username=self.config["username"], + password=self.config["password"], + ) + # Create the logging object + self.tl = logging + # Variables torlog uses from config.json + self.logpath=self.config["logpath"] + self.loglevel=self.config["loglevel"] + torlog(self) + self.t = time + f = open('./tracker-whitelist.json') + self.tracker_whitelist = load(f) + self.tracker_protected_list = [] + self.tracker_nonprotected_list = [] + self.tracker_protected_tag = 'ipt' + self.tracker_non_protected_tag = 'public' + self.torrent_hash_delete_list = [] + self.tl.debug(self.tracker_whitelist) + try: + self.tl.info('Connecting to host.') + self.qbt_client.auth_log_in() + self.tl.info('Connected.') + except qbittorrentapi.LoginFailed as e: + self.tl.exception(e) + self.torrentlist = {} + self.torrentlist = self.qbt_client.torrents_info() + buildtorlist(self) + torprocessor(self) + printprocessor(self) + #tordelete(self) + +if __name__== "__main__": + Qbt() \ No newline at end of file diff --git a/qlist.py b/qlist.py new file mode 100644 index 0000000..ea5c7fa --- /dev/null +++ b/qlist.py @@ -0,0 +1,44 @@ +#The second file shall contain functions to build out a list of torrents. +def buildtorlist(self): + self.protected_count = 0 + self.nonprotected_count = 0 + while self.torrentlist: + torrent = self.torrentlist.pop() + self.tl.debug(torrent['tracker']) + if self.tracker_whitelist['iptorrents-empirehost'] in torrent['tracker']: + self.tl.debug(f'Protected torrent tracker: {torrent["tracker"]}hash: {torrent["hash"]}') + self.protected_count += 1 + self.qbt_client.torrents_add_tags(self.tracker_protected_tag,torrent['hash']) + self.tracker_protected_list.append(torrent) + elif self.tracker_whitelist["iptorrents-stackoverflow"] in torrent['tracker']: + self.tl.debug(f'Protected torrent tracker: {torrent["tracker"]}hash: {torrent["hash"]}') + self.protected_count += 1 + self.qbt_client.torrents_add_tags(self.tracker_protected_tag,torrent['hash']) + self.tracker_protected_list.append(torrent) + elif self.tracker_whitelist["iptorrents-bgp"] in torrent['tracker']: + self.tl.debug(f'Protected torrent tracker: {torrent["tracker"]}hash: {torrent["hash"]}') + self.protected_count += 1 + self.qbt_client.torrents_add_tags(self.tracker_protected_tag,torrent['hash']) + self.tracker_protected_list.append(torrent) + else: + self.tl.debug(f'Non-protected tracker: {torrent["tracker"]}hash: {torrent["hash"]}') + self.nonprotected_count += 1 + self.qbt_client.torrents_add_tags(self.tracker_non_protected_tag,torrent['hash']) + self.tracker_nonprotected_list.append(torrent) + +def writetor(self, filepath='./torrentinfo.txt'): + with open(filepath, 'w') as fp: + fp.write(str(self.torrentlist)) + +def listfirsttor(self, index=0): + torrent = self.torrentlist[index] + for k,v in torrent.items(): + self.tl.debug(f'{k}: {v}') + +def listqbitapiinfo(self): + self.tl.info(f'qBittorrent: {self.qbt_client.app.version}') + self.tl.info(f'qBittorrent Web API: {self.qbt_client.app.web_api_version}') + +def torrentcount(self): + self.tl.debug(f'torrents that are protected {self.protected_count}') + self.tl.debug(f"torrents that aren't protected {self.nonprotected_count}") \ No newline at end of file diff --git a/qlogging.py b/qlogging.py new file mode 100644 index 0000000..ad57e89 --- /dev/null +++ b/qlogging.py @@ -0,0 +1,14 @@ +#The third file shall contain logging and email communication. + +def torlog(self): + + if self.loglevel == 'DEBUG': + self.tl.basicConfig(filename=self.logpath, format='%(asctime)s:%(levelname)s:%(message)s', encoding='utf-8', datefmt='%m/%d/%Y %I:%M:%S %p',level=self.tl.DEBUG) + if self.loglevel == 'INFO': + self.tl.basicConfig(filename=self.logpath, format='%(asctime)s:%(levelname)s:%(message)s', encoding='utf-8', datefmt='%m/%d/%Y %I:%M:%S %p',level=self.tl.INFO) +def toremail(self): + pass + +def getunixtimestamp(self): + self.uts = self.t.time() + self.tl.info(self.uts) \ No newline at end of file diff --git a/qprocess.py b/qprocess.py new file mode 100644 index 0000000..84be045 --- /dev/null +++ b/qprocess.py @@ -0,0 +1,38 @@ +#The fourth file shall be logic to process torrents. +def torprocessor(self): + for canidate in self.tracker_nonprotected_list: + if 'ipt' in canidate['tags']: + self.tl.warning(f'{canidate["name"]} was in non-protected list.') + break + if canidate['state'] == 'downloading': + self.tl.info(f'{canidate["name"]} is still downloading and will be skipped.') + break + else: + self.torrent_hash_delete_list.append(canidate['infohash_v1']) + self.tl.info(f'Submitted {canidate["name"]} for deletion.') + for canidate in self.tracker_protected_list: + if canidate['state'] == 'downloading': + self.tl.warning(f'{canidate["name"]} is still downloading and will be skipped.') + break + if canidate['ratio'] < float(1.05): + self.tl.debug(f'{canidate["name"]} is below a 1.05 ratio({canidate["ratio"]})') + if canidate['added_on'] + self.config["age"] <= self.t.time(): + self.tl.debug(f'Calculation: {canidate["added_on"] + self.config["age"]}') + self.tl.debug(f'Comparison: {self.t.time()}') + self.torrent_hash_delete_list.append(canidate['infohash_v1']) + self.tl.info(f'Submitted {canidate["name"]} for deletion from the protected list.') + if canidate['ratio'] >= float(1.05): + self.tl.debug(f'{canidate["name"]} is above a 1.05 ratio({canidate["ratio"]}).') + self.torrent_hash_delete_list.append(canidate['infohash_v1']) + self.tl.info(f'Submitted {canidate["name"]} for deletion from the protected list.') + else: + pass + +def printprocessor(self): + self.tl.info(f'Protected torrents: {len(self.tracker_protected_list)}') + self.tl.info(f'Non-protected torrents: {len(self.tracker_nonprotected_list)}') + self.tl.info(f'Total torrents set for deletion: {len(self.torrent_hash_delete_list)}') + +def tordelete(self): + self.tl.debug(self.torrent_hash_delete_list) + self.qbt_client.torrents_delete(True, self.torrent_hash_delete_list) \ No newline at end of file diff --git a/tracker-whitelist.json b/tracker-whitelist.json new file mode 100644 index 0000000..516d89b --- /dev/null +++ b/tracker-whitelist.json @@ -0,0 +1,5 @@ +{ +"iptorrents-empirehost": "ssl.empirehost.me", +"iptorrents-stackoverflow": "localhost.stackoverflow.tech", +"iptorrents-bgp": "routing.bgp.technology" +} \ No newline at end of file