From e24966935efe714835c1f766036cb155b3c0630b Mon Sep 17 00:00:00 2001 From: ypshidifu Date: Thu, 17 Sep 2026 15:07:51 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20>=20=E9=87=8D=E5=AE=9A=E5=90=91=E7=9B=AE?= =?UTF-8?q?=E5=BD=95+=E6=A0=BC=E5=BC=8F=E8=AF=AD=E6=B3=95=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:输入 'dir/*.ncm>../.ogg' 时,所有文件输出到同一路径 '../.ogg', 覆盖互相,导致 17 个文件都写入同一个空白路径。 根因:_parse_gt_output 将包含路径分隔符的输出规格 '../.ogg' 直接当作 完整文件路径处理,未识别出 '..' 是目录、'.ogg' 是格式扩展名。 修复:当输出规格包含路径分隔符时,检查分隔符后的部分: - 若为 '.ext' 格式(如 '.ogg' '._mp3')→ 视为 目录+格式,为每个输入文件 生成独立输出路径 /. - 若为裸格式名(如 'ogg') → 同样视为目录+格式 - 若为普通文件名(如 'yy.mp3') → 保持原有字面路径行为 - 若仅目录(如 '/tmp/') → 使用原文件名 + mp3 扩展名 提交哈希: --- vipsong.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/vipsong.py b/vipsong.py index 08f179d..73a4ff1 100644 --- a/vipsong.py +++ b/vipsong.py @@ -37,6 +37,18 @@ _EXT_TO_FMT = { stats = {"success": 0, "failed": 0, "files": []} kgm_decoder = None +DEBUG = bool(os.environ.get("VIPSONG_DEBUG", "")) + +def _normalize_format(fmt, default="mp3"): + """规范化格式字符串,确保非空且可识别。""" + if not fmt or not isinstance(fmt, str) or not fmt.strip(): + return _FORMAT_MAP.get(default.lower(), "mp3") + fmt = fmt.strip().lower() + if fmt in _EXT_TO_FMT: + return _EXT_TO_FMT[fmt] + if fmt in _FORMAT_MAP: + return _FORMAT_MAP[fmt] + return default def _u32le(data, pos): return struct.unpack("output' 重定向语法,返回 (output_path, output_fmt)。""" p = Path(input_path) - spec = output_spec - # 如果包含路径分隔符,作为完整路径 + spec = output_spec.strip() + if not spec: + # 空输出规格(如 'song.ncm>'),使用默认 mp3 格式 + out_path = p.with_suffix(".mp3") + return str(out_path), "mp3" + # 如果包含路径分隔符,判断是目录+格式还是具体文件路径 if "/" in spec or "\\" in spec: - out_path = Path(spec) + last_sep = max(spec.rfind("/"), spec.rfind("\\")) + remainder = spec[last_sep + 1:].strip() + if not remainder: + # 仅目录(如 '/path/'),使用原文件名 + mp3 扩展名 + out_path = Path(spec) / (p.stem + ".mp3") + elif remainder.startswith(".") and remainder[1:].lower() in _EXT_TO_FMT: + # 目录 + 格式扩展(如 '../.ogg'),输出到该目录 + ext = remainder[1:].lower() + dir_part = spec[:last_sep + 1] + out_path = Path(dir_part) / (p.stem + "." + ext) + elif remainder.lower() in _EXT_TO_FMT: + # 目录 + 裸格式名(如 '/path/ogg'),输出到该目录 + ext = _extension_for(_EXT_TO_FMT[remainder.lower()]) + dir_part = spec[:last_sep + 1] + out_path = Path(dir_part) / (p.stem + "." + ext) + else: + # 具体文件路径(如 '/path/yy.mp3') + out_path = Path(spec) elif "." in spec: # 仅文件名,放在输入文件同目录 out_path = p.parent / spec @@ -84,6 +117,8 @@ def _parse_gt_output(input_path, output_spec): out_path = p.with_suffix("." + spec) ext = out_path.suffix.lower().lstrip(".") out_fmt = _EXT_TO_FMT.get(ext, ext) + if not out_fmt: + out_fmt = "mp3" return str(out_path), out_fmt def ncm_stream_bytes(key): @@ -282,7 +317,14 @@ def _add_tags(output_path, tags, cover=None): def _convert_audio(raw, container, out_fmt, output_path): """将解密后的原始音频转码为目标格式。""" src_fmt = _FORMAT_MAP.get(container, container) + if not src_fmt: + src_fmt = "mp3" dst_fmt = _FORMAT_MAP.get(out_fmt, out_fmt) + if not dst_fmt: + dst_fmt = "mp3" + if DEBUG: + print(" [DEBUG] container=" + str(container) + " out_fmt=" + str(out_fmt) + + " src_fmt=" + str(src_fmt) + " dst_fmt=" + str(dst_fmt)) if src_fmt == dst_fmt: Path(output_path).write_bytes(raw) else: @@ -334,6 +376,8 @@ def convert_file(file_path, output_fmt="mp3", delete_flag=False, output_path=Non stats["files"].append((file_path, False)) return out_fmt = container if output_fmt == "original" else output_fmt + if not out_fmt: + out_fmt = "mp3" if output_path is None: output_path = _resolve_output(file_path, out_fmt) try: @@ -382,9 +426,13 @@ def _process_patterns(patterns, output_fmt, delete_flag): if not input_spec: print(" [WARN] > 重定向语法缺少输入文件: " + pat) continue + if not output_spec: + print(" [WARN] > 重定向语法缺少输出: " + pat + " (使用默认格式: mp3)") files = expand_files([input_spec]) for f in files: out_path, out_fmt = _parse_gt_output(f, output_spec) + if not out_fmt: + out_fmt = output_fmt convert_file(f, out_fmt, delete_flag, output_path=out_path) else: files = expand_files([pat]) @@ -435,7 +483,7 @@ def main(): while i < len(argv): arg = argv[i] if arg == "-f" and i + 1 < len(argv): - output_fmt = argv[i + 1].lower() + output_fmt = _normalize_format(argv[i + 1], "mp3") i += 2 elif arg == "-d": delete_flag = True @@ -499,7 +547,7 @@ def main(): i = 0 while i < len(parts): if parts[i] == "-f" and i + 1 < len(parts): - output_fmt = parts[i + 1].lower() + output_fmt = _normalize_format(parts[i + 1], "mp3") i += 2 elif parts[i] == "-d": delete_flag = True