fix(ffmpeg handling): add language tag to is russian identification

This commit is contained in:
lucas committed 2026-10-26 13:00:58 +01:00
1 parent d43fcd530c
commit b672696f91
2 files changed
+89 -63

No files matched your search

+87 -63
View File
@@ -136,8 +136,11 @@ RUSSIAN_RE = re.compile(
)
def is_russian_track(description: str) -> bool:
return bool(RUSSIAN_RE.search(description))
def is_russian_track(track: dict) -> bool:
descr = bool(RUSSIAN_RE.search(track["description"]))
language = bool(RUSSIAN_RE.search(track["language"]))
lang = bool(RUSSIAN_RE.search(track["lang"]))
return descr or lang or language
class ArgsBuffer:
@@ -147,7 +150,7 @@ class ArgsBuffer:
loud: bool | None = False
key: str | None = None
value: str | None = None
audio_index: int | None = None
audio_index: list | None = None
dry_run: bool | None = None
audio_codec: str | None = None
video_codec: str | None = None
@@ -168,27 +171,28 @@ def translate_audio_tracks(tracks: list[dict]) -> list[dict]:
translated = []
for track in tracks:
description = track.get("description", "")
new_track = track.copy()
for src, dst in AUDIO_TRANSLATIONS.items():
description = re.sub(
rf"\b{re.escape(src)}\b",
dst,
description,
flags=re.IGNORECASE,
)
for key, value in new_track.items():
if not isinstance(value, str):
continue
if CYRILLIC_RE.search(description):
raise ValueError(
f"Contains unidentified Cyrillic characters: {description}"
)
for src, dst in AUDIO_TRANSLATIONS.items():
value = re.sub(
rf"\b{re.escape(src)}\b",
dst,
value,
flags=re.IGNORECASE,
)
translated.append(
{
**track,
"description": description,
}
)
if CYRILLIC_RE.search(value):
raise ValueError(
f"Contains unidentified Cyrillic characters in {key}: {value}"
)
new_track[key] = value
translated.append(new_track)
return translated
@@ -210,17 +214,17 @@ def ensure(value, prompt):
return value
def move_override_file(files_move_from, files_move_to, args):
def move_overwrite_file(files_move_from, files_move_to, args):
backup_files = args.backup
silent = not args.loud
for file_to, file_from in zip(files_move_to, files_move_from):
if not file_to.is_file():
logger.error(f"Override Target File does not exist: {file_to}")
logger.error(f"overwrite Target File does not exist: {file_to}")
return False
if not file_from.is_file():
logger.trace(f"Override File does not exist: {file_from}")
logger.trace(f"overwrite File does not exist: {file_from}")
continue
backup = file_to.parent / f"{file_to.stem}_backup{file_from.suffix}"
@@ -229,7 +233,7 @@ def move_override_file(files_move_from, files_move_to, args):
logger.trace(f"Move original to backup: {backup}")
safe_rename(file_to, backup, silent=silent)
# move override -> original name
# move overwrite -> original name
retstr, ret = safe_rename(file_from, file_to, silent=silent, overwrite=True)
logger.trace(retstr)
if not ret:
@@ -584,13 +588,14 @@ def cmd_remove_audio_track(args):
"""
probe = ffmpeg.probe(str(args.input))
remove_idx = select_audio_stream(args.input, args.audio_index)
remove_indices = []
for audio_index in args.audio_index:
remove_indices.append(select_audio_stream(args.input, audio_index))
remaining_audio = [
s
for s in probe["streams"]
if s["codec_type"] == "audio" and s["index"] != remove_idx
if s["codec_type"] == "audio" and s["index"] not in remove_indices
]
if not remaining_audio:
logger.error("No audiotracks remaining")
@@ -598,7 +603,7 @@ def cmd_remove_audio_track(args):
remaining_audio = translate_audio_tracks(remaining_audio)
removed_was_default = any(
s["index"] == remove_idx and s.get("disposition", {}).get("default", 0) == 1
s["index"] in remove_indices and s.get("disposition", {}).get("default", 0) == 1
for s in probe["streams"]
)
@@ -610,7 +615,7 @@ def cmd_remove_audio_track(args):
]
for stream in probe["streams"]:
if stream["index"] != remove_idx:
if stream["index"] not in remove_indices:
cmd.extend(["-map", f"0:{stream['index']}"])
cmd.extend(
@@ -671,19 +676,24 @@ def cmd_extract_audio_track(args):
output_audio = Path(args.output)
output_audio = str(output_audio.with_name(f"{output_audio.stem}.mp3"))
remove_idx = select_audio_stream(args.input, args.audio_index)
outputs = []
for index in args.audio_index:
remove_idx = select_audio_stream(args.input, index)
extracted_audio = None
extracted_audio = None
for s in probe["streams"]:
idx = s["index"]
if idx == remove_idx:
extracted_audio = inp[str(idx)]
for s in probe["streams"]:
idx = s["index"]
if idx == remove_idx:
extracted_audio = inp[str(idx)]
out = ffmpeg.output(
extracted_audio, output_audio, acodec="libmp3lame", audio_bitrate="192k"
)
return True, out
outputs.append(
ffmpeg.output(
extracted_audio, output_audio, acodec="libmp3lame", audio_bitrate="192k"
)
)
return True, outputs
def cmd_downsize(args):
@@ -736,8 +746,14 @@ def cmd_filter_audio(args):
5. alimiter: Hard limit at 0.95 to prevent digital clipping (distortion).
6. dynaudnorm: Smooths out loudness levels over time.
"""
if len(args.audio_index) > 1:
logger.warning(
f"Filter Audio only filters the first element of list audio_index: {args.audio_index}"
)
idx_filt = select_audio_stream(args.input, args.audio_index)
index = args.audio_index[0]
idx_filt = select_audio_stream(args.input, index)
inp = ffmpeg.input(args.input)
probe = ffmpeg.probe(str(args.input))
@@ -888,7 +904,13 @@ def cmd_set_primary_audio(args):
inp = ffmpeg.input(args.input)
primary_audio = select_audio_stream(args.input, args.audio_index)
if len(args.audio_index) > 1:
logger.warning(
f"Set primary can only set the first element of list audio_index: {args.audio_index}"
)
index = args.audio_index[0]
primary_audio = select_audio_stream(args.input, index)
probe = ffmpeg.probe(str(args.input))
audio_streams = [
@@ -1111,17 +1133,17 @@ def cmd_remove_russian_from_db(args_in):
logger.warning("%s", e)
return False, "nooutputneeded"
logger.trace(f"File: {file.name}: Audio tracks: {audio_tracks}")
logger.trace(f"Remove Russian from File: {file.name}")
logger.trace(f"With Audio tracks: {audio_tracks}")
remove_tracks = []
for track in audio_tracks:
if is_russian_track(track["description"]):
if is_russian_track(track):
remove_tracks.append(track["number"])
logger.debug(f"Removing russian track {track['number']} from {file}")
for number in remove_tracks:
args = args_in
args.audio_index = number
cmd_remove_audio_track(args)
args = args_in
args.audio_index = remove_tracks
if remove_tracks:
cmd_remove_audio_track(args)
return True, "nooutputneeded"
else:
return False, "nooutputneeded"
@@ -1428,7 +1450,7 @@ def cmd_test(args):
cmd = commands["remove-audio"]
if cmd["name"] in args.choices:
args_test = ArgsBuffer()
args_test.audio_index = 1
args_test.audio_index = [1]
for test_file in test_files:
video_streams, audio_streams, subtitle_streams, cover_image = get_streams(
@@ -1454,7 +1476,7 @@ def cmd_test(args):
cmd = commands["extract-audio"]
if cmd["name"] in args.choices:
args_test = ArgsBuffer()
args_test.audio_index = 1
args_test.audio_index = [1]
for test_file in test_files:
args_test.input = test_file
@@ -1587,7 +1609,7 @@ def cmd_test(args):
Path(test_dir)
/ f"out_{str(cmd['name']).replace('-', '_')}_{Path(test_file).stem}{Path(test_file).suffix}"
)
args_test.audio_index = 1
args_test.audio_index = [1]
test_dict = [
{
"video_count": len(video_streams),
@@ -1640,7 +1662,7 @@ def cmd_test(args):
Path(test_dir)
/ f"out_{str(cmd['name']).replace('-', '_')}_{Path(test_file).stem}{Path(test_file).suffix}"
)
args_test.audio_index = 2
args_test.audio_index = [2]
test_dict = [
{
"video_count": len(video_streams),
@@ -1771,6 +1793,7 @@ def add_audio_index(parser):
"-a",
"--audio-index",
type=int,
nargs="+",
help="Audio track index",
)
@@ -1789,7 +1812,7 @@ def main():
base_p.add_argument("input", nargs="?", type=Path, help="Input file or directory")
base_p.add_argument("-o", "--output", type=Path, help="Output file")
base_p.add_argument("--override", action="store_true", help="Overwrite input")
base_p.add_argument("--overwrite", action="store_true", help="Overwrite input")
base_p.add_argument("--backup", action="store_true", help="Backup original")
base_p.add_argument("--loud", action="store_true", help="Show ffmpeg output")
base_p.add_argument(
@@ -2103,9 +2126,9 @@ def main():
parser.error(f"Input file {args.input} does not exist!")
if commands[args.command]["output"] and not getattr(args, "output", None):
input_path = Path(args.input)
if args.override:
if args.overwrite:
args.output = input_path.with_name(
f"{input_path.stem}_override{input_path.suffix}"
f"{input_path.stem}_overwrite{input_path.suffix}"
)
else:
args.output = ensure(
@@ -2158,9 +2181,10 @@ def main():
"extract-audio",
"set-primary-audio",
]:
args.audio_index = ensure(getattr(args, "audio_index", None), "Audio index")
index = str(ensure(getattr(args, "audio_index", None), "Audio index"))
args.audio_index = [int(x) for x in re.split(r"[,\s]+", index.strip()) if x]
if args.audio_index is None:
args.audio_index = 1
args.audio_index = [1]
if args.command == "chapters":
args.chapter_file = ensure(
@@ -2212,8 +2236,8 @@ def main():
continue
logger.trace(f"Processing File: {file}")
args.input = Path(file)
if args.override:
out_path = file.with_name(f"{file.stem}_override{file.suffix}")
if args.overwrite:
out_path = file.with_name(f"{file.stem}_overwrite{file.suffix}")
else:
out_path = file.with_name(
f"{file.stem}_{args.command}{file.suffix}"
@@ -2223,13 +2247,13 @@ def main():
return_value, output_str = args.func(args)
if output_str != "nooutputneeded":
outputs.append(output_str)
if args.override and return_value:
if args.overwrite and return_value:
files_move_from.append(out_path)
files_move_to.append(file)
counter += 1
# ---- SINGLE FILE ----
elif input_path.is_file():
return_value,output_str = args.func(args)
return_value, output_str = args.func(args)
if output_str != "nooutputneeded":
outputs.append(output_str)
else:
@@ -2245,10 +2269,10 @@ def main():
run_ffmpeg(
ffmpeg.merge_outputs(*outputs), overwrite=True, loud=args.loud
)
if args.override and not move_override_file(
if args.overwrite and not move_overwrite_file(
files_move_from, files_move_to, args
):
logger.error("move_override failed")
logger.error("move_overwrite failed")
sys.exit(1)
except ffmpeg.Error as e:
logger.error("FFmpeg failed.\n")
+2
View File
@@ -223,6 +223,8 @@ def get_audio_tracks(
or stream.get("tags", {}).get("language")
or f"Track {stream['index']}"
),
"lang": stream.get("lang", ""),
"language": stream.get("tags", {}).get("language", ""),
"primary": stream.get("codec_type") == "audio"
and stream.get("disposition", {}).get("default") == 1,
}