33
44from library import usage
55from library .mediafiles import torrents_start
6+ from library .utils import arggroups , argparse_utils , consts , iterables , printing , strings
7+ from library .mediafiles import torrents_start
68from library .utils import arggroups , argparse_utils , consts , iterables , printing , processes , strings
79from library .utils .path_utils import domain_from_url
810
911
1012def parse_args ():
1113 parser = argparse_utils .ArgumentParser (usage = usage .torrents_info )
1214 arggroups .qBittorrent (parser )
15+ parser .add_argument (
16+ "--downloading" ,
17+ "--download" ,
18+ "--down" ,
19+ "--dl" ,
20+ "--leech" ,
21+ action = argparse .BooleanOptionalAction ,
22+ default = True ,
23+ help = "Include downloading torrents" ,
24+ )
25+ parser .add_argument (
26+ "--uploading" ,
27+ "--upload" ,
28+ "--up" ,
29+ "--ul" ,
30+ "--seeds" ,
31+ action = argparse .BooleanOptionalAction ,
32+ default = False ,
33+ help = "Include uploading torrents" ,
34+ )
35+
36+ parser .add_argument ("--priority" , action = 'store_true' , help = "Sort by priority" )
37+
38+ parser .add_argument ("--active" , action = argparse .BooleanOptionalAction , default = True , help = "Show active torrents" )
39+ parser .add_argument (
40+ "--inactive" , "--dead" , action = argparse .BooleanOptionalAction , default = False , help = "Show inactive torrents"
41+ )
42+
43+ parser .add_argument ("--status" , action = argparse .BooleanOptionalAction , default = True , help = "Show status summary" )
44+ parser .add_argument ("--transfers" , action = argparse .BooleanOptionalAction , default = True , help = "Show transfer summary" )
45+ parser .add_argument ("--trackers" , action = argparse .BooleanOptionalAction , default = False , help = "Show tracker summary" )
46+ parser .add_argument ("--file-count" , "--count" , action = 'store_true' , help = "Include file counts (a bit slow)" )
47+
1348 parser .add_argument (
1449 "--force-start" , "--start" , action = argparse .BooleanOptionalAction , help = "Force start matching torrents"
1550 )
@@ -36,49 +71,118 @@ def qbt_get_tracker(qbt_client, torrent):
3671def torrents_info ():
3772 args = parse_args ()
3873
74+ def shorten (s , width ):
75+ return s if args .verbose >= consts .LOG_INFO else strings .shorten (s , width )
76+
3977 qbt_client = torrents_start .start_qBittorrent (args )
40- all_torrents = qbt_client .torrents_info ()
78+ torrents = qbt_client .torrents_info ()
79+
80+ torrents = [
81+ t
82+ for t in torrents
83+ if t .state_enum .is_downloading == args .downloading or t .state_enum .is_uploading == args .uploading
84+ ]
4185
4286 if args .torrent_search or args .file_search :
43- torrents = [t for t in all_torrents if strings .glob_match (args .torrent_search , [t .name , t .save_path , t .hash ])]
87+ torrents = [t for t in torrents if strings .glob_match (args .torrent_search , [t .name , t .save_path , t .hash ])]
4488
4589 if args .file_search :
4690 torrents = [t for t in torrents if strings .glob_match (args .file_search , [f .name for f in t .files ])]
4791
48- if not torrents :
49- processes .no_media_found ()
92+ if not torrents :
93+ processes .no_media_found ()
5094
51- torrents = sorted (torrents , key = lambda t : - t .time_active )
52- for torrent in torrents :
53- printing .extended_view (torrent )
95+ if args .downloading and not args .uploading :
96+ if args .priority :
97+ torrents = sorted (torrents , key = lambda t : t .priority )
98+ else :
99+ torrents = sorted (torrents , key = lambda t : t .eta )
100+ else :
101+ torrents = sorted (torrents , key = lambda t : (t .added_on , t .time_active ))
54102
55- files = torrent .files
56- if args .file_search :
57- files = [f for f in torrent .files if strings .glob_match (args .file_search , [f .name ])]
103+ if args .torrent_search or args .file_search :
104+ print (len (torrents ), "matching torrents" )
105+
106+ if args .inactive :
107+ active_torrents = [t for t in torrents if t .downloaded_session > 0 ]
108+ if active_torrents :
109+ print ('Active' )
110+
111+ def gen_row (t ):
112+ d = {
113+ 'name' : shorten (t .name , 35 ),
114+ "num_seeds" : f"{ t .num_seeds } ({ t .num_complete } )" ,
115+ 'progress' : strings .safe_percent (t .progress ),
116+ 'remaining' : strings .file_size (t .amount_left ),
117+ 'speed' : strings .file_size (t .dlspeed ) + '/s' if t .dlspeed else None ,
118+ 'eta' : strings .duration (t .eta ),
119+ 'downloaded_session' : strings .file_size (t .downloaded_session ),
120+ }
121+ if args .file_search :
122+ files = t .files
123+ files = [f for f in t .files if strings .glob_match (args .file_search , [f .name ])]
124+
125+ print (t .name )
126+ printing .extended_view (files )
127+ print ()
128+
129+ d |= {'files' : f"{ len (files )} ({ len (t .files )} )" }
130+ if args .verbose >= consts .LOG_INFO :
131+ d |= {
132+ 'tracker' : qbt_get_tracker (qbt_client , t ),
133+ 'seen_complete' : strings .relative_datetime (t .seen_complete ),
134+ 'added_on' : strings .relative_datetime (t .added_on ),
135+ 'last_activity' : strings .relative_datetime (t .last_activity ),
136+ 'size' : strings .file_size (t .total_size ),
137+ 'comment' : t .comment ,
138+ 'content_path' : t .content_path ,
139+ }
58140
59- if args .verbose >= consts .LOG_INFO :
60- printing .extended_view (files )
141+ return d
61142
62- if len (torrent .files ) == 1 :
63- print ("1 file" )
64- elif args .file_search :
65- print (len (files ), "files of" , len (torrent .files ), "matched" )
66- else :
67- print (len (torrent .files ), "total files" )
68- print ()
143+ printing .table (iterables .conform ([gen_row (t ) for t in active_torrents ]))
144+
145+ if args .inactive :
146+ inactive_torrents = [t for t in torrents if t .downloaded_session == 0 ]
147+ if inactive_torrents :
148+ print ('Inactive' )
149+ inactive_torrents = sorted (
150+ inactive_torrents , key = lambda t : (t .downloaded > 0 , t .time_active * t .last_activity )
151+ )
69152
70- print (len (torrents ), "matched torrents" )
153+ def gen_row (t ):
154+ d = {
155+ 'name' : shorten (t .name , 50 ),
156+ 'progress' : strings .safe_percent (t .progress ),
157+ 'downloaded' : strings .file_size (t .downloaded ),
158+ 'time_active' : strings .duration (t .time_active ),
159+ "num_seeds" : f"{ t .num_seeds } ({ t .num_complete } )" ,
160+ 'seen_complete' : strings .relative_datetime (t .seen_complete ),
161+ 'last_activity' : strings .relative_datetime (t .last_activity ),
162+ }
163+ if args .file_search :
164+ files = t .files
165+ files = [f for f in t .files if strings .glob_match (args .file_search , [f .name ])]
166+
167+ print (t .name )
168+ printing .extended_view (files )
169+ print ()
170+
171+ d |= {'files' : f"{ len (files )} ({ len (t .files )} )" }
172+ if args .verbose >= consts .LOG_INFO :
173+ d |= {
174+ 'state' : t .state ,
175+ 'tracker' : qbt_get_tracker (qbt_client , t ),
176+ 'added_on' : strings .relative_datetime (t .added_on ),
177+ 'size' : strings .file_size (t .total_size ),
178+ 'remaining' : strings .file_size (t .amount_left ),
179+ 'comment' : t .comment ,
180+ 'content_path' : t .content_path ,
181+ }
71182
72- torrent_hashes = [t .hash for t in torrents ]
73- if args .mark_deleted :
74- qbt_client .torrents_add_tags (tags = "library-delete" , torrent_hashes = torrent_hashes )
75- elif args .delete_files :
76- qbt_client .torrents_delete (delete_files = True , torrent_hashes = torrent_hashes )
77- elif args .delete_rows :
78- qbt_client .torrents_delete (delete_files = False , torrent_hashes = torrent_hashes )
79- elif args .force_start is not None :
80- qbt_client .torrents_set_force_start (args .force_start , torrent_hashes = torrent_hashes )
81- return
183+ return d
184+
185+ printing .table (iterables .conform ([gen_row (t ) for t in torrents ]))
82186
83187 interesting_states = [
84188 "stoppedUP" ,
@@ -94,36 +198,34 @@ def torrents_info():
94198 "error" ,
95199 ]
96200
97- torrents_by_state = {}
98- for torrent in all_torrents :
99- torrents_by_state .setdefault (torrent .state , []).append (torrent )
100-
101- if args .verbose >= consts .LOG_INFO :
102- torrents_by_tracker = {}
103- for torrent in all_torrents :
104- torrents_by_tracker .setdefault (qbt_get_tracker (qbt_client , torrent ), []).append (torrent )
201+ if args .status :
202+ torrents_by_state = {}
203+ for torrent in torrents :
204+ torrents_by_state .setdefault (torrent .state , []).append (torrent )
105205
106206 tbl = []
107207 for state in {* interesting_states } - {"uploading" , "downloading" }:
108- torrents = torrents_by_state .get (state )
109- if not torrents :
208+ state_torrents = torrents_by_state .get (state )
209+ if not state_torrents :
110210 continue
111211
112- torrents = sorted (torrents , key = lambda t : (- t .seen_complete , t .time_active ))
212+ state_torrents = sorted (state_torrents , key = lambda t : (- t .seen_complete , t .time_active ))
113213
114214 tbl .extend (
115215 [
116216 {
117217 "state" : state ,
118218 "name" : printing .path_fill (t .name , width = 76 ),
119- "seen_complete" : strings .relative_datetime (t .seen_complete ) if t .seen_complete > 0 else None ,
219+ "seen_complete" : (
220+ strings .relative_datetime (t .seen_complete ) if t .seen_complete > 0 else None
221+ ),
120222 "last_activity" : strings .relative_datetime (t .last_activity ),
121223 "time_active" : strings .duration (t .time_active ),
122224 "num_seeds" : f"{ t .num_seeds } ({ t .num_complete } )" ,
123225 # 'num_leechs': f"{t.num_leechs} ({t.num_incomplete})",
124226 # 'comment': t.comment,
125227 }
126- for t in torrents
228+ for t in state_torrents
127229 ]
128230 )
129231 if tbl :
@@ -132,12 +234,12 @@ def torrents_info():
132234
133235 tbl = []
134236 for state in ["downloading" , "missingFiles" , "error" ]:
135- torrents = torrents_by_state .get (state )
136- if not torrents :
237+ state_torrents = torrents_by_state .get (state )
238+ if not state_torrents :
137239 continue
138240
139- torrents = sorted (
140- torrents , key = lambda t : (t .amount_left == t .total_size , t .eta , t .amount_left ), reverse = True
241+ state_torrents = sorted (
242+ state_torrents , key = lambda t : (t .amount_left == t .total_size , t .eta , t .amount_left ), reverse = True
141243 )
142244
143245 tbl .extend (
@@ -149,25 +251,59 @@ def torrents_info():
149251 "eta" : strings .duration (t .eta ) if t .eta < 8640000 else None ,
150252 "remaining" : strings .file_size (t .amount_left ),
151253 }
152- for t in torrents
254+ for t in state_torrents
153255 ]
154256 )
155257 if tbl :
156258 printing .table (tbl )
157259 print ()
158260
261+ categories = []
262+ for state , state_torrents in torrents_by_state .items ():
263+ remaining = sum (t .amount_left for t in state_torrents )
264+ categories .append (
265+ {
266+ "state" : state ,
267+ "count" : len (state_torrents ),
268+ "size" : strings .file_size (sum (t .total_size for t in state_torrents )),
269+ "remaining" : strings .file_size (remaining ) if remaining else None ,
270+ "file_count" : (
271+ sum (len (t .files ) for t in state_torrents ) if args .verbose >= 2 else None
272+ ), # a bit slow
273+ }
274+ )
275+
276+ categories = sorted (
277+ categories ,
278+ key = lambda d : (
279+ d ["state" ].endswith (("missingFiles" , "error" )),
280+ d ["state" ].endswith (("downloading" , "DL" )),
281+ iterables .safe_index (interesting_states , d ["state" ]),
282+ ),
283+ )
284+ printing .table (iterables .list_dict_filter_bool (categories ))
285+ print ()
286+
287+ if args .trackers :
288+ torrents_by_tracker = {}
289+ for torrent in torrents :
290+ torrents_by_tracker .setdefault (qbt_get_tracker (qbt_client , torrent ), []).append (torrent )
291+
292+
159293 trackers = []
160- for tracker , torrents in torrents_by_tracker .items ():
161- torrents = [t for t in torrents if args .verbose >= 2 or t .state not in ("stoppedDL" ,)]
162- remaining = sum (t .amount_left for t in torrents )
294+ for tracker , tracker_torrents in torrents_by_tracker .items ():
295+ tracker_torrents = [t for t in tracker_torrents if args .verbose >= 2 or t .state not in ("stoppedDL" ,)]
296+ remaining = sum (t .amount_left for t in tracker_torrents )
163297 if remaining or args .verbose >= 2 :
164298 trackers .append (
165299 {
166300 "tracker" : tracker ,
167- "count" : len (torrents ),
168- "size" : sum (t .total_size for t in torrents ),
301+ "count" : len (tracker_torrents ),
302+ "size" : sum (t .total_size for t in tracker_torrents ),
169303 "remaining" : remaining ,
170- "file_count" : sum (len (t .files ) for t in torrents ) if args .verbose >= 2 else None , # a bit slow
304+ "file_count" : (
305+ sum (len (t .files ) for t in tracker_torrents ) if args .verbose >= 2 else None
306+ ), # a bit slow
171307 }
172308 )
173309 if trackers :
@@ -183,39 +319,26 @@ def torrents_info():
183319 printing .table (iterables .list_dict_filter_bool (trackers ))
184320 print ()
185321
186- categories = []
187- for state , torrents in torrents_by_state .items ():
188- remaining = sum (t .amount_left for t in torrents )
189- categories .append (
190- {
191- "state" : state ,
192- "count" : len (torrents ),
193- "size" : strings .file_size (sum (t .total_size for t in torrents )),
194- "remaining" : strings .file_size (remaining ) if remaining else None ,
195- "file_count" : sum (len (t .files ) for t in torrents ) if args .verbose >= 2 else None , # a bit slow
196- }
197- )
198-
199- categories = sorted (
200- categories ,
201- key = lambda d : (
202- d ["state" ].endswith (("missingFiles" , "error" )),
203- d ["state" ].endswith (("downloading" , "DL" )),
204- iterables .safe_index (interesting_states , d ["state" ]),
205- ),
206- )
207- printing .table (iterables .list_dict_filter_bool (categories ))
208- print ()
209-
210- transfer = qbt_client .transfer_info ()
211- print (transfer .connection_status .upper ())
212-
213- dl_speed = strings .file_size (transfer .dl_info_speed )
214- dl_limit = f"[{ strings .file_size (transfer .dl_rate_limit )} /s]" if transfer .dl_rate_limit > 0 else ""
215- dl_d = strings .file_size (transfer .dl_info_data )
216- print (f"DL { dl_speed } /s { dl_limit } ({ dl_d } )" )
217-
218- up_speed = strings .file_size (transfer .up_info_speed )
219- up_limit = f"[{ strings .file_size (transfer .up_rate_limit )} /s]" if transfer .up_rate_limit > 0 else ""
220- up_d = strings .file_size (transfer .up_info_data )
221- print (f"UP { up_speed } /s { up_limit } ({ up_d } )" )
322+ if args .transfers :
323+ transfer = qbt_client .transfer_info ()
324+ print (transfer .connection_status .upper ())
325+
326+ dl_speed = strings .file_size (transfer .dl_info_speed )
327+ dl_limit = f"[{ strings .file_size (transfer .dl_rate_limit )} /s]" if transfer .dl_rate_limit > 0 else ""
328+ dl_d = strings .file_size (transfer .dl_info_data )
329+ print (f"DL { dl_speed } /s { dl_limit } ({ dl_d } )" )
330+
331+ up_speed = strings .file_size (transfer .up_info_speed )
332+ up_limit = f"[{ strings .file_size (transfer .up_rate_limit )} /s]" if transfer .up_rate_limit > 0 else ""
333+ up_d = strings .file_size (transfer .up_info_data )
334+ print (f"UP { up_speed } /s { up_limit } ({ up_d } )" )
335+
336+ torrent_hashes = [t .hash for t in torrents ]
337+ if args .mark_deleted :
338+ qbt_client .torrents_add_tags (tags = "library-delete" , torrent_hashes = torrent_hashes )
339+ elif args .delete_files :
340+ qbt_client .torrents_delete (delete_files = True , torrent_hashes = torrent_hashes )
341+ elif args .delete_rows :
342+ qbt_client .torrents_delete (delete_files = False , torrent_hashes = torrent_hashes )
343+ elif args .force_start is not None :
344+ qbt_client .torrents_set_force_start (args .force_start , torrent_hashes = torrent_hashes )
0 commit comments