diff --git a/modules/get_song.py b/modules/get_song.py index fbe8648..2285cd4 100644 --- a/modules/get_song.py +++ b/modules/get_song.py @@ -50,59 +50,71 @@ def get_song_info_raw(types: list, id: str): return need -def get_song_lyric(id: str | int, path: str): +def get_song_lyric(id: str | int | dict, path: str, allinfo: bool = False): """获取歌词 ``id`` 提供一个歌曲id - ``path`` 提供歌曲下载的路径""" - sinfo = get_song_info_raw(["name", "artists"], id) - if sinfo == "dl_err_connection": # 处理各式各样的事件 - return "dl_err_connection" - elif sinfo == "song_nf": - return "song_nf" - else: # 整理歌曲数据,获取歌词 - artists = "" + ``path`` 提供歌曲下载的路径 + ``allinfo`` 若此项为 True ,则提供的id格式必须为 {"id": int | str, "name": str, "artists": [[str, ...], ...]} (dict)""" + if allinfo: + sinfo = id + id = id["id"] + else: + sinfo = get_song_info_raw(["name", "artists"], id) + if sinfo == "dl_err_connection": # 处理各式各样的事件 + return "dl_err_connection" + elif sinfo == "song_nf": + return "song_nf" + + # 整理歌曲数据,获取歌词 + artists = "" + if allinfo: + for i in sinfo["artists"]: + artists += f"{i[0]}," + else: for i in sinfo["artists"]: artists += f"{i['name']}," - artists = artists[:-1] + artists = artists[:-1] - name = sinfo["name"] - replaces = { # 处理非法字符所用的替换字典(根据网易云下载的文件分析得到) - "|": "|", - ":": ":", - "<": "<", - ">": ">", - "?": "?", - "/": "/", - "\\": "\", - "*": "*", - '"': """ - } - for k, v in replaces.items(): - name = name.replace(k, v) + name = sinfo["name"] + replaces = { # 处理非法字符所用的替换字典(根据网易云下载的文件分析得到) + "|": "|", + ":": ":", + "<": "<", + ">": ">", + "?": "?", + "/": "/", + "\\": "\", + "*": "*", + '"': """ + } + for k, v in replaces.items(): + name = name.replace(k, v) - print(f"歌曲:{name} - {artists}") - filename = f"{name} - {artists}.lrc" + print(f"歌曲:{name} - {artists}") + filename = f"{name} - {artists}.lrc" - try: - response = post(f"http://music.163.com/api/song/media?id={id}") - except ConnectionError: - return "dl_err_connection" - else: - info = loads(response.text) - if info["code"] == 406: # 此处与上方一样,防止因为请求限制而跳过下载 - result = wait_retry() - if result == "continue": - pass - elif result == "dl_err_connection": - return "dl_err_connection" - else: - raise Exception("Unknown exception...") + try: + response = post(f"http://music.163.com/api/song/media?id={id}") + except ConnectionError: + return "dl_err_connection" + else: + info = loads(response.text) + if info["code"] == 406: # 此处与上方一样,防止因为请求限制而跳过下载 + result = wait_retry() + if result == "continue": + pass + elif result == "dl_err_connection": + return "dl_err_connection" + else: + raise Exception("Unknown exception...") - tmp = loads(response.text) - if tmp.get("nolyric") or not tmp.get('lyric'): - print("这首歌没有歌词,跳过...") - else: - with open(f"{path}{filename}", "w", encoding="utf-8") as f: - f.write(tmp["lyric"]) - print(f"歌词下载完成!被保存在{path}{filename}") + tmp = loads(response.text) + if tmp.get("nolyric") or not tmp.get('lyric'): + print("这首歌没有歌词,跳过...") + return + else: + with open(f"{path}{filename}", "w", encoding="utf-8") as f: + f.write(tmp["lyric"]) + print(f"歌词下载完成!被保存在{path}{filename}") + return diff --git a/modules/multi_download.py b/modules/multi_download.py index 09fce9a..236edb6 100644 --- a/modules/multi_download.py +++ b/modules/multi_download.py @@ -21,7 +21,7 @@ def mdl(path: str): except ValueError: - tmp = re.search("song\?id=[0-9]*", r) + tmp = re.search(r"song\?id=[0-9]*", r) if tmp: r = tmp.group()[8:] else: diff --git a/modules/readmp3.py b/modules/readmp3.py new file mode 100644 index 0000000..852697f --- /dev/null +++ b/modules/readmp3.py @@ -0,0 +1,89 @@ +import json +import os +import re +from tinytag import TinyTag +from requests import post +from modules.get_song import get_song_lyric +from modules.clear_screen import clear + + +def load_information_from_mp3(path): + """从音乐文件中的 Comment 字段获取 163 key 并解密返回歌曲信息""" + file = TinyTag.get(path) # 使用 TinyTag 获取歌曲信息 + if file.comment: + if file.comment[:7] == "163 key": + ciphertext = file.comment[22:] + else: + return "not_support" + else: + return "not_support" + data = { + "data": ciphertext, + "type": "aes_decrypt", + "encode": "base64", + "key": "#14ljk_!\\]&0U<'(", # 感谢大佬 chenjunyu19 在 MorFans Dev 上提供的密文密钥 + "digit": 128, + "mode": "ECB", + "pad": "Pkcs5Padding" + } + try: + r = post("https://www.mklab.cn/utils/handle", data=data).text[17:-2].replace("\\\"", "\"") # 十分感谢 MKLAB 网站提供的解密接口!! + except ConnectionError: + return "dl_err_connection" + if r: + return json.loads(r) + else: + return "decrypt_failed" + + +def get_lyric_from_folder(lyric_path: str): + clear() + path = input("请输入歌曲的保存文件夹(绝对路径):").strip().replace("\\", "/") + if not os.path.exists(path): + input("路径不存在,请检查输入...") + return + if path[-1] != "/": + path += "/" + + print("正在遍历目录,请稍后...") + musics = [] + fails = 0 + for i in os.listdir(path): # 遍历目录,查找目标文件 + match = re.match(r".*\.((mp3)|(flac))$", i) + if match: + result = load_information_from_mp3(path+match.group()) + if result == "not_support": + fails += 1 + print(f"文件 \"{i}\" 未包含 163 key ,跳过") + elif result == "decrypt_failed": + fails += 1 + print(f"文件 \"{i}\" 内 163 key 解密失败,跳过") + elif result == "dl_err_connection": + input("获取解密结果失败!请检查网络连接是否正常,若确信自身没有问题请向作者反馈是否解密接口出现问题!") + return + else: + musics.append({"id": result['musicId'], "name": result["musicName"], "artists": result["artist"]}) + + # 汇报索引结果 + print(f"\n索引完毕!共找到{fails+len(musics)}个目标文件\n{len(musics)}个文件已载入\n{fails}个文件失败\n") + while True: + r = input("你希望如何保存这些歌曲的歌词?\n[1]保存到刚刚输入的绝对路径中\n[2]保存到程序设定的下载路径中\n请选择: ").strip().lower() + if r == "1": + break + elif r == "2": + path = lyric_path + break + else: + try: + input("无效选择, 若取消请按 ^C ,继续请按回车") + clear() + except KeyboardInterrupt: + return + + clear() + for i in range(0, len(musics)): # 根据索引结果获取歌词 + print("\n进度: %d/%d" % (i + 1, len(musics))) + if get_song_lyric(musics[i], path, allinfo=True) == "dl_err_connection": + input("下载发生错误!可能是连接被拒绝!请检查网络后再试\n按回车键继续任务(该任务会被跳过)...") + input("按回车键返回...") + return