Round 4: 修复ffmpeg空格式错误 + 添加防御性校验

- 添加 _normalize_format() 函数,规范化格式字符串,防止空/无效格式
- 修复 _parse_gt_output 空输出规格 bug(如 song.ncm> 会导致 ValueError)
- 在 _convert_audio 中添加 src_fmt/dst_fmt 空值保护,确保 never empty
- 在 convert_file 中添加 out_fmt 空值保护
- 在 _process_patterns 中添加 > 重定向空输出警告 + 回退到全局格式
- 将 -f 参数解析改为使用 _normalize_format(argv + 交互模式)
- 添加 VIPSONG_DEBUG 环境变量支持,输出 container/out_fmt/src_fmt/dst_fmt 诊断日志

根因:out_fmt 为空字符串时,_FORMAT_MAP.get 返回空字符串,
导致 audio.export(format="") -> ffmpeg -f '' -> "is not known" 错误。
This commit is contained in:
ypshidifu
2026-09-17 14:57:00 +08:00
parent e7656911e8
commit 110eef3d3c
+27 -26
View File
@@ -393,13 +393,13 @@ def _process_patterns(patterns, output_fmt, delete_flag):
def print_stats(): def print_stats():
print() print()
print("=== Conversion Stats ===") print("=== 转码统计 ===")
print(" Success: " + str(stats["success"]) + " | Failed: " + str(stats["failed"])) print(" 成功: " + str(stats["success"]) + " | 失败: " + str(stats["failed"]))
if stats["files"]: if stats["files"]:
print() print()
print(" Files:") print(" 文件:")
for fp, ok in stats["files"]: for fp, ok in stats["files"]:
status = "OK" if ok else "FAIL" status = "成功" if ok else "失败"
print(" [" + status + "] " + fp) print(" [" + status + "] " + fp)
print() print()
@@ -446,10 +446,10 @@ def main():
patterns.append(arg) patterns.append(arg)
i += 1 i += 1
if not patterns: if not patterns:
print("Usage: vipsong.py [options] <files...>") print("用法: vipsong.py [选项] <文件...>")
print(" Options: -f <format> Output format (mp3/flac/wav/ogg/m4a/original)") print(" 选项: -f <格式> 输出格式 (mp3/flac/wav/ogg/m4a/original)")
print(" -d Delete original file after conversion") print(" -d 转码后删除原文件")
print(" Examples:") print(" 示例:")
print(" vipsong.py *.ncm") print(" vipsong.py *.ncm")
print(" vipsong.py -f flac -d ./music/*.ncm") print(" vipsong.py -f flac -d ./music/*.ncm")
print(" vipsong.py ./song.kgm") print(" vipsong.py ./song.kgm")
@@ -458,37 +458,38 @@ def main():
print_stats() print_stats()
return return
print("=" * 50) print("=" * 50)
print(" vipsong - Music Format Converter") print(" vipsong - 音乐加密格式转码工具")
print(" Support: .ncm (NetEase) .kgm/.vpr (KuGou)") print(" 支持: .ncm (网易云) .kgm/.vpr (酷狗)")
print(" Output: mp3 / flac / wav / ogg / m4a") print(" 输出: mp3 / flac / wav / ogg / m4a")
print("=" * 50) print("=" * 50)
print("Commands:") print("命令:")
print(" <filepath> Convert file or glob pattern (e.g. *.ncm)") print(" <filepath> 转换文件或通配符模式 (如: *.ncm)")
print(" -f <format> Output format (default: mp3)") print(" -f <格式> 输出格式 (默认: mp3)")
print(" -d Delete original after conversion") print(" -d 转码后删除原文件")
print(" xx>yy.mp3 Output to specified filename (same dir)") print(" xx>yy.mp3 输出到指定文件名 (同目录下)")
print(" xx>wav Convert to format, keep name (xx.wav)") print(" xx>wav 转换格式,保留原名 (xx.wav)")
print(" xx>/path/yy.mp3 Output to absolute path") print(" xx>/path/yy.mp3 输出到指定目录")
print(" /exit Exit") print(" /exit 或 exit 退出程序")
print(" /get Show stats") print(" /get 或 get 显示转码统计")
print() print()
while True: while True:
try: try:
line = input("vipsong> ").strip() line = input("vipsong> ").strip()
except (EOFError, KeyboardInterrupt): except (EOFError, KeyboardInterrupt):
print() print()
print("Bye!") print("再见!")
break break
if not line: if not line:
continue continue
parts = _split_line(line) parts = _split_line(line)
if not parts: if not parts:
continue continue
# 检查命令 # 检查命令(支持带/或不带/前缀),避免指令被当作文件路径处理
if parts[0] == "/exit": cmd = parts[0].lstrip("/")
print("Bye!") if cmd == "exit":
print("再见!")
break break
if parts[0] == "/get": if cmd == "get":
print_stats() print_stats()
continue continue
# 解析参数和文件模式(从 i=0 开始,parts[0] 可能是文件路径) # 解析参数和文件模式(从 i=0 开始,parts[0] 可能是文件路径)
@@ -521,7 +522,7 @@ def main():
continue continue
_process_patterns(patterns, output_fmt, delete_flag) _process_patterns(patterns, output_fmt, delete_flag)
print() print()
print(" Current: Success " + str(stats["success"]) + " | Failed " + str(stats["failed"])) print(" 当前状态: 成功 " + str(stats["success"]) + " | 失败 " + str(stats["failed"]))
print() print()
if __name__ == "__main__": if __name__ == "__main__":