11import argparse
22import asyncio
3+ import copy
34import sys
45import os
56
1920from src .rip import on_decrypt_success , on_decrypt_failed , rip_song , rip_album , rip_artist , rip_playlist
2021from src .url import AppleMusicURL , URLType
2122from src .utils import check_dep , run_sync , safely_create_task , config_outdated
23+ from src .quality import print_song_quality , print_album_quality , print_playlist_quality , key_to_Headers
2224
2325
2426class InteractiveShell :
@@ -60,7 +62,8 @@ def __init__(self, loop: asyncio.AbstractEventLoop):
6062 self .parser = argparse .ArgumentParser (exit_on_error = False )
6163 subparser = self .parser .add_subparsers ()
6264 download_parser = subparser .add_parser ("download" , aliases = ["dl" ])
63- download_parser .add_argument ("url" , nargs = '*' ,type = str )
65+ quality_parser = subparser .add_parser ("quality" , aliases = ["qa" ])
66+ download_parser .add_argument ("url" , nargs = '*' , type = str )
6467 download_parser .add_argument ("-c" , "--codec" ,
6568 choices = ["alac" , "ec3" , "aac" , "aac-binaural" , "aac-downmix" , "aac-legacy" , "ac3" ],
6669 default = "alac" )
@@ -69,12 +72,23 @@ def __init__(self, loop: asyncio.AbstractEventLoop):
6972 download_parser .add_argument ("-l" , "--language" , default = it (Config ).region .language , action = "store" )
7073 download_parser .add_argument ("--include-participate-songs" , default = False , dest = "include" , action = "store_true" )
7174
75+ quality_parser .add_argument ("url" , nargs = '*' , type = str )
76+ quality_parser .add_argument ("-i" ,"--invert" , default = False , action = "store_true" )
77+ quality_parser .add_argument ("--codec-id" , default = True , action = "store_false" )
78+ quality_parser .add_argument ("--codec" , default = True , action = "store_false" )
79+ quality_parser .add_argument ("--bitrate" , default = True , action = "store_false" )
80+ quality_parser .add_argument ("--average-bitrate" , default = True , action = "store_false" )
81+ quality_parser .add_argument ("--channels" , default = True , action = "store_false" )
82+ quality_parser .add_argument ("--sample-rate" , default = True , action = "store_false" )
83+ quality_parser .add_argument ("--bit-depth" , default = True , action = "store_false" )
84+ quality_parser .add_argument ("-b" , "--batch" , default = False , action = "store_true" )
85+
7286 subparser .add_parser ("status" )
7387 subparser .add_parser ("login" )
7488 subparser .add_parser ("logout" )
7589 subparser .add_parser ("exit" )
7690
77- self .batch_download_mode = False
91+ self .batch_mode = False
7892
7993 async def show_status (self ):
8094 it (WrapperManager ).status .cache_invalidate ()
@@ -83,36 +97,50 @@ async def show_status(self):
8397 it (GlobalLogger ).logger .error ("The currently used wrapper-manager instance has no available account. Please execute login command to log in." )
8498 it (GlobalLogger ).logger .info (f"Regions available on wrapper-manager instance: { ', ' .join (st_resp .regions )} " )
8599
100+ async def handle_batch_mode (self , args , cmds ):
101+ try :
102+ if args .batch :
103+ self .batch_mode = True
104+ self .batch_args = args
105+ self .batch_command = cmds [0 ]
106+ it (GlobalLogger ).logger .info ("Entering batch mode. Enter one or more URLs per line (space-separated), type 'exit' to quit" )
107+ except :
108+ pass
109+
110+ async def batch_mode_parser (self , cmds : str ):
111+ args = self .batch_args
112+ args .url = copy .deepcopy (cmds )
113+ if cmds [0 ]!= "exit" :
114+ cmds [0 ] = self .batch_command
115+ return cmds ,args
116+
86117 async def command_parser (self , cmd : str ):
87118 if not cmd .strip ():
88119 return
89120 cmds = cmd .split (" " )
90- if self .batch_download_mode :
91- if cmds [0 ]== "exit" :
92- self .batch_download_mode = False
93- it (GlobalLogger ).logger .info ("Batch mode exited. Returning to normal command mode." )
121+ if self .batch_mode :
122+ cmds , args = await self .batch_mode_parser (cmds )
123+ else :
124+ try :
125+ args = self .parser .parse_args (cmds )
126+ except (argparse .ArgumentError , argparse .ArgumentTypeError , SystemExit ):
127+ it (GlobalLogger ).logger .warning (f"Unknown command: { cmd } " )
94128 return
95- args = self .batch_download_args
96- await self .do_download (cmds , args .codec , args .force , args .language , args .include )
97- return
98- try :
99- args = self .parser .parse_args (cmds )
100- except (argparse .ArgumentError , argparse .ArgumentTypeError , SystemExit ):
101- it (GlobalLogger ).logger .warning (f"Unknown command: { cmd } " )
102- return
129+ await self .handle_batch_mode (args , cmds )
103130 match cmds [0 ]:
104131 case "download" | "dl" :
105- if args .batch :
106- self .batch_download_mode = True
107- self .batch_download_args = args
108- it (GlobalLogger ).logger .info ("Entering batch mode. Enter one or more URLs per line (space-separated), type 'exit' to quit" )
109- return
110-
111- await self .do_download (args .url , args .codec , args .force , args .language , args .include )
132+ safely_create_task (self .do_download (args .url , args .codec , args .force , args .language , args .include ))
112133 case "status" :
113134 await self .show_status ()
114135 case "exit" :
115- self .handle_exit ()
136+ if self .batch_mode :
137+ self .batch_mode = False
138+ it (GlobalLogger ).logger .info ("Batch mode exited. Returning to normal command mode." )
139+ else :
140+ self .handle_exit ()
141+ case "quality" | "qa" :
142+ safely_create_task (self .do_quality (args .url , args ))
143+
116144
117145 async def do_download (self , raw_urls : list [str ], codec : str , force_download : bool , language : str , include : bool = False ):
118146 for raw_url in raw_urls :
@@ -133,6 +161,34 @@ async def do_download(self, raw_urls: list[str], codec: str, force_download: boo
133161 include_participate_in_works = include )))
134162 case URLType .Playlist :
135163 safely_create_task (rip_playlist (url , codec , Flags (force_save = force_download , language = language )))
164+ case _:
165+ it (GlobalLogger ).logger .error (f"Unsupported URLType - { raw_url } " )
166+ continue
167+
168+ async def do_quality (self , raw_urls : list [str ], args ):
169+ all_fields = list (key_to_Headers .keys ())
170+ show_fields = []
171+ for field in all_fields :
172+ if args .invert :
173+ show_fields = [f for f in key_to_Headers if not getattr (args , f )]
174+ else :
175+ show_fields = [f for f in key_to_Headers if getattr (args , f )]
176+
177+ for raw_url in raw_urls :
178+ url = AppleMusicURL .parse_url (raw_url )
179+ if not url :
180+ real_url = await it (WebAPI ).get_real_url (raw_url )
181+ url = AppleMusicURL .parse_url (real_url )
182+ if not url :
183+ it (GlobalLogger ).logger .error (f"Illegal URL! - { raw_url } " )
184+ continue
185+ match url .type :
186+ case URLType .Song :
187+ safely_create_task (print_song_quality (url , show_fields ))
188+ case URLType .Album :
189+ safely_create_task (print_album_quality (url , show_fields ))
190+ case URLType .Playlist :
191+ safely_create_task (print_playlist_quality (url , show_fields ))
136192 case _:
137193 it (GlobalLogger ).logger .error (f"Unsupported URLType - { raw_url } " )
138194 continue
@@ -143,6 +199,7 @@ def bottom_toolbar(self):
143199 def completer (self ):
144200 mycompleter = {
145201 "dl" : {
202+ "--batch" : None ,
146203 "--codec" : {
147204 "ec3" : None ,
148205 "aac" : None ,
@@ -164,6 +221,17 @@ def completer(self):
164221 },
165222 "--include-participate-songs" : None
166223 },
224+ "qa" : {
225+ "--invert" : None ,
226+ "--codec-id" : None ,
227+ "--codec" : None ,
228+ "--bitrate" : None ,
229+ "--average-bitrate" : None ,
230+ "--channels" : None ,
231+ "--sample-rate" : None ,
232+ "--bit-depth" : None ,
233+ "--batch" : None
234+ },
167235 "status" : None ,
168236 "login" : None ,
169237 "logout" : None ,
0 commit comments