diff --git a/main.py b/main.py index f336956..25da52e 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,7 @@ from sys import exit from colorama import init from modules.utils.inputs import rinput -from modules.utils.information import print_info +from modules.utils.prints import print_info, print_menu from modules.functions.mainly.multi_download import mdl from modules.functions.mainly.one_download import download_one_lyric from modules.submenus.settings import settings_menu @@ -26,8 +26,14 @@ class MainProcess(object): """程序主循环""" while True: cls_stay(self, "[程序主菜单]") - print("[0] 退出程序\n[1] 单个歌曲的歌词下载\n[2] 多个歌曲的歌词下载\n[3] 从网易云下载的歌曲中获取歌词" - "\n[s] 进入设置\n[i] 程序信息") + print_menu({ + "0": "退出程序", + "1": "单个歌曲的歌词下载", + "2": "多个歌曲的歌词下载", + "3": "从网易云下载的歌曲中获取歌词", + "s": "进入设置", + "i": "程序信息", + }) r = rinput("请选择:") if r == "1": diff --git a/modules/functions/mainly/load_file_song.py b/modules/functions/mainly/load_file_song.py index 7b0ef71..6f2b024 100644 --- a/modules/functions/mainly/load_file_song.py +++ b/modules/functions/mainly/load_file_song.py @@ -1,23 +1,23 @@ -import binascii import json import os -import struct from base64 import b64decode from multiprocessing import Process, Queue from queue import Empty from time import sleep +from sys import exit import mutagen.mp3 from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import unpad -from mutagen import File, flac -from mutagen.id3 import ID3, TPE1, APIC, COMM, TIT2, TALB + +from mutagen import File from colorama import Fore, Style from modules.utils.clear_screen import cls_stay from modules.functions.mainly.get_song import get_song_lyric from modules.utils.inputs import cinput, rinput from modules.utils.bar import CompactBar, CompactArrowBar +from modules.utils.dump import load_and_decrypt_from_ncm def load_information_from_song(path) -> str | dict: @@ -27,7 +27,7 @@ def load_information_from_song(path) -> str | dict: except mutagen.mp3.HeaderNotFoundError: return "not_a_music" if os.path.splitext(path)[-1] == ".mp3": # 当文件为 mp3 时使用 ID3 格式读取 - if file.tags.get("COMM::XXX"): + if file.tags and file.tags.get("COMM::XXX"): if file.tags["COMM::XXX"].text[0][:7] == "163 key": ciphertext = file.tags["COMM::XXX"].text[0][22:] else: @@ -63,101 +63,17 @@ def load_information_from_song(path) -> str | dict: return "decrypt_failed" -def load_and_decrypt_from_ncm(file_path, target_dir) -> dict: # nondanee的源代码, 根据需求更改了某些东西 - core_key = binascii.a2b_hex("687A4852416D736F356B496E62617857") - meta_key = binascii.a2b_hex("2331346C6A6B5F215C5D2630553C2728") - f = open(file_path, 'rb') - header = f.read(8) - assert binascii.b2a_hex(header) == b'4354454e4644414d' - f.seek(2, 1) - key_length = f.read(4) - key_length = struct.unpack('= key_length: - key_offset = 0 - key_box[i] = key_box[c] - key_box[c] = swap - last_byte = c - meta_length = f.read(4) - meta_length = struct.unpack(' Settings: # 加载 的函数 @@ -36,7 +44,7 @@ def load_settings() -> Settings: # 加载 的函数 if not os.path.exists(settings.lyric_path): # 检测输出文件夹,若文件夹不存在则在启动时创建 os.mkdir(settings.lyric_path) return settings - except json.decoder.JSONDecodeError: # 如果检测到文件无法读取,将会删除设置文件并重新创建 + except json.decoder.JSONDecodeError or KeyError: # 如果检测到文件无法读取,将会删除设置文件并重新创建 print("设置文件损坏,重新创建...") os.remove("settings.json") return load_settings() @@ -52,4 +60,4 @@ def save_settings(settings): # 保存 的函数 返回 done 即为完成,理论上不存在报错所以暂时没有做其他处理""" with open("settings.json", 'w', encoding="utf-8") as f: f.write(json.dumps(settings, default=class2dict)) - input("保存完成!按回车继续...") + return "done" diff --git a/modules/submenus/settings.py b/modules/submenus/settings.py index 7b279ed..e397ea5 100644 --- a/modules/submenus/settings.py +++ b/modules/submenus/settings.py @@ -1,17 +1,29 @@ """集合设置参数""" import os +from colorama import Fore from modules.utils.clear_screen import cls_stay from modules.utils.inputs import rinput, cinput from modules.functions.settings.save_load_settings import save_settings +from modules.utils.prints import print_menu def settings_menu(self): """设置菜单主循环""" while True: - cls_stay(self, "[设置菜单]") - print("[0] 返回上级\n[1] 歌曲保存路径\n[2] 清空输出文件夹内的内容\n[3] 歌词文件保存格式\n[4] 部分动态效果\n" - "[s] 将设置保存到文件") + if self.settings.auto_save: + save_settings(self.settings) + cls_stay(self, f"[设置菜单] " + f"{Fore.LIGHTCYAN_EX}自动保存: " + f"{({True: f'{Fore.GREEN}开', False: f'{Fore.RED}关'}[self.settings.auto_save])}") + print_menu({ + "0": "返回上级菜单", + "1": "歌曲保存路径", + "2": "清空输出文件夹内的内容", + "3": "歌词文件保存格式", + "4": "部分动态效果", + "s": "切换设置自动保存" + }) r = rinput("请选择:") if r == "0": return @@ -24,7 +36,7 @@ def settings_menu(self): elif r == "4": pass elif r == "s": - __save_settings(self) + self.settings.auto_save = not self.settings.auto_save else: input("输入无效!按回车键继续...") diff --git a/modules/utils/clear_screen.py b/modules/utils/clear_screen.py index 62f290b..b9585e6 100644 --- a/modules/utils/clear_screen.py +++ b/modules/utils/clear_screen.py @@ -1,5 +1,6 @@ """用来清空命令行的信息,自动判别系统""" import os +from colorama import Fore def clear(): @@ -15,5 +16,5 @@ def clear(): def cls_stay(self, custom=""): """保留版本号清除屏幕""" clear() - print(f"[NeteaseMusicLyricDownloader] {self.version}") - print(custom) \ No newline at end of file + print(f"{Fore.YELLOW}[{Fore.GREEN}NeteaseMusicLyricDownloader{Fore.YELLOW}] {Fore.LIGHTBLACK_EX}{self.version}") + print(Fore.LIGHTMAGENTA_EX+custom) diff --git a/modules/utils/dump.py b/modules/utils/dump.py new file mode 100644 index 0000000..1662837 --- /dev/null +++ b/modules/utils/dump.py @@ -0,0 +1,131 @@ +import binascii +import json +import os +import struct +import base64 + +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 + + +def load_and_decrypt_from_ncm(file_path, target_dir, out_format) -> dict | str: # author: Nzix Repo: nondanee + + core_key = binascii.a2b_hex('687A4852416D736F356B496E62617857') + meta_key = binascii.a2b_hex('2331346C6A6B5F215C5D2630553C2728') + + f = open(file_path, 'rb') + + # magic header + header = f.read(8) + assert binascii.b2a_hex(header) == b'4354454e4644414d' + + f.seek(2, 1) + + # key data + key_length = f.read(4) + key_length = struct.unpack(' 1024 ** 2 * 16 else 'mp3'} + + f.seek(5, 1) + + # album cover + image_space = f.read(4) + image_space = struct.unpack('