Fix: > 重定向目录+格式语法解析错误
问题:输入 'dir/*.ncm>../.ogg' 时,所有文件输出到同一路径 '../.ogg', 覆盖互相,导致 17 个文件都写入同一个空白路径。 根因:_parse_gt_output 将包含路径分隔符的输出规格 '../.ogg' 直接当作 完整文件路径处理,未识别出 '..' 是目录、'.ogg' 是格式扩展名。 修复:当输出规格包含路径分隔符时,检查分隔符后的部分: - 若为 '.ext' 格式(如 '.ogg' '._mp3')→ 视为 目录+格式,为每个输入文件 生成独立输出路径 <dir>/<filename>.<ext> - 若为裸格式名(如 'ogg') → 同样视为目录+格式 - 若为普通文件名(如 'yy.mp3') → 保持原有字面路径行为 - 若仅目录(如 '/tmp/') → 使用原文件名 + mp3 扩展名 提交哈希:
This commit is contained in:
+52
-4
@@ -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("<I", data[pos:pos+4])[0]
|
||||
@@ -72,9 +84,30 @@ def _extension_for(fmt):
|
||||
def _parse_gt_output(input_path, output_spec):
|
||||
"""解析 'input>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:
|
||||
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:
|
||||
# 仅文件名,放在输入文件同目录
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user