diff --git a/README.md b/README.md index a5b2b11..41936ef 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ To run the script in an interactive terminal run: * Add the `tag` definition based on tracker URL * 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. * To run the script in an interactive terminal with a list of possible commands run: ```bash diff --git a/config.yml.sample b/config.yml.sample index 7891bbc..3253947 100644 --- a/config.yml.sample +++ b/config.yml.sample @@ -75,4 +75,13 @@ recyclebin: # 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 \ No newline at end of file + 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 9a726e4..888d6a6 100644 --- a/config/config.yml.sample +++ b/config/config.yml.sample @@ -8,7 +8,7 @@ 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, noHL, and RecycleBin. - # remote_dir var: # Path of docker host mapping of root_dir. + # remote_dir var: # Path of docker host mapping of root_dir. # Must be set if you're running qbit_manage locally and qBittorrent/cross_seed is in a docker cross_seed: "/your/path/here/" root_dir: "/data/torrents/" @@ -74,7 +74,16 @@ nohardlinks: #By default the Recycle Bin will be emptied on every run of the qbit_manage script if empty_after_x_days is defined. recyclebin: enabled: true - # empty_after_x_days var: Will automatically remove all files and folders in recycle bin after x days. (Checks every script run) + # empty_after_x_days var: Will automatically remove all files and folders in recycle bin after x days. (Checks every script run) # If this variable is not defined it, the RecycleBin will never be emptied. # WARNING: Setting this variable to 0 will delete all files immediately upon script run! - empty_after_x_days: 60 \ No newline at end of file + 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/qbit_manage.py b/qbit_manage.py index 169b58b..1fb4462 100644 --- a/qbit_manage.py +++ b/qbit_manage.py @@ -13,6 +13,7 @@ import datetime import time import stat import sys +import fnmatch try: from qbittorrentapi import Client @@ -41,7 +42,6 @@ parser.add_argument('-tnhl', '--tag-nohardlinks', dest='tag_nohardlinks', action parser.add_argument('-sr', '--skip-recycle', dest='skip_recycle', action="store_true", default=False, help='Use this to skip emptying the Reycle Bin folder.') parser.add_argument('-dr', '--dry-run', dest='dry_run', action="store_true", default=False, help='If you would like to see what is gonna happen but not actually move/delete or tag/categorize anything.') parser.add_argument('-ll', '--log-level', dest='log_level', action="store", default='INFO', type=str, help='Change your log level.') - args = parser.parse_args() @@ -304,7 +304,7 @@ def set_recheck(): f' --Ratio vs Max Ratio: {torrent.ratio} < {torrent.max_ratio}\n' f' --Seeding Time vs Max Seed Time: {datetime.timedelta(seconds=torrent.seeding_time)} < {datetime.timedelta(minutes=torrent.max_seeding_time)}') if torrent.ratio < torrent.max_ratio and (torrent.seeding_time < (torrent.max_seeding_time * 60)): - if dry_run: + if dry_run: logger.dryrun(f'\n - Not Resuming {new_tag} - {torrent.name}') else: logger.info(f'\n - Resuming {new_tag} - {torrent.name}') @@ -491,7 +491,7 @@ def set_rem_unregistered(): 'PACKS' in msg_up or \ 'REPACKED' in msg_up or \ 'PACK' in msg_up or \ - 'TRUMP' in msg_up + 'TRUMP' in msg_up ) and x.status == 4 and 'DOWN' not in msg_up and 'UNREACHABLE' not in msg_up: logger.debug(f'Torrent counts: {t_count}') logger.debug(f'msg: {t_msg}') @@ -537,6 +537,7 @@ def set_rem_unregistered(): if (len(pot_unr) > 0): logger.debug(f'Potential Unregistered torrents: {pot_unr}') + def set_rem_orphaned(): if rem_orphaned: torrent_files = [] @@ -551,13 +552,23 @@ def set_rem_orphaned(): for torrent in torrent_list: for file in torrent.files: torrent_files.append(os.path.join(torrent.save_path,file.name)) - + orphaned_files = set(root_files) - set(torrent_files) orphaned_files = sorted(orphaned_files) + + excluded_orphan_files = [] + if 'orphaned' in cfg and cfg["orphaned"] is not None and 'exclude_patterns' in cfg['orphaned'] and cfg['orphaned']['exclude_patterns'] != '': + exclude_patterns = cfg['orphaned']['exclude_patterns'] + excluded_orphan_files = [file for file in orphaned_files for exclude_pattern in exclude_patterns if fnmatch.fnmatch(file, exclude_pattern)] + + orphaned_files = set(orphaned_files) - set(excluded_orphan_files) + logger.debug('----------torrent files-----------') logger.debug("\n".join(torrent_files)) logger.debug('----------root_files-----------') logger.debug("\n".join(root_files)) + logger.debug('----------excluded_orphan_files-----------') + logger.debug("\n".join(excluded_orphan_files)) logger.debug('----------orphaned_files-----------') logger.debug("\n".join(orphaned_files)) logger.debug('----------Deleting orphan files-----------') @@ -724,7 +735,7 @@ def nohardlink(file): def tor_delete_recycle(torrent): if 'recyclebin' in cfg and cfg["recyclebin"] != None: if 'enabled' in cfg["recyclebin"] and cfg["recyclebin"]['enabled']: - tor_files = [] + tor_files = [] #Define torrent files/folders for file in torrent.files: tor_files.append(os.path.join(torrent.save_path,file.name)) @@ -807,7 +818,7 @@ def set_empty_recycle(): return -#Define global parameters +#Define global parameters torrent_list = None torrentdict = None def start():