1- #!/usr/bin/python3
21import argparse , os , statistics
32from collections import defaultdict
43from pathlib import Path
4+
5+ #!/usr/bin/python3
6+ from random import randint
57from statistics import mean , median
68from time import sleep
79
@@ -99,15 +101,44 @@ def parse_args():
99101 return args
100102
101103
104+ def torrent_files (t ):
105+ import qbittorrentapi
106+
107+ attempts = 10
108+ attempt = 0
109+ while attempt < attempts :
110+ attempt += 1
111+
112+ try :
113+ return t .files
114+ except (qbittorrentapi .APIConnectionError , ConnectionRefusedError , TimeoutError ):
115+ sleep (randint (1 , 10 ))
116+
117+ return []
118+
119+
102120def qbt_get_tracker (qbt_client , torrent ):
103- tracker = torrent .tracker
104- if not tracker :
105- tracker = iterables .safe_unpack (
106- sorted (
107- (tr .url for tr in qbt_client .torrents_trackers (torrent .hash ) if tr .url .startswith ("http" )), reverse = True
108- )
109- )
110- return tld_from_url (tracker )
121+ import qbittorrentapi
122+
123+ attempts = 10
124+ attempt = 0
125+ while attempt < attempts :
126+ attempt += 1
127+
128+ try :
129+ tracker = torrent .tracker
130+ if not tracker :
131+ tracker = iterables .safe_unpack (
132+ sorted (
133+ (tr .url for tr in qbt_client .torrents_trackers (torrent .hash ) if tr .url .startswith ("http" )),
134+ reverse = True ,
135+ )
136+ )
137+ return tld_from_url (tracker )
138+ except (qbittorrentapi .APIConnectionError , ConnectionRefusedError , TimeoutError ):
139+ sleep (randint (1 , 10 ))
140+
141+ return None
111142
112143
113144def qbt_enhance_torrents (qbt_client , qbt_torrents ):
@@ -174,9 +205,9 @@ def filter_torrents_by_criteria(args, torrents):
174205 if "sizes" not in args .defaults :
175206 torrents = [t for t in torrents if args .sizes (t .total_size )]
176207 if "file_count" not in args .defaults :
177- torrents = [t for t in torrents if args .file_count (len (t . files ))]
208+ torrents = [t for t in torrents if args .file_count (len (torrent_files ( t ) ))]
178209 if "avg_sizes" not in args .defaults :
179- torrents = [t for t in torrents if args .avg_sizes (median ([f .size for f in t . files ]))]
210+ torrents = [t for t in torrents if args .avg_sizes (median ([f .size for f in torrent_files ( t ) ]))]
180211 if "ratio" not in args .defaults :
181212 torrents = [t for t in torrents if args .ratio (t .ratio )]
182213 if "seeders" not in args .defaults :
@@ -281,9 +312,11 @@ def filter_torrents_by_criteria(args, torrents):
281312 )
282313 ]
283314 if args .file_search :
284- torrents = [t for t in torrents if strings .glob_match_all (args .file_search , [f .name for f in t . files ])]
315+ torrents = [t for t in torrents if strings .glob_match_all (args .file_search , [f .name for f in torrent_files ( t ) ])]
285316 if args .file_exclude :
286- torrents = [t for t in torrents if not strings .glob_match_any (args .file_exclude , [f .name for f in t .files ])]
317+ torrents = [
318+ t for t in torrents if not strings .glob_match_any (args .file_exclude , [f .name for f in torrent_files (t )])
319+ ]
287320
288321 if args .tracker :
289322 trackers = set (args .tracker )
@@ -298,15 +331,17 @@ def filter_torrents_by_criteria(args, torrents):
298331 for t in torrents
299332 if args .any_exists
300333 is any (
301- (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name ).exists () for f in t .files
334+ (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name ).exists ()
335+ for f in torrent_files (t )
302336 )
303337 ]
304338 if args .all_exists is True :
305339 torrents = [
306340 t
307341 for t in torrents
308342 if all (
309- (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name ).exists () for f in t .files
343+ (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name ).exists ()
344+ for f in torrent_files (t )
310345 )
311346 ]
312347 elif args .all_exists is False :
@@ -315,7 +350,7 @@ def filter_torrents_by_criteria(args, torrents):
315350 for t in torrents
316351 if any (
317352 not (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name ).exists ()
318- for f in t . files
353+ for f in torrent_files ( t )
319354 )
320355 ]
321356 if args .opened is not None :
@@ -325,7 +360,7 @@ def filter_torrents_by_criteria(args, torrents):
325360 if args .opened
326361 is any (
327362 file_utils .is_file_open (Path (t .save_path if t .state_enum .is_complete else t .download_path ) / f .name )
328- for f in t . files
363+ for f in torrent_files ( t )
329364 )
330365 ]
331366
@@ -380,7 +415,9 @@ def print_torrents_by_tracker(args, torrents):
380415 "count" : len (tracker_torrents ),
381416 "size" : sum (t .total_size for t in tracker_torrents ),
382417 "remaining" : remaining ,
383- "files" : (sum (len (t .files ) for t in tracker_torrents ) if args .file_counts else None ), # a bit slow
418+ "files" : (
419+ sum (len (torrent_files (t )) for t in tracker_torrents ) if args .file_counts else None
420+ ), # a bit slow
384421 }
385422 )
386423 if trackers :
@@ -416,7 +453,7 @@ def agg_torrents_state(args, state, state_torrents):
416453 return {
417454 "state" : state ,
418455 "count" : len (state_torrents ),
419- "files" : (sum (len (t . files ) for t in state_torrents ) if args .file_counts else None ),
456+ "files" : (sum (len (torrent_files ( t ) ) for t in state_torrents ) if args .file_counts else None ),
420457 "size" : strings .file_size (sum (t .total_size for t in state_torrents )),
421458 "downloaded" : strings .file_size (downloaded ) if downloaded else None ,
422459 "uploaded" : strings .file_size (uploaded ) if uploaded else None ,
@@ -488,11 +525,11 @@ def shorten(s, width):
488525 elif args .sort == "remaining" :
489526 torrents = sorted (torrents , key = lambda t : t .amount_left , reverse = reverse_sort )
490527 elif args .sort in ["counts" , "count" ]:
491- torrents = sorted (torrents , key = lambda t : len (t . files ), reverse = reverse_sort )
528+ torrents = sorted (torrents , key = lambda t : len (torrent_files ( t ) ), reverse = reverse_sort )
492529 elif args .sort in ["size" , "total_size" ]:
493530 torrents = sorted (torrents , key = lambda t : t .total_size , reverse = reverse_sort )
494531 elif args .sort in ["avg_size" ]:
495- torrents = sorted (torrents , key = lambda t : mean ([f .size for f in t . files ]), reverse = reverse_sort )
532+ torrents = sorted (torrents , key = lambda t : mean ([f .size for f in torrent_files ( t ) ]), reverse = reverse_sort )
496533 elif args .sort in ["network" , "download+upload" , "ingress+egress" ]:
497534 torrents = sorted (
498535 torrents ,
@@ -590,7 +627,9 @@ def shorten(s, width):
590627
591628 base_path = None
592629 for test_path in base_paths :
593- if test_path and ((Path (test_path ) / t .name ).exists () or (Path (test_path ) / t .files [0 ].name ).exists ()):
630+ if test_path and (
631+ (Path (test_path ) / t .name ).exists () or (Path (test_path ) / torrent_files (t )[0 ].name ).exists ()
632+ ):
594633 base_path = test_path
595634 break
596635
@@ -603,7 +642,7 @@ def shorten(s, width):
603642 continue
604643
605644 if "f" in args .print :
606- for f in t . files :
645+ for f in torrent_files ( t ) :
607646 file_path = Path (base_path ) / f .name
608647 print (file_path )
609648 else :
@@ -646,16 +685,16 @@ def gen_row(t):
646685 d |= {"tracker" : t .tracker_domain ()}
647686
648687 if args .file_search :
649- files = t . files
650- files = [f for f in t . files if strings .glob_match_all (args .file_search , [f .name ])]
688+ files = torrent_files ( t )
689+ files = [f for f in torrent_files ( t ) if strings .glob_match_all (args .file_search , [f .name ])]
651690
652691 print (t .name )
653692 printing .extended_view (files )
654693 print ()
655694
656- d |= {"files" : f"{ len (files )} ({ len (t . files )} )" }
695+ d |= {"files" : f"{ len (files )} ({ len (torrent_files ( t ) )} )" }
657696 elif args .file_counts :
658- d |= {"files" : len (t . files )}
697+ d |= {"files" : len (torrent_files ( t ) )}
659698
660699 if args .paths :
661700 if t .state_enum .is_complete :
@@ -719,16 +758,16 @@ def gen_row(t):
719758 d |= {"tracker" : t .tracker_domain ()}
720759
721760 if args .file_search :
722- files = t . files
723- files = [f for f in t . files if strings .glob_match_all (args .file_search , [f .name ])]
761+ files = torrent_files ( t )
762+ files = [f for f in torrent_files ( t ) if strings .glob_match_all (args .file_search , [f .name ])]
724763
725764 print (t .name )
726765 printing .extended_view (files )
727766 print ()
728767
729- d |= {"files" : f"{ len (files )} ({ len (t . files )} )" }
768+ d |= {"files" : f"{ len (files )} ({ len (torrent_files ( t ) )} )" }
730769 elif args .file_counts :
731- d |= {"files" : len (t . files )}
770+ d |= {"files" : len (torrent_files ( t ) )}
732771
733772 if args .paths :
734773 if t .state_enum .is_complete :
@@ -778,7 +817,7 @@ def gen_row(t):
778817 if invalid_state :
779818 continue
780819
781- for file in t . files :
820+ for file in torrent_files ( t ) :
782821 for base_path in base_paths :
783822 file_path = Path (base_path ) / file .name
784823 if file_path .exists () and not file_path .is_dir ():
0 commit comments