diff --git a/.dockerignore b/.dockerignore index 281f674..c3dcf59 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,5 +17,6 @@ config Dockerfile venv .idea +.env test.py !config/config.yml.sample \ No newline at end of file diff --git a/.gitignore b/.gitignore index b92a666..1057d88 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ __pycache__/ *.log* *.yml .vscode/* -!.github/** \ No newline at end of file +!.github/** +*.svg +.env \ No newline at end of file diff --git a/README.md b/README.md index 560823e..bd9c976 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# qBit Management +# qBit Management [![GitHub release (latest by date)](https://img.shields.io/github/v/release/StuffAnThings/qbit_manage?style=plastic)](https://github.com/StuffAnThings/qbit_manage/releases) [![GitHub commits since latest release (by SemVer)](https://img.shields.io/github/commits-since/StuffAnThings/qbit_manage/latest/develop?label=Commits%20in%20Develop&style=plastic)](https://github.com/StuffAnThings/qbit_manage/tree/develop) @@ -9,10 +9,10 @@ This is a program used to manage your qBittorrent instance such as: -* Tag torrents based on tracker URL (only tag torrents that have no tags) +* Tag torrents based on tracker URL and set seed goals/limit upload speed by tag (only tag torrents that have no tags) * Update categories based on save directory * Remove unregistered torrents (delete data & torrent if it is not being cross-seeded, otherwise it will just remove the torrent) -* Automatically add [cross-seed](https://github.com/mmgoodnow/cross-seed) torrents in paused state (used in conjunction with the [cross-seed](https://github.com/mmgoodnow/cross-seed) script) <-- cross-seed now allows for torrent injections directly to qBit. +* Automatically add [cross-seed](https://github.com/mmgoodnow/cross-seed) torrents in paused state. **\*Note: cross-seed now allows for torrent injections directly to qBit, making this feature obsolete.\*** * Recheck paused torrents sorted by lowest size and resume if completed * Remove orphaned files from your root directory that are not referenced by qBittorrent * Tag any torrents that have no hard links and allows optional cleanup to delete these torrents and contents based on maximum ratio and/or time seeded @@ -30,7 +30,7 @@ To run the script in an interactive terminal run: * add your qBittorrent host, user and pass. If you are not using a username and password you can remove the `user` and `pass` lines. * add your `cross_seed` and `root_dir`. If you're running cross-seed in a docker container you must fill out `remote_dir` as well. * Add your categories and save path to match with what is being used in your qBittorrent instance. I suggest using the full path when defining `save_path` -* Add the `tag` definition based on tracker URL +* Add the `tag` definition based on tracker URL (optional add seed goals/limit upload speed by tag) * Modify the `nohardlinks` by specifying your completed movies/series category to match with qBittorrent. Please ensure the `root_dir` and/or `remote_dir` is added in the `directory` section * `root_dir` needs to be defined in order to use the RecycleBin function. If optional `empty_after_x_days` is not defined then it will never empty the RecycleBin. Setting it to 0 will empty the RecycleBin immediately. * Modify the `orphaned` section to define file patterns not to consider as orphans. Use this to exclude your incomplete torrents directory, or to ignore auto-generated files such as Thumbs.db. @@ -51,7 +51,7 @@ python qbit_manage.py -h | `-cs` or `--cross-seed` | QBT_CROSS_SEED | Use this after running [cross-seed script](https://github.com/mmgoodnow/cross-seed) to add torrents from the cross-seed output folder to qBittorrent | False | | `-re` or `--recheck` | QBT_RECHECK | Recheck paused torrents sorted by lowest size. Resume if Completed. | False | | `-cu` or `--cat-update` | QBT_CAT_UPDATE | Use this if you would like to update your categories. | False | -| `-tu` or `--tag-update` | QBT_TAG_UPDATE | Use this if you would like to update your tags. (Only adds tags to untagged torrents) | False | +| `-tu` or `--tag-update` | QBT_TAG_UPDATE | Use this if you would like to update your tags and/or set seed goals/limit upload speed by tag. (Only adds tags to untagged torrents) | False | | `-ru` or `--rem-unregistered` | QBT_REM_UNREGISTERED | Use this if you would like to remove unregistered torrents. (It will the delete data & torrent if it is not being cross-seeded, otherwise it will just remove the torrent without deleting data) | False | | `-ro` or `--rem-orphaned` | QBT_REM_ORPHANED | Use this if you would like to remove orphaned files from your `root_dir` directory that are not referenced by any torrents. It will scan your `root_dir` directory and compare it with what is in qBittorrent. Any data not referenced in qBittorrent will be moved into `/data/torrents/orphaned_data` folder for you to review/delete. | False | | `-tnhl` or `--tag-nohardlinks` | QBT_TAG_NOHARDLINKS | Use this to tag any torrents that do not have any hard links associated with any of the files. This is useful for those that use Sonarr/Radarr that hard links your media files with the torrents for seeding. When files get upgraded they no longer become linked with your media therefore will be tagged with a new tag noHL. You can then safely delete/remove these torrents to free up any extra space that is not being used by your media folder. | False | diff --git a/VERSION b/VERSION index 42f7d23..6161878 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1 \ No newline at end of file +2.2 \ No newline at end of file diff --git a/config.yml.sample b/config.yml.sample deleted file mode 100644 index 3253947..0000000 --- a/config.yml.sample +++ /dev/null @@ -1,87 +0,0 @@ -# qBittorrent parameters -qbt: - host: "localhost:8080" - user: "username" - pass: "password" - -directory: - # Do not remove these - # Cross-seed var: #Output directory of cross-seed - # root_dir var: #Root downloads directory used to check for orphaned files and used in RecycleBin - # remote_dir var: # Path of docker host mapping of root_dir. Must be set if you are using docker! - cross_seed: "/your/path/here/" - root_dir: "/data/torrents/" - remote_dir: "/mnt/user/data/torrents/" - -# Category/Pathing Parameters -cat: - # : #Path of your save directory. Can be a keyword or full path - movies: "/data/torrents/Movies" - tv: "TV" - -# Tag Parameters -tags: - # : - animebytes.tv: AnimeBytes - avistaz: Avistaz - beyond-hd: Beyond-HD - blutopia: Blutopia - cartoonchaos: CartoonChaos - digitalcore: DigitalCore - gazellegames: GGn - hdts: HDTorrents - landof.tv: BroadcasTheNet - myanonamouse: MaM - passthepopcorn: PassThePopcorn - privatehd: PrivateHD - tleechreload: TorrentLeech - torrentdb: TorrentDB - torrentleech: TorrentLeech - tv-vault: TV-Vault - -#Tag Movies/Series that are not hard linked -nohardlinks: - # Mandatory to fill out directory parameter above to use this function (root_dir/remote_dir) - # This variable should be set to your category name of your completed movies/completed series in qbit. Acceptable variable can be any category you would like to tag if there are no hardlinks found - movies-completed: - # exclude_tags var: Will exclude the following tags when searching through the category. - exclude_tags: - - Beyond-HD - - AnimeBytes - - MaM - # cleanup var: WARNING!! Setting this as true Will remove and delete contents of any torrents that are in paused state and has the NoHL tag - cleanup: false - # max_ratio var: Will set the torrent Maximum share ratio until torrent is stopped from seeding/uploading - max_ratio: 4.0 - # seeding time var: Will set the torrent Maximum seeding time (min) until torrent is stopped from seeding - max_seeding_time: 86400 - - #Can have additional categories set with separate ratio/seeding times defined. - series-completed: - # exclude_tags var: Will exclude the following tags when searching through the category. - exclude_tags: - - Beyond-HD - - BroadcasTheNet - # cleanup var: WARNING!! Setting this as true Will remove and delete contents of any torrents that are in paused state and has the NoHL tag - cleanup: false - # max_ratio var: Will set the torrent Maximum share ratio until torrent is stopped from seeding/uploading - max_ratio: 4.0 - # seeding time var: Will set the torrent Maximum seeding time (min) until torrent is stopped from seeding - max_seeding_time: 86400 - -#Recycle Bin method of deletion will move files into the recycle bin instead of directly deleting them in qbit -recyclebin: - enabled: true - # empty_after_x_days var: Will automatically remove all files and folders in recycle bin after x days. - # If this variable is not defined it, the RecycleBin will never be emptied. - # Setting this variable to 0 will delete files immediately. - empty_after_x_days: 60 - -# Orphaned files are those in the root_dir download directory that are not referenced by any active torrents. -orphaned: - # File patterns that will not be considered orphaned files. Handy for generated files that aren't part of the torrent but belong with the torrent's files - exclude_patterns: - - "**/.DS_Store" - - "**/Thumbs.db" - - "**/@eaDir" - - "/data/torrents/temp/**" \ No newline at end of file diff --git a/config/config.yml.sample b/config/config.yml.sample index 888d6a6..449cbf0 100644 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -20,17 +20,25 @@ cat: movies: "/data/torrents/Movies" tv: "TV" -# Tag Parameters +# Tag Parameters (Use either Format 1 or Format 2) tags: + # Format 1 # : + # Format 2 + # : + # tag: + # Will set the torrent Maximum share ratio until torrent is stopped from seeding/uploading. -2 means the global limit should be used, -1 means no limit. + # max_ratio: 5.0 + # Will set the torrent Maximum seeding time (min) until torrent is stopped from seeding. -2 means the global limit should be used, -1 means no limit. + # max_seeding_time: 129600 + # Will limit the upload speed KiB/s (KiloBytes/second) (-1 sets the limit to infinity) + # limit_upload_speed: 150 + + #Format 1 Examples animebytes.tv: AnimeBytes - avistaz: Avistaz beyond-hd: Beyond-HD blutopia: Blutopia cartoonchaos: CartoonChaos - digitalcore: DigitalCore - gazellegames: GGn - hdts: HDTorrents landof.tv: BroadcasTheNet myanonamouse: MaM passthepopcorn: PassThePopcorn @@ -39,6 +47,22 @@ tags: torrentdb: TorrentDB torrentleech: TorrentLeech tv-vault: TV-Vault + + #Format 2 Examples + avistaz: + tag: Avistaz + max_ratio: 5.0 + max_seeding_time: 129600 + limit_upload_speed: 150 + digitalcore + tag: DigitalCore + max_ratio: 5.0 + gazellegames: + tag: GGn + limit_upload_speed: 150 + hdts: + tag: HDTorrents + max_seeding_time: 129600 #Tag Movies/Series that are not hard linked nohardlinks: diff --git a/qbit_manage.py b/qbit_manage.py index e01617f..80760f3 100644 --- a/qbit_manage.py +++ b/qbit_manage.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -import argparse, logging, os, sys, time, shutil, urllib3, stat, fnmatch +import argparse, logging, os, sys, time, shutil, stat, fnmatch from logging.handlers import RotatingFileHandler from datetime import timedelta,datetime from collections import Counter @@ -8,7 +8,7 @@ from pathlib import Path try: import yaml, schedule - from qbittorrentapi import Client + from qbittorrentapi import Client, LoginFailed, APIConnectionError from modules.docker import GracefulKiller from modules import util except ModuleNotFoundError: @@ -28,7 +28,7 @@ parser.add_argument('-lf', '--log-file', dest='logfile', action='store',default= parser.add_argument('-cs', '--cross-seed', dest='cross_seed', action="store_true", default=False, help='Use this after running cross-seed script to add torrents from the cross-seed output folder to qBittorrent') parser.add_argument('-re', '--recheck', dest='recheck', action="store_true", default=False, help='Recheck paused torrents sorted by lowest size. Resume if Completed.') parser.add_argument('-cu', '--cat-update', dest='cat_update', action="store_true", default=False, help='Use this if you would like to update your categories.') -parser.add_argument('-tu', '--tag-update', dest='tag_update', action="store_true", default=False, help='Use this if you would like to update your tags. (Only adds tags to untagged torrents)') +parser.add_argument('-tu', '--tag-update', dest='tag_update', action="store_true", default=False, help='Use this if you would like to update your tags and/or set seed goals/limit upload speed by tag. (Only adds tags to untagged torrents)') parser.add_argument('-ru', '--rem-unregistered', dest='rem_unregistered', action="store_true", default=False, help='Use this if you would like to remove unregistered torrents.') parser.add_argument('-ro', '--rem-orphaned', dest='rem_orphaned', action="store_true", default=False, help='Use this if you would like to remove unregistered torrents.') parser.add_argument('-tnhl', '--tag-nohardlinks', dest='tag_nohardlinks', action="store_true", default=False, help='Use this to tag any torrents that do not have any hard links associated with any of the files. This is useful for those that use Sonarr/Radarr which hard link your media files with the torrents for seeding. When files get upgraded they no longer become linked with your media therefore will be tagged with a new tag noHL. You can then safely delete/remove these torrents to free up any extra space that is not being used by your media folder.') @@ -132,8 +132,6 @@ else: os.makedirs(os.path.join(default_dir, "logs"), exist_ok=True) -urllib3.disable_warnings() - logger = logging.getLogger('qBit Manage') logging.DRYRUN = 25 @@ -180,10 +178,12 @@ if 'pass' in cfg['qbt']: else: password = '' -client = Client(host=host, - username=username, - password=password) - +client = Client(host=host, username=username, password=password) +try: + client.auth_log_in() +except (LoginFailed,APIConnectionError)as e: + logger.error(e) + sys.exit(0) ############FUNCTIONS############## #truncate the value of the torrent url to remove sensitive information @@ -203,24 +203,42 @@ def get_category(path): category = '' return category category = '' - logger.warning('No categories matched. Check your config.yml file. - Setting category to NULL') + logger.warning(f'No categories matched for the save path {path}. Check your config.yml file. - Setting category to NULL') return category #Get tags from config file based on keyword def get_tags(urls): - if 'tags' in cfg and cfg["tags"] != None: - tag_path = cfg['tags'] - for i, f in tag_path.items(): + new_tag = '' + max_ratio = '' + max_seeding_time = '' + limit_upload_speed = '' + url = trunc_val(urls[0], '/') + if 'tags' in cfg and cfg["tags"] != None and urls: + tag_values = cfg['tags'] + for tag_url, tag_details in tag_values.items(): + new_tag = '' + max_ratio = '' + max_seeding_time = '' + limit_upload_speed = '' + # If using Format 1 + if(type(tag_details) == str): + new_tag = tag_details + # Using Format 2 + else: + if 'tag' in tag_details: + new_tag = tag_details['tag'] + else: + logger.warning(f'No tags defined for {tag_url}. Please check your config.yml file.') + if 'max_ratio' in tag_details: max_ratio = tag_details['max_ratio'] + if 'max_seeding_time' in tag_details: max_seeding_time = tag_details['max_seeding_time'] + if 'limit_upload_speed' in tag_details: limit_upload_speed = tag_details['limit_upload_speed'] for url in urls: - if i in url: - tag = f - if tag: return tag,trunc_val(url, '/') + if tag_url in url: + return (new_tag,trunc_val(url, '/'),max_ratio,max_seeding_time,limit_upload_speed) else: - tag = ('','') - return tag - tag = ('','') - logger.warning('No tags matched. Check your config.yml file. Setting tag to NULL') - return tag + return (new_tag,url,max_ratio,max_seeding_time,limit_upload_speed) + logger.warning(f'No tags matched for {url}. Please check your config.yml file. Setting tag to NULL') + return (new_tag,url,max_ratio,max_seeding_time,limit_upload_speed) #Move files from source to destination, mod variable is to change the date modified of the file being moved @@ -266,6 +284,8 @@ def get_torrent_info(t_list): save_path = torrent.save_path category = get_category(save_path) is_complete = False + msg = None + status = None if torrent.name in torrentdict: t_count = torrentdict[torrent.name]['count'] + 1 msg_list = torrentdict[torrent.name]['msg'] @@ -278,9 +298,12 @@ def get_torrent_info(t_list): status_list = [] is_complete = torrent.state_enum.is_complete first_hash = torrent.hash - msg,status = [(x.msg,x.status) for x in torrent.trackers if x.url.startswith('http')][0] - msg_list.append(msg) - status_list.append(status) + try: + msg,status = [(x.msg,x.status) for x in torrent.trackers if x.url.startswith('http')][0] + except IndexError: + pass + if msg != None: msg_list.append(msg) + if status != None: status_list.append(status) torrentattr = {'Category': category, 'save_path': save_path, 'count': t_count, 'msg': msg_list, 'status': status_list, 'is_complete': is_complete, 'first_hash':first_hash} torrentdict[torrent.name] = torrentattr return torrentdict @@ -293,8 +316,7 @@ def set_recheck(): torrent_sorted_list = client.torrents.info(status_filter='paused',sort='size') if torrent_sorted_list: for torrent in torrent_sorted_list: - new_tag,t_url = get_tags([x.url for x in torrent.trackers if x.url.startswith('http')]) - if torrent.tags == '' or ('cross-seed' in torrent.tags and len([e for e in torrent.tags.split(",") if not 'noHL' in e]) == 1): torrent.add_tags(tags=new_tag) + new_tag = get_tags([x.url for x in torrent.trackers if x.url.startswith('http')])[0] #Resume torrent if completed if torrent.progress == 1: #Check to see if torrent meets AutoTorrentManagement criteria @@ -304,16 +326,16 @@ def set_recheck(): logger.debug(util.insert_space(f'-- Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} < {timedelta(minutes=torrent.max_seeding_time)}',4)) if torrent.ratio < torrent.max_ratio and (torrent.seeding_time < (torrent.max_seeding_time * 60)): if dry_run: - logger.dryrun(f'Not Resuming {new_tag} - {torrent.name}') + logger.dryrun(f'Not Resuming [{new_tag}] - {torrent.name}') else: - logger.info(f'Resuming {new_tag} - {torrent.name}') + logger.info(f'Resuming [{new_tag}] - {torrent.name}') torrent.resume() #Recheck elif torrent.progress == 0 and torrentdict[torrent.name]['is_complete'] and not torrent.state_enum.is_checking: if dry_run: - logger.dryrun(f'Not Rechecking {new_tag} - {torrent.name}') + logger.dryrun(f'Not Rechecking [{new_tag}] - {torrent.name}') else: - logger.info(f'Rechecking {new_tag} - {torrent.name}') + logger.info(f'Rechecking [{new_tag}] - {torrent.name}') torrent.recheck() # Function used to move any torrents from the cross seed directory to the correct save directory @@ -406,21 +428,22 @@ def set_category(): num_cat = 0 for torrent in torrent_list: if torrent.category == '': - for x in torrent.trackers: - if x.url.startswith('http'): - t_url = trunc_val(x.url, '/') - new_cat = get_category(torrent.save_path) - if dry_run: - logger.dryrun(util.insert_space(f'Torrent Name: {torrent.name}',3)) - logger.dryrun(util.insert_space(f'New Category: {new_cat}',3)) - logger.dryrun(util.insert_space(f'Tracker: {t_url}',8)) - num_cat += 1 - else: - logger.info(util.insert_space(f'- Torrent Name: {torrent.name}',1)) - logger.info(util.insert_space(f'-- New Category: {new_cat}',5)) - logger.info(util.insert_space(f'-- Tracker: {t_url}',5)) - torrent.set_category(category=new_cat) - num_cat += 1 + new_cat = get_category(torrent.save_path) + try: + t_url = [trunc_val(x.url, '/') for x in torrent.trackers if x.url.startswith('http')][0] + except IndexError: + t_url = None + if dry_run: + logger.dryrun(util.insert_space(f'Torrent Name: {torrent.name}',3)) + logger.dryrun(util.insert_space(f'New Category: {new_cat}',3)) + logger.dryrun(util.insert_space(f'Tracker: {t_url}',8)) + num_cat += 1 + else: + logger.info(util.insert_space(f'- Torrent Name: {torrent.name}',1)) + logger.info(util.insert_space(f'-- New Category: {new_cat}',5)) + logger.info(util.insert_space(f'-- Tracker: {t_url}',5)) + torrent.set_category(category=new_cat) + num_cat += 1 if dry_run: if num_cat >= 1: logger.dryrun(f'Did not update {num_cat} new categories.') @@ -439,18 +462,84 @@ def set_tags(): num_tags = 0 for torrent in torrent_list: if torrent.tags == '' or ('cross-seed' in torrent.tags and len([e for e in torrent.tags.split(",") if not 'noHL' in e]) == 1): - new_tag,t_url = get_tags([x.url for x in torrent.trackers if x.url.startswith('http')]) - if dry_run: - logger.dryrun(util.insert_space(f'Torrent Name: {torrent.name}',3)) - logger.dryrun(util.insert_space(f'New Tag: {new_tag}',8)) - logger.dryrun(util.insert_space(f'Tracker: {t_url}',8)) - num_tags += 1 - else: - logger.info(util.insert_space(f'Torrent Name: {torrent.name}',3)) - logger.info(util.insert_space(f'New Tag: {new_tag}',8)) - logger.info(util.insert_space(f'Tracker: {t_url}',8)) - torrent.add_tags(tags=new_tag) - num_tags += 1 + new_tag,url,max_ratio,max_seeding_time,limit_upload_speed = get_tags([x.url for x in torrent.trackers if x.url.startswith('http')]) + if new_tag: + if dry_run: + num_tags += 1 + logger.dryrun(util.insert_space(f'Torrent Name: {torrent.name}',3)) + logger.dryrun(util.insert_space(f'New Tag: {new_tag}',8)) + logger.dryrun(util.insert_space(f'Tracker: {url}',8)) + if limit_upload_speed: + if limit_upload_speed == -1: + logger.dryrun(util.insert_space(f'Limit UL Speed: Infinity',1)) + else: + logger.dryrun(util.insert_space(f'Limit UL Speed: {limit_upload_speed} kB/s',1)) + if max_ratio: + if max_ratio == -2: + logger.dryrun(util.insert_space(f'Share Limit: Use Global Share Limit',4)) + continue + elif max_ratio == -1: + logger.dryrun(util.insert_space(f'Share Limit: Set No Share Limit',4)) + continue + else: + max_ratio = torrent.max_ratio + if max_seeding_time: + if max_seeding_time == -2: + logger.dryrun(util.insert_space(f'Share Limit: Use Global Share Limit',4)) + continue + elif max_seeding_time == -1: + logger.dryrun(util.insert_space(f'Share Limit: Set No Share Limit',4)) + continue + else: + max_seeding_time = torrent.max_seeding_time + if max_ratio != torrent.max_ratio and max_seeding_time != torrent.max_seeding_time: + logger.dryrun(util.insert_space(f'Share Limit: Max Ratio = {max_ratio}, Max Seed Time = {max_seeding_time} min',4)) + elif max_ratio != torrent.max_ratio: + logger.dryrun(util.insert_space(f'Share Limit: Max Ratio = {max_ratio}',4)) + elif max_seeding_time != torrent.max_seeding_time: + logger.dryrun(util.insert_space(f'Share Limit: Max Seed Time = {max_seeding_time} min',4)) + else: + torrent.add_tags(tags=new_tag) + num_tags += 1 + logger.info(util.insert_space(f'Torrent Name: {torrent.name}',3)) + logger.info(util.insert_space(f'New Tag: {new_tag}',8)) + logger.info(util.insert_space(f'Tracker: {url}',8)) + if limit_upload_speed: + if limit_upload_speed == -1: + logger.info(util.insert_space(f'Limit UL Speed: Infinity',1)) + else: + logger.info(util.insert_space(f'Limit UL Speed: {limit_upload_speed} kB/s',1)) + torrent.set_upload_limit(limit_upload_speed*1024) + if max_ratio: + if max_ratio == -2: + logger.info(util.insert_space(f'Share Limit: Use Global Share Limit',4)) + torrent.set_share_limits(-2,-2) + continue + elif max_ratio == -1: + logger.info(util.insert_space(f'Share Limit: Set No Share Limit',4)) + torrent.set_share_limits(-1,-1) + continue + else: + max_ratio = torrent.max_ratio + if max_seeding_time: + if max_seeding_time == -2: + logger.info(util.insert_space(f'Share Limit: Use Global Share Limit',4)) + torrent.set_share_limits(-2,-2) + continue + elif max_seeding_time == -1: + logger.info(util.insert_space(f'Share Limit: Set No Share Limit',4)) + torrent.set_share_limits(-1,-1) + continue + else: + max_seeding_time = torrent.max_seeding_time + if max_ratio != torrent.max_ratio and max_seeding_time != torrent.max_seeding_time: + logger.info(util.insert_space(f'Share Limit: Max Ratio = {max_ratio}, Max Seed Time = {max_seeding_time} min',4)) + elif max_ratio != torrent.max_ratio: + logger.info(util.insert_space(f'Share Limit: Max Ratio = {max_ratio}',4)) + elif max_seeding_time != torrent.max_seeding_time: + logger.info(util.insert_space(f'Share Limit: Max Seed Time = {max_seeding_time} min',4)) + torrent.set_share_limits(max_ratio,max_seeding_time) + if dry_run: if num_tags >= 1: logger.dryrun(f'Did not update {num_tags} new tags.') @@ -552,6 +641,7 @@ def set_rem_unregistered(): def set_rem_orphaned(): if rem_orphaned: util.separator(f"Checking for Orphaned Files", space=False, border=False) + global torrent_list torrent_files = [] root_files = [] orphaned_files = [] @@ -562,6 +652,9 @@ def set_rem_orphaned(): root_files = [os.path.join(path.replace(remote_path,root_path), name) for path, subdirs, files in os.walk(remote_path) for name in files if os.path.join(remote_path,'orphaned_data') not in path and os.path.join(remote_path,'.RecycleBin') not in path] else: root_files = [os.path.join(path, name) for path, subdirs, files in os.walk(root_path) for name in files if os.path.join(root_path,'orphaned_data') not in path and os.path.join(root_path,'.RecycleBin') not in path] + + #Get an updated list of torrents + torrent_list = client.torrents.info(sort='added_on') for torrent in torrent_list: for file in torrent.files: diff --git a/qbm_logo.png b/qbm_logo.png new file mode 100644 index 0000000..90739ab Binary files /dev/null and b/qbm_logo.png differ