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": []}
|
stats = {"success": 0, "failed": 0, "files": []}
|
||||||
kgm_decoder = None
|
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):
|
def _u32le(data, pos):
|
||||||
return struct.unpack("<I", data[pos:pos+4])[0]
|
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):
|
def _parse_gt_output(input_path, output_spec):
|
||||||
"""解析 'input>output' 重定向语法,返回 (output_path, output_fmt)。"""
|
"""解析 'input>output' 重定向语法,返回 (output_path, output_fmt)。"""
|
||||||
p = Path(input_path)
|
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:
|
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)
|
out_path = Path(spec)
|
||||||
elif "." in spec:
|
elif "." in spec:
|
||||||
# 仅文件名,放在输入文件同目录
|
# 仅文件名,放在输入文件同目录
|
||||||
@@ -84,6 +117,8 @@ def _parse_gt_output(input_path, output_spec):
|
|||||||
out_path = p.with_suffix("." + spec)
|
out_path = p.with_suffix("." + spec)
|
||||||
ext = out_path.suffix.lower().lstrip(".")
|
ext = out_path.suffix.lower().lstrip(".")
|
||||||
out_fmt = _EXT_TO_FMT.get(ext, ext)
|
out_fmt = _EXT_TO_FMT.get(ext, ext)
|
||||||
|
if not out_fmt:
|
||||||
|
out_fmt = "mp3"
|
||||||
return str(out_path), out_fmt
|
return str(out_path), out_fmt
|
||||||
|
|
||||||
def ncm_stream_bytes(key):
|
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):
|
def _convert_audio(raw, container, out_fmt, output_path):
|
||||||
"""将解密后的原始音频转码为目标格式。"""
|
"""将解密后的原始音频转码为目标格式。"""
|
||||||
src_fmt = _FORMAT_MAP.get(container, container)
|
src_fmt = _FORMAT_MAP.get(container, container)
|
||||||
|
if not src_fmt:
|
||||||
|
src_fmt = "mp3"
|
||||||
dst_fmt = _FORMAT_MAP.get(out_fmt, out_fmt)
|
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:
|
if src_fmt == dst_fmt:
|
||||||
Path(output_path).write_bytes(raw)
|
Path(output_path).write_bytes(raw)
|
||||||
else:
|
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))
|
stats["files"].append((file_path, False))
|
||||||
return
|
return
|
||||||
out_fmt = container if output_fmt == "original" else output_fmt
|
out_fmt = container if output_fmt == "original" else output_fmt
|
||||||
|
if not out_fmt:
|
||||||
|
out_fmt = "mp3"
|
||||||
if output_path is None:
|
if output_path is None:
|
||||||
output_path = _resolve_output(file_path, out_fmt)
|
output_path = _resolve_output(file_path, out_fmt)
|
||||||
try:
|
try:
|
||||||
@@ -382,9 +426,13 @@ def _process_patterns(patterns, output_fmt, delete_flag):
|
|||||||
if not input_spec:
|
if not input_spec:
|
||||||
print(" [WARN] > 重定向语法缺少输入文件: " + pat)
|
print(" [WARN] > 重定向语法缺少输入文件: " + pat)
|
||||||
continue
|
continue
|
||||||
|
if not output_spec:
|
||||||
|
print(" [WARN] > 重定向语法缺少输出: " + pat + " (使用默认格式: mp3)")
|
||||||
files = expand_files([input_spec])
|
files = expand_files([input_spec])
|
||||||
for f in files:
|
for f in files:
|
||||||
out_path, out_fmt = _parse_gt_output(f, output_spec)
|
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)
|
convert_file(f, out_fmt, delete_flag, output_path=out_path)
|
||||||
else:
|
else:
|
||||||
files = expand_files([pat])
|
files = expand_files([pat])
|
||||||
@@ -435,7 +483,7 @@ def main():
|
|||||||
while i < len(argv):
|
while i < len(argv):
|
||||||
arg = argv[i]
|
arg = argv[i]
|
||||||
if arg == "-f" and i + 1 < len(argv):
|
if arg == "-f" and i + 1 < len(argv):
|
||||||
output_fmt = argv[i + 1].lower()
|
output_fmt = _normalize_format(argv[i + 1], "mp3")
|
||||||
i += 2
|
i += 2
|
||||||
elif arg == "-d":
|
elif arg == "-d":
|
||||||
delete_flag = True
|
delete_flag = True
|
||||||
@@ -499,7 +547,7 @@ def main():
|
|||||||
i = 0
|
i = 0
|
||||||
while i < len(parts):
|
while i < len(parts):
|
||||||
if parts[i] == "-f" and i + 1 < 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
|
i += 2
|
||||||
elif parts[i] == "-d":
|
elif parts[i] == "-d":
|
||||||
delete_flag = True
|
delete_flag = True
|
||||||
|
|||||||
Reference in New Issue
Block a user