NCMDUMP UPDATE

Make menu more colorful!

Changed NCMDUMP code, 1000% faster!!

Settings update and small fix
This commit is contained in:
2023-06-22 01:36:23 +08:00
parent 94149eabea
commit bee6f61c38
7 changed files with 193 additions and 112 deletions

View File

@@ -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('<I', bytes(key_length))[0]
key_data = f.read(key_length)
key_data_array = bytearray(key_data)
for i in range(0, len(key_data_array)):
key_data_array[i] ^= 0x64
key_data = bytes(key_data_array)
cryptor = AES.new(core_key, AES.MODE_ECB)
key_data = unpad(cryptor.decrypt(key_data), 16)[17:]
key_length = len(key_data)
key_data = bytearray(key_data)
key_box = bytearray(range(256))
last_byte = 0
key_offset = 0
for i in range(256):
swap = key_box[i]
c = (swap + last_byte + key_data[key_offset]) & 0xff
key_offset += 1
if key_offset >= 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('<I', bytes(meta_length))[0]
meta_data = f.read(meta_length)
meta_data_array = bytearray(meta_data)
for i in range(0, len(meta_data_array)):
meta_data_array[i] ^= 0x63
meta_data = bytes(meta_data_array)
comment = meta_data
meta_data = b64decode(meta_data[22:])
cryptor = AES.new(meta_key, AES.MODE_ECB)
meta_data = unpad(cryptor.decrypt(meta_data), 16).decode('utf-8')[6:]
meta_data = json.loads(meta_data)
crc32 = f.read(4)
crc32 = struct.unpack('<I', bytes(crc32))[0]
..., crc32
f.seek(5, 1)
image_size = f.read(4)
image_size = struct.unpack('<I', bytes(image_size))[0]
image_data = f.read(image_size)
file_name = f.name.split("/")[-1].split(".ncm")[0] + '.' + meta_data['format']
m = open(os.path.join(target_dir, file_name), 'wb')
while True:
chunk = bytearray(f.read(0x8000))
chunk_length = len(chunk)
if not chunk:
break
for i in range(1, chunk_length + 1):
j = i & 0xff
chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j] + j) & 0xff]) & 0xff]
m.write(chunk)
m.close()
f.close()
# 对解密后的文件进行信息补全
if meta_data["format"] == "mp3": # 针对 mp3 使用 ID3 进行信息补全
audio = ID3(os.path.join(target_dir, os.path.splitext(file_path.split("/")[-1])[0] + ".mp3"))
artists = []
for i in meta_data["artist"]:
artists.append(i[0])
audio["TPE1"] = TPE1(encoding=3, text=artists) # 插入歌手
audio["APIC"] = APIC(encoding=3, mime='image/jpg', type=3, desc='', data=image_data) # 插入封面
audio["COMM::XXX"] = COMM(encoding=3, lang='XXX', desc='', text=[comment.decode("utf-8")]) # 插入 163 key 注释
audio["TIT2"] = TIT2(encoding=3, text=[meta_data["musicName"]]) # 插入歌曲名
audio["TALB"] = TALB(encoding=3, text=[meta_data["album"]]) # 插入专辑名
audio.save()
elif meta_data["format"] == "flac": # 针对 flac 使用 FLAC 进行信息补全
audio = flac.FLAC(os.path.join(target_dir, os.path.splitext(file_path.split("/")[-1])[0] + ".flac"))
artists = []
for i in meta_data["artist"]:
artists.append(i[0])
audio["artist"] = artists[:] # 插入歌手
audio["title"] = [meta_data["musicName"]] # 插入歌曲名
audio["album"] = [meta_data["album"]] # 插入专辑名
audio["description"] = comment.decode("utf-8") # 插入 163 key 注释
audio.save()
return meta_data
def process_work(path, filename, target, q_err: Queue, q_info: Queue):
def process_work(path, filename, target, lyric_format, q_err: Queue, q_info: Queue):
try:
result = load_and_decrypt_from_ncm(path, target)
result = load_and_decrypt_from_ncm(path, target, lyric_format)
except AssertionError:
q_err.put(f"\t- 文件 \"{filename}\" 破解失败!")
except KeyboardInterrupt:
os.remove(target)
exit(-1)
else:
if result == "no_meta_data":
q_err.put(f"\t- 文件 \"{filename}\"破译成功, 但是未发现有效的歌曲信息, 将不会下载该歌词")
q_info.put(result)
@@ -238,6 +154,7 @@ def get_lyric_from_folder(self):
args=(os.path.join(path, ncm_files[allocated]),
ncm_files[allocated],
target_path,
self.settings.lyric_format,
q_err,
q_info)).start()
bar.print_onto_bar(Fore.CYAN + "已分配: " + Style.RESET_ALL + "%s" % ncm_files[allocated])