mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2024-09-21 07:46:02 +08:00
Merge branch 'develop'
This commit is contained in:
commit
57b2a1b28b
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
*.log
|
||||
*.yml
|
||||
.vscode/settings.json
|
||||
|
|
|
@ -35,3 +35,24 @@ tags:
|
|||
hdts: HDTorrents
|
||||
tv-vault: TV-Vault
|
||||
cartoonchaos: CartoonChaos
|
||||
|
||||
#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:
|
||||
#<OPTIONAL> 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
|
||||
#<OPTIONAL> max_ratio var: Will set the torrent Maximum share ratio until torrent is stopped from seeding/uploading
|
||||
max_ratio: 4.0
|
||||
#<OPTIONAL> 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:
|
||||
#<OPTIONAL> 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
|
||||
#<OPTIONAL> max_ratio var: Will set the torrent Maximum share ratio until torrent is stopped from seeding/uploading
|
||||
max_ratio: 4.0
|
||||
#<OPTIONAL> seeding time var: Will set the torrent Maximum seeding time (min) until torrent is stopped from seeding
|
||||
max_seeding_time: 86400
|
145
qbit_manage.py
145
qbit_manage.py
|
@ -64,6 +64,13 @@ parser.add_argument('-ro', '--rem-orphaned',
|
|||
help='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 Qbitorrent. Any data not referenced in Qbitorrent will be moved into '
|
||||
' `/data/torrents/orphaned_data` folder for you to review/delete.')
|
||||
parser.add_argument('-tnhl', '--tag-nohardlinks',
|
||||
dest='tag_nohardlinks',
|
||||
action='store_const',
|
||||
const='tag_nohardlinks',
|
||||
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'
|
||||
'to 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.')
|
||||
parser.add_argument('--dry-run',
|
||||
dest='dry_run',
|
||||
action='store_const',
|
||||
|
@ -146,7 +153,7 @@ def get_tags(urls):
|
|||
if i in url:
|
||||
tag = f
|
||||
if tag: return tag,trunc_val(url, '/')
|
||||
tag = ''
|
||||
tag = ('','')
|
||||
logger.warning('No tags matched. Check your config.yml file. Setting tag to NULL')
|
||||
return tag
|
||||
|
||||
|
@ -154,13 +161,13 @@ def remove_empty_directories(pathlib_root_dir):
|
|||
# list all directories recursively and sort them by path,
|
||||
# longest first
|
||||
L = sorted(
|
||||
pathlib_root_dir.glob("**"),
|
||||
pathlib_root_dir.glob("*/*"),
|
||||
key=lambda p: len(str(p)),
|
||||
reverse=True,
|
||||
)
|
||||
for pdir in L:
|
||||
try:
|
||||
pdir.rmdir() # remove directory if empty
|
||||
pdir.rmdir() # remove directory if empty
|
||||
except OSError:
|
||||
continue # catch and continue if non-empty
|
||||
|
||||
|
@ -195,7 +202,7 @@ def recheck():
|
|||
torrentdict = get_torrent_info(client.torrents.info(sort='added_on',reverse=True))
|
||||
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 == '': torrent.add_tags(tags=new_tag)
|
||||
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)
|
||||
#Resume torrent if completed
|
||||
if torrent.progress == 1:
|
||||
if args.dry_run == 'dry_run':
|
||||
|
@ -316,7 +323,7 @@ def update_tags():
|
|||
num_tags = 0
|
||||
torrent_list = client.torrents.info(sort='added_on',reverse=True)
|
||||
for torrent in torrent_list:
|
||||
if torrent.tags == '':
|
||||
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 args.dry_run == 'dry_run':
|
||||
logger.dryrun(f'\n - Torrent Name: {torrent.name}'
|
||||
|
@ -464,6 +471,133 @@ def rem_orphaned():
|
|||
else:
|
||||
logger.info('No Orphaned Files found.')
|
||||
|
||||
def tag_nohardlinks():
|
||||
if args.tag_nohardlinks == 'tag_nohardlinks':
|
||||
nohardlinks = cfg['nohardlinks']
|
||||
|
||||
if 'root_dir' in cfg['directory']:
|
||||
root_path = os.path.join(cfg['directory']['root_dir'], '')
|
||||
else:
|
||||
logger.error('root_dir not defined in config.')
|
||||
return
|
||||
if 'remote_dir' in cfg['directory']:
|
||||
remote_path = os.path.join(cfg['directory']['remote_dir'], '')
|
||||
else:
|
||||
remote_path = root_path
|
||||
|
||||
for category in nohardlinks:
|
||||
t_count = 0 #counter for the number of torrents that has no hard links
|
||||
t_del = 0 #counter for the number of torrents that has no hard links and meets the criteria for ratio limit/seed limit for deletion
|
||||
t_del_cs = 0 #counter for the number of torrents that has no hard links and meets the criteria for ratio limit/seed limit for deletion including cross-seeds
|
||||
n_info = ''
|
||||
tdel_dict = {} #dictionary to track the torrent names and content path that meet the deletion criteria
|
||||
torrent_list = client.torrents.info(category=category,filter='completed')
|
||||
if len(torrent_list) == 0:
|
||||
logger.error('The category ('+category+') defined in config.yml inside the nohardlinks section does not match any category in qbittorrent. Please make sure the category defined in config matches with one in qbittorrent.')
|
||||
continue
|
||||
for torrent in torrent_list:
|
||||
if args.dry_run != 'dry_run':
|
||||
torrent.resume()
|
||||
#Checks for any hard links and not already tagged
|
||||
if (nohardlink(torrent['content_path'].replace(root_path,remote_path))):
|
||||
|
||||
#Will only tag new torrents that don't have noHL tag
|
||||
if('noHL' not in torrent.tags):
|
||||
t_count += 1
|
||||
n_info += (f'\n - Torrent Name: {torrent.name} has no hard links found.')
|
||||
n_info += (' Adding tags noHL.')
|
||||
if(nohardlinks[category] != None):
|
||||
#set the max seeding time for the torrent
|
||||
if ('max_seeding_time' in nohardlinks[category]):
|
||||
seeding_time_limit = nohardlinks[category]['max_seeding_time']
|
||||
n_info += (' \n Setting max seed time to ' + str(seeding_time_limit) + '.')
|
||||
else:
|
||||
seeding_time_limit = -2
|
||||
#set the max ratio for the torrent
|
||||
if ('max_ratio' in nohardlinks[category]):
|
||||
ratio_limit = nohardlinks[category]['max_ratio']
|
||||
n_info += (' \n Setting max ratio to ' + str(ratio_limit)+ '.')
|
||||
else:
|
||||
ratio_limit = -2
|
||||
else:
|
||||
seeding_time_limit = -2
|
||||
ratio_limit = -2
|
||||
if args.dry_run != 'dry_run':
|
||||
#set the tag for no hard links
|
||||
torrent.add_tags(tags='noHL')
|
||||
client.torrents_set_share_limits(ratio_limit,seeding_time_limit,torrent.hash)
|
||||
|
||||
#Cleans up previously tagged noHL torrents
|
||||
else:
|
||||
if(nohardlinks[category] != None):
|
||||
# Deletes torrent with data if cleanup is set to true and meets the ratio/seeding requirements
|
||||
if ('cleanup' in nohardlinks[category] and nohardlinks[category]['cleanup'] and torrent.state_enum.is_paused and len(nohardlinks[category])>0):
|
||||
t_del += 1
|
||||
n_info += (f'\n - Torrent Name: {torrent.name} has no hard links found and meets ratio/seeding requirements.')
|
||||
tdel_dict[torrent.name] = torrent['content_path'].replace(root_path,remote_path)
|
||||
if args.dry_run == 'dry_run':
|
||||
n_info += (' \n Cleanup flag set to true. NOT Deleting torrent + contents.')
|
||||
else:
|
||||
n_info += (' \n Cleanup flag set to true. Deleting torrent + contents.')
|
||||
|
||||
if(nohardlinks[category] != None):
|
||||
#loop through torrent list again for cleanup purposes
|
||||
if ('cleanup' in nohardlinks[category] and nohardlinks[category]['cleanup']):
|
||||
for torrent in torrent_list:
|
||||
if torrent.name in tdel_dict.keys() and 'noHL' in torrent.tags:
|
||||
#Double check that the content path is the same before we delete anything
|
||||
if torrent['content_path'].replace(root_path,remote_path) == tdel_dict[torrent.name]:
|
||||
t_del_cs += 1
|
||||
if args.dry_run != 'dry_run':
|
||||
if (os.path.exists(torrent['content_path'].replace(root_path,remote_path))):
|
||||
torrent.delete(hash=torrent.hash, delete_files=True)
|
||||
else:
|
||||
torrent.delete(hash=torrent.hash, delete_files=False)
|
||||
|
||||
|
||||
#Checks to see if previous noHL tagged torrents now have hard links.
|
||||
if (not (nohardlink(torrent['content_path'].replace(root_path,remote_path))) and ('noHL' in torrent.tags)):
|
||||
n_info += (f'\n - Previous Tagged noHL Torrent Name: {torrent.name} has hard links found now.')
|
||||
n_info += (' Removing tags noHL.')
|
||||
n_info += (' Removing ratio and seeding time limits.')
|
||||
if args.dry_run != 'dry_run':
|
||||
#Remove tags and share limits
|
||||
torrent.remove_tags(tags='noHL')
|
||||
client.torrents_set_share_limits(-2,-2,torrent.hash)
|
||||
|
||||
if args.dry_run == 'dry_run':
|
||||
if t_count >= 1 or len(n_info) > 1:
|
||||
logger.dryrun(n_info)
|
||||
logger.dryrun(f'Did not tag/set ratio limit/seeding time for {t_count} .torrents(s)')
|
||||
if t_del >= 1:
|
||||
logger.dryrun(f'Did not delete {t_del} .torrents(s) or content files.')
|
||||
logger.dryrun(f'Did not delete {t_del_cs} .torrents(s) (including cross-seed) or content files.')
|
||||
else:
|
||||
logger.dryrun('No torrents to tag with no hard links.')
|
||||
else:
|
||||
|
||||
if t_count >= 1 or len(n_info) > 1:
|
||||
logger.info(n_info)
|
||||
logger.info(f'tag/set ratio limit/seeding time for {t_count} .torrents(s)')
|
||||
if t_del >= 1:
|
||||
logger.info(f'Deleted {t_del} .torrents(s) AND content files.')
|
||||
logger.info(f'Deleted {t_del_cs} .torrents(s) (includes cross-seed torrents) AND content files.')
|
||||
else:
|
||||
logger.info('No torrents to tag with no hard links.')
|
||||
|
||||
|
||||
#will check if there are any hard links if it passes a file or folder
|
||||
def nohardlink(file):
|
||||
check = True
|
||||
if (os.path.isfile(file)):
|
||||
if (os.stat(file).st_nlink > 1):
|
||||
check = False
|
||||
else:
|
||||
for path, subdirs, files in os.walk(file):
|
||||
for x in files:
|
||||
if (os.stat(os.path.join(path,x)).st_nlink > 1):
|
||||
check = False
|
||||
return check
|
||||
|
||||
def run():
|
||||
update_category()
|
||||
|
@ -472,6 +606,7 @@ def run():
|
|||
cross_seed()
|
||||
recheck()
|
||||
rem_orphaned()
|
||||
tag_nohardlinks()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
|
|
Loading…
Reference in a new issue