修复 vipsong.py 多处错误
1. 修复 Line 277 语法错误:output_path")" 多余引号导致的 SyntaxError
2. 添加缺失的 META_KEY 常量(NCM 元数据 AES 密钥,16 字节)
3. 添加缺失的 KUGOU_KEY_PATH 常量(指向 kugou_key.xz)
4. 添加缺失的 stats 和 kgm_decoder 模块级初始化
5. 添加缺失的 import io(用于 _convert_audio 的 BytesIO)
6. 添加缺失的 _sniff_container 函数(音频容器魔术字节检测)
7. 添加缺失的 _extension_for 函数(格式到扩展名映射)
8. 添加缺失的 _convert_audio 函数(原始音频转码)
9. 修复 print("\n") 为 print()(恢复更清洁的输出格式)
This commit is contained in:
+55
-3
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os, sys, glob, struct, base64, json
|
import os, sys, glob, struct, base64, json, io
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
@@ -12,11 +12,53 @@ from pydub import AudioSegment
|
|||||||
|
|
||||||
NCM_MAGIC = b"CTENFDAM"
|
NCM_MAGIC = b"CTENFDAM"
|
||||||
CORE_KEY = b"hzHRAmso5kInbaxW"
|
CORE_KEY = b"hzHRAmso5kInbaxW"
|
||||||
|
META_KEY = bytes.fromhex("2331346C6A6B5F215C5D2630553C2728")
|
||||||
META_PREFIX = b"163 key(D't modify):"
|
META_PREFIX = b"163 key(D't modify):"
|
||||||
|
KUGOU_KEY_PATH = Path(__file__).parent / "kugou_key.xz"
|
||||||
|
|
||||||
|
_FORMAT_MAP = {
|
||||||
|
"mp3": "mp3",
|
||||||
|
"flac": "flac",
|
||||||
|
"wav": "wav",
|
||||||
|
"ogg": "ogg",
|
||||||
|
"m4a": "mp4",
|
||||||
|
"mp4": "mp4",
|
||||||
|
}
|
||||||
|
|
||||||
|
stats = {"success": 0, "failed": 0, "files": []}
|
||||||
|
kgm_decoder = None
|
||||||
|
|
||||||
def _u32le(data, pos):
|
def _u32le(data, pos):
|
||||||
return struct.unpack(b"<I", data[pos:pos+4])[0]
|
return struct.unpack(b"<I", data[pos:pos+4])[0]
|
||||||
|
|
||||||
|
def _sniff_container(header):
|
||||||
|
"""从魔术字节检测音频容器类型。"""
|
||||||
|
if len(header) >= 3 and header[:3] == b"ID3":
|
||||||
|
return "mp3"
|
||||||
|
if len(header) >= 2 and header[0] == 0xFF and (header[1] & 0xE0) == 0xE0:
|
||||||
|
return "mp3"
|
||||||
|
if len(header) >= 4 and header[:4] == b"fLaC":
|
||||||
|
return "flac"
|
||||||
|
if len(header) >= 4 and header[:4] == b"OggS":
|
||||||
|
return "ogg"
|
||||||
|
if len(header) >= 12 and header[:4] == b"RIFF" and header[8:12] == b"WAVE":
|
||||||
|
return "wav"
|
||||||
|
if len(header) >= 12 and header[4:8] == b"ftyp":
|
||||||
|
return "mp4"
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _extension_for(fmt):
|
||||||
|
"""将格式名称映射为文件后缀。"""
|
||||||
|
mapping = {
|
||||||
|
"mp3": "mp3",
|
||||||
|
"flac": "flac",
|
||||||
|
"wav": "wav",
|
||||||
|
"ogg": "ogg",
|
||||||
|
"m4a": "m4a",
|
||||||
|
"mp4": "m4a",
|
||||||
|
}
|
||||||
|
return mapping.get(fmt, "mp3")
|
||||||
|
|
||||||
def ncm_stream_bytes(key):
|
def ncm_stream_bytes(key):
|
||||||
sbox = bytearray(range(256))
|
sbox = bytearray(range(256))
|
||||||
j = 0
|
j = 0
|
||||||
@@ -210,6 +252,16 @@ def _add_tags(output_path, tags, cover=None):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _convert_audio(raw, container, out_fmt, output_path):
|
||||||
|
"""将解密后的原始音频转码为目标格式。"""
|
||||||
|
src_fmt = _FORMAT_MAP.get(container, container)
|
||||||
|
dst_fmt = _FORMAT_MAP.get(out_fmt, out_fmt)
|
||||||
|
if src_fmt == dst_fmt:
|
||||||
|
Path(output_path).write_bytes(raw)
|
||||||
|
else:
|
||||||
|
audio = AudioSegment.from_file(io.BytesIO(raw), format=src_fmt)
|
||||||
|
audio.export(output_path, format=dst_fmt)
|
||||||
|
|
||||||
def convert_file(file_path, output_fmt, delete_flag):
|
def convert_file(file_path, output_fmt, delete_flag):
|
||||||
global stats, kgm_decoder
|
global stats, kgm_decoder
|
||||||
file_path = str(Path(file_path).resolve())
|
file_path = str(Path(file_path).resolve())
|
||||||
@@ -339,11 +391,11 @@ def main():
|
|||||||
convert_file(f, output_fmt, delete_flag)
|
convert_file(f, output_fmt, delete_flag)
|
||||||
print_stats()
|
print_stats()
|
||||||
return
|
return
|
||||||
print("=* 50")
|
print("=" * 50)
|
||||||
print(" vipsong - Music Format Converter")
|
print(" vipsong - Music Format Converter")
|
||||||
print(" Support: .ncm (NetEase) .kgm/.vpr (KuGou)")
|
print(" Support: .ncm (NetEase) .kgm/.vpr (KuGou)")
|
||||||
print(" Output: mp3 / flac / wav / ogg / m4a")
|
print(" Output: mp3 / flac / wav / ogg / m4a")
|
||||||
print("=* 50")
|
print("=" * 50)
|
||||||
print("Commands:")
|
print("Commands:")
|
||||||
print(" <filepath> Convert file or glob pattern (e.g. *.ncm)")
|
print(" <filepath> Convert file or glob pattern (e.g. *.ncm)")
|
||||||
print(" -f <format> Output format (default: mp3)")
|
print(" -f <format> Output format (default: mp3)")
|
||||||
|
|||||||
Reference in New Issue
Block a user