diff --git a/main.py b/main.py index ea6ffd9..6ebdeb4 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ from modules.submenus.settings import settings_menu from modules.functions.settings.save_load_settings import load_settings from modules.utils.clear_screen import cls_stay from modules.functions.mainly.load_file_song import get_lyric_from_folder +from modules.submenus.tools import tools_menu class MainProcess(object): @@ -31,25 +32,28 @@ class MainProcess(object): "1": "单个歌曲的歌词下载", "2": "多个歌曲的歌词下载", "3": "从网易云下载的歌曲中获取歌词", + "t": "小工具", "s": "进入设置", "i": "程序信息", }) r = rinput("请选择:") - - if r == "1": - download_one_lyric(self) - elif r == "2": - mdl(self) - elif r == "3": - get_lyric_from_folder(self) - elif r == "0": - exit(0) - elif r == "i": - print_info(self) - elif r == "s": - settings_menu(self) - else: - input("请输入正确的选项\n按回车键继续...") + match r: + case "1": + download_one_lyric(self) + case "2": + mdl(self) + case "3": + get_lyric_from_folder(self) + case "0": + exit(0) + case "t": + tools_menu(self) + case "i": + print_info(self) + case "s": + settings_menu(self) + case _: + input("请输入正确的选项\n按回车键继续...") if __name__ == "__main__": diff --git a/modules/functions/mainly/load_file_song.py b/modules/functions/mainly/load_file_song.py index cae31d5..230fe05 100644 --- a/modules/functions/mainly/load_file_song.py +++ b/modules/functions/mainly/load_file_song.py @@ -147,7 +147,7 @@ def get_lyric_from_folder(self): suffix="", max=len(ncm_files), color="green", width=9999) as bar: total = len(ncm_files) allocated = 0 # 已经分配的任务数量 - while True: # 进入循环,执行 新建进程->检测队列->检测任务完成 的循环 + while True: # 进入循环,执行 "新建进程->检测队列->检测任务完成" 的循环 sleep(0.05) if current_process <= max_process and allocated < total: # 分配进程 Process(target=process_work, @@ -176,7 +176,7 @@ def get_lyric_from_folder(self): passed += 1 current_process -= 1 bar.print_onto_bar(Fore.YELLOW + - f"\"{r['musicName']} - " + f"\"{r['musicName']} by " f"{''.join([x + ', ' for x in [x[0] for x in r['artist']]])[:-2]}" "\"" + Fore.GREEN + " 已完成!") bar.next() diff --git a/modules/functions/mainly/multi_download.py b/modules/functions/mainly/multi_download.py index 92ca3a4..223bb19 100644 --- a/modules/functions/mainly/multi_download.py +++ b/modules/functions/mainly/multi_download.py @@ -6,7 +6,6 @@ from modules.utils.inputs import rinput from modules.functions.mainly.get_song import get_song_lyric from modules.utils.bar import CompactArrowBar - def mdl(self): """多个歌词文件的下载 @@ -34,7 +33,7 @@ def mdl(self): with CompactArrowBar(f"进度: %(index){len(str(len(ids)))}d/%(max)d", suffix="", max=len(ids), color="yellow", width=9999) as bar: for i in range(0, len(ids)): - r = get_song_lyric(ids[i], self.settings.lyric_path, self.settings.lyric_path, bar=bar) + r = get_song_lyric(ids[i], self.settings.lyric_path, self.settings.lyric_format, bar=bar) if r == "dl_err_connection": bar.print_onto_bar(Fore.RED + "下载发生错误!可能是连接被拒绝!请检查网络后再试\n按回车键继续任务(该任务会被跳过)...") input() diff --git a/modules/functions/mainly/multi_unlock.py b/modules/functions/mainly/multi_unlock.py new file mode 100644 index 0000000..e41d840 --- /dev/null +++ b/modules/functions/mainly/multi_unlock.py @@ -0,0 +1,3 @@ + + +def mult_unlock(...) \ No newline at end of file diff --git a/modules/submenus/settings.py b/modules/submenus/settings.py index d80fabd..82cf4f0 100644 --- a/modules/submenus/settings.py +++ b/modules/submenus/settings.py @@ -24,20 +24,21 @@ def settings_menu(self): "4": "部分动态效果", "s": "切换设置自动保存" }) - if r == "0": - return - elif r == "1": - __set_lyric_path(self) - elif r == "2": - __remove_output_files(self) - elif r == "3": - __set_lyric_format(self) - elif r == "4": - pass - elif r == "s": - self.settings.auto_save = not self.settings.auto_save - else: - input("输入无效!按回车键继续...") + match r: + case "0": + return + case "1": + __set_lyric_path(self) + case "2": + __remove_output_files(self) + case "3": + __set_lyric_format(self) + case "4": + pass + case "s": + self.settings.auto_save = not self.settings.auto_save + case _: + input("输入无效!按回车键继续...") def __remove_output_files(self): diff --git a/modules/submenus/tools.py b/modules/submenus/tools.py new file mode 100644 index 0000000..dd5ae6c --- /dev/null +++ b/modules/submenus/tools.py @@ -0,0 +1,50 @@ +"""小工具以及菜单""" +import os +from colorama import Fore + +from modules.utils.prints import print_menu +from modules.utils.clear_screen import cls_stay +from modules.utils.inputs import cinput +from modules.functions.mainly.load_file_song import load_and_decrypt_from_ncm, process_work + + + +def tools_menu(self): + while True: + cls_stay(self, "[小工具菜单]") + print_menu({ + "0": "返回上级菜单", + "d": "解锁指定ncm文件/指定文件夹" + }) + r = cinput("请选择:") + match r: + case "0": + return + case "d": + ncm_unlock(self) + input("按回车继续...") + case _: + input("请输入正确的选项\n按回车键继续...") + + +def ncm_unlock(self): + cls_stay(self, "[小工具 - 文件解锁]") + path = cinput("请输入绝对路径:").replace("\\","") + if not os.path.exists(path): # 判断目标存在与否 + print("目标不存在!") + return + if os.path.isfile(path): # 目标为文件则执行单文件解密 + r = load_and_decrypt_from_ncm(path, os.path.split(path)[-1], "original", True) + match r: + case "file_not_found": + print("文件未找到") + case "perm_error": + print("权限错误。请检查是否拥有对应权限或者文件是否被占用。") + case _: + print(f"解锁完毕!文件保存在:\n{Fore.GREEN}{r[-1]}") + return + elif os.path.isdir(path): # 目标为文件夹则执行文件夹遍历 + ... + else: + print("无法识别目标文件。请确认目标文件是否正确以及是否拥有对应权限。") + diff --git a/modules/utils/dump.py b/modules/utils/dump.py index 95d26f2..be2d40e 100644 --- a/modules/utils/dump.py +++ b/modules/utils/dump.py @@ -8,6 +8,7 @@ from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import unpad from Cryptodome.Util.strxor import strxor as xor from mutagen import mp3, flac, id3 +from modules.utils.wrappers import escape_file_not_found, escape_permission_error def regular_filename(filename): @@ -28,7 +29,14 @@ def regular_filename(filename): return filename -def load_and_decrypt_from_ncm(file_path, target_dir, out_format) -> dict | str: # author: Nzix Repo: nondanee +@escape_file_not_found +@escape_permission_error +def load_and_decrypt_from_ncm(file_path, target_dir, out_format, return_output_path=False) -> dict | str | tuple: + # Original author: Nzix Repo: nondanee + """解锁指定文件并按照规则保存在指定位置 + ``file_path`` 源文件路径 + ``target_dir`` 解锁后文件保存路径 + ``out_format`` 输出文件格式,使用“字典”格式字符串,若使用源文件名仅替换后缀则传入\"original\"""" core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') @@ -89,7 +97,9 @@ def load_and_decrypt_from_ncm(file_path, target_dir, out_format) -> dict | str: f.seek(image_space - image_size, 1) # media data - if meta_length: + if out_format == "original": + output_path = f"{os.path.splitext(file_path)[0]}.{meta_data['format']}" + elif meta_length: output_path = os.path.join(target_dir, regular_filename(out_format % {"name": meta_data["musicName"], "artists": "".join( [x[0]+"," for x in meta_data["artist"]] @@ -145,6 +155,12 @@ def load_and_decrypt_from_ncm(file_path, target_dir, out_format) -> dict | str: audio['album'] = meta_data['album'] audio['artist'] = '/'.join([artist[0] for artist in meta_data['artist']]) audio.save() - return meta_data + if return_output_path: + return meta_data, output_path + else: + return meta_data else: - return "no_meta_data" + if return_output_path: + return "no_meta_data", output_path + else: + return "no_meta_data" diff --git a/modules/utils/wrappers.py b/modules/utils/wrappers.py new file mode 100644 index 0000000..f2795ee --- /dev/null +++ b/modules/utils/wrappers.py @@ -0,0 +1,28 @@ +"""一些有的没有的装饰器""" + + +def escape_file_not_found(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except FileNotFoundError: + return "file_not_found" + return wrapper + + +def escape_decrypt_unsatisfied_file(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except (AssertionError, IsADirectoryError): + return "file_not_satisfied" + return wrapper + + +def escape_permission_error(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except PermissionError: + return "perm_error" + return wrapper