feat(ffmpeg_handling): dictonary expansion, change russ search function

generate search function from translation keys
dont require perfect match for translation
expand vocabulary
This commit is contained in:
lucas committed 2026-10-27 19:45:26 +01:00
1 parent 4b4b0fcb5f
commit 22522549b5
1 file changed
+79 -82
+79 -82
View File
@@ -49,26 +49,41 @@ commands = {
} }
# ------ Russian Dictonary --------------# # ------ Russian Dictonary --------------#
AUDIO_TRANSLATIONS = { AUDIO_TRANSLATIONS = {
# Additional descriptors # Voice-over / dubbing
"Закадровый": "Voice-over", "Закадровый": "Voice-over",
"Закадровое озвучивание": "Voice-over", "Закадровое озвучивание": "Voice-over",
"Многоголосый закадровый": "Multi-voice voice-over",
"Многоголосное закадровое озвучивание": "Multi-voice voice-over",
# Variants found in metadata
"Оригинален език": "Original language",
"Многоголосное озвучивание": "Multi-voice dub",
"Многоголосый": "Multi-voice",
"Любительский": "Amateur voice-over",
"многоголосый": "Multi-voice",
"Профессиональный": "Professional",
"Одноголосый": "Single voice",
"Двухголосый": "Dual voice",
"Авторский": "Author voice-over",
"закадровый": "Voice-over", "закадровый": "Voice-over",
"Многоголосый закадровый": "Voice-over",
"Многоголосное закадровое озвучивание": "Voice-over",
"Многоголосное озвучивание": "Voice-over",
"Многоголосый": "Voice-over",
"Одноголосый": "Voice-over",
"Двухголосый": "Voice-over",
"Авторский": "Voice-over",
"Любительский": "Voice-over",
"Профессиональный": "Professional",
"Оригинал": "Original", "Оригинал": "Original",
"Дубляж": "Dubbed", "Дубляж": "Dubbed",
# Russian groups # Languages
r"русск": "Russian",
"Английски": "English",
"български": "Bulgarian",
"Български": "Bulgarian",
"Українська": "Ukrainian",
"українська": "Ukrainian",
"Македонски": "Macedonian",
"Српски": "Serbian",
"Немецкая": "German",
"Немецкое": "German",
"немецкие": "German",
"Английская": "English",
"Английское": "English",
"Французская": "French",
"Французское": "French",
# Studios / groups
"Кубик в кубе": "Kubik v Kube", "Кубик в кубе": "Kubik v Kube",
"Синема УС": "Cinema US",
"Озвучка Rumble": "Rumble",
"LostFilm": "LostFilm", "LostFilm": "LostFilm",
"AlexFilm": "AlexFilm", "AlexFilm": "AlexFilm",
"Baibako": "Baibako", "Baibako": "Baibako",
@@ -77,60 +92,42 @@ AUDIO_TRANSLATIONS = {
"Kerob": "Kerob", "Kerob": "Kerob",
"TVShows": "TVShows", "TVShows": "TVShows",
"HDRezka": "HDRezka", "HDRezka": "HDRezka",
# Groups / studios
"Синема УС": "Cinema US",
"KinoPub": "KinoPub", "KinoPub": "KinoPub",
"Озвучка Rumble": "Rumble",
"Rumble": "Rumble", "Rumble": "Rumble",
# Voice actor "супербит": "Superbit",
"Видеосервис": "Videoservice",
"Семён Гальцев": "Semyon Galtsev",
# Voice actors
"Юрий Сербин": "Yuriy Serbin", "Юрий Сербин": "Yuriy Serbin",
"Ю. Сербин": "Y. Serbin", "Ю. Сербин": "Y. Serbin",
# Languages "П.Либергал": "P. Libergal",
"Английски": "English", "Визгунов": "Vizgunov",
"Русский": "Russian", # Misc
"Русская": "Russian", # Subtitles
"Русское": "Russian", "субтитры": "Russian Subtitles",
# ISO codes "Оригинален език": "Original language",
# ISO
"rus": "Russian", "rus": "Russian",
"ru": "Russian", "ru": "Russian",
"ger": "German",
"deu": "German",
"eng": "English",
"und": "Unknown",
} }
IGNORE_WORDS = {
"English",
"German",
"French",
}
RUSSIAN_RE = re.compile( RUSSIAN_RE = re.compile(
r"\brus\b|" "|".join(
r"\bru\b|" sorted(
r"russian|" (
r"рус|" re.escape(key)
r"русский|" for key in AUDIO_TRANSLATIONS
r"русская|" if AUDIO_TRANSLATIONS[key] not in IGNORE_WORDS
r"русское|" ),
r"дубляж|" key=len,
r"оригинал|" reverse=True,
r"многоголос|" )
r"двухголос|" ),
r"одноголос|"
r"авторский|"
r"любительский|"
r"профессиональн|"
r"закадров|"
r"юрий\s+сербин|"
r"ю\.\s*сербин|"
r"сербин|"
r"синема\s+ус|"
r"kinopub|"
r"rumble|"
r"озвучка\s+rumble|"
r"кубик\s+в\s+кубе|"
r"lostfilm|"
r"alexfilm|"
r"baibako|"
r"newstudio|"
r"jaskier|"
r"kerob|"
r"tvshows|"
r"hdrezka",
re.IGNORECASE, re.IGNORECASE,
) )
@@ -175,18 +172,13 @@ def translate_audio_tracks(tracks: list[dict]) -> list[dict]:
for key, value in new_track.items(): for key, value in new_track.items():
if not isinstance(value, str): if not isinstance(value, str):
continue continue
for src, dst in AUDIO_TRANSLATIONS.items(): for src, dst in AUDIO_TRANSLATIONS.items():
value = re.sub( if re.search(rf"\b{re.escape(src)}\b", value, flags=re.IGNORECASE):
rf"\b{re.escape(src)}\b", value = dst
dst, break
value,
flags=re.IGNORECASE,
)
if CYRILLIC_RE.search(value): if CYRILLIC_RE.search(value):
raise ValueError( raise ValueError(
f"Contains unidentified Cyrillic characters in {key}: {value}" f"Track contains unidentified Cyrillic characters in {key}: {value}"
) )
new_track[key] = value new_track[key] = value
@@ -520,7 +512,10 @@ def cmd_convert_video(args):
) )
return True, out return True, out
from collections.abc import Callable from collections.abc import Callable
def remove_streams( def remove_streams(
args, args,
remove_indices: set[int] | list[int], remove_indices: set[int] | list[int],
@@ -533,8 +528,7 @@ def remove_streams(
remaining_audio = [ remaining_audio = [
s s
for s in probe["streams"] for s in probe["streams"]
if s["codec_type"] == "audio" if s["codec_type"] == "audio" and s["index"] not in remove_indices
and s["index"] not in remove_indices
] ]
if not remaining_audio: if not remaining_audio:
@@ -618,14 +612,13 @@ def remove_streams(
logger.error("STDERR:\n%s", e.stderr) logger.error("STDERR:\n%s", e.stderr)
raise raise
def cmd_remove_subs(args): def cmd_remove_subs(args):
probe = ffmpeg.probe(str(args.input)) probe = ffmpeg.probe(str(args.input))
if args.num is None: if args.num is None:
remove_indices = { remove_indices = {
s["index"] s["index"] for s in probe["streams"] if s["codec_type"] == "subtitle"
for s in probe["streams"]
if s["codec_type"] == "subtitle"
} }
else: else:
remove_indices = set(args.num) remove_indices = set(args.num)
@@ -635,10 +628,10 @@ def cmd_remove_subs(args):
remove_indices=remove_indices, remove_indices=remove_indices,
) )
def cmd_remove_audio_track(args): def cmd_remove_audio_track(args):
remove_indices = { remove_indices = {
select_audio_stream(args.input, audio_index) select_audio_stream(args.input, audio_index) for audio_index in args.audio_index
for audio_index in args.audio_index
} }
return remove_streams( return remove_streams(
@@ -646,6 +639,8 @@ def cmd_remove_audio_track(args):
remove_indices=remove_indices, remove_indices=remove_indices,
output_subtitle_codec=True, output_subtitle_codec=True,
) )
def cmd_extract_audio_track(args): def cmd_extract_audio_track(args):
""" """
1. Saves that specific audio track as a standalone MP3 (Extra Output). 1. Saves that specific audio track as a standalone MP3 (Extra Output).
@@ -929,6 +924,7 @@ def cmd_set_primary_audio(args):
return True, out return True, out
def cmd_show_metadata(args, show_metadata=False, return_tags=None): def cmd_show_metadata(args, show_metadata=False, return_tags=None):
probe = ffmpeg.probe(str(args.input)) probe = ffmpeg.probe(str(args.input))
format_tags = probe.get("format", {}).get("tags", {}) format_tags = probe.get("format", {}).get("tags", {})
@@ -942,7 +938,6 @@ def cmd_show_metadata(args, show_metadata=False, return_tags=None):
for k, v in format_tags.items(): for k, v in format_tags.items():
logger.info(f"{k}: {v}") logger.info(f"{k}: {v}")
# logger.info(f"\n=== STREAM METADATA ===") # logger.info(f"\n=== STREAM METADATA ===")
# for i, stream in enumerate(probe.get("streams", [])): # for i, stream in enumerate(probe.get("streams", [])):
# logger.info(f"\n--- Stream {i} ({stream.get('codec_type')}) ---") # logger.info(f"\n--- Stream {i} ({stream.get('codec_type')}) ---")
@@ -1113,8 +1108,8 @@ def cmd_remove_russian_from_db(args):
try: try:
audio_tracks = translate_audio_tracks(get_audio_tracks(file)) audio_tracks = translate_audio_tracks(get_audio_tracks(file))
subtitle_tracks = translate_audio_tracks(get_subtitle_tracks(file)) subtitle_tracks = translate_audio_tracks(get_subtitle_tracks(file))
except ValueError as e: except ValueError as error_msg:
logger.warning("%s", e) logger.warning(f"File {file.name} {error_msg}")
return False, "nooutputneeded" return False, "nooutputneeded"
logger.trace("Remove Russian from File: %s", file.name) logger.trace("Remove Russian from File: %s", file.name)
@@ -1179,7 +1174,7 @@ def cmd_test(args):
_test_dict = [] _test_dict = []
logger.info(f"TEST: {name}") logger.info(f"TEST: {name}")
# Run FFmpeg for this single command only # Run FFmpeg for this single command only
return_value ,outputs = cmd_func(args_probe) return_value, outputs = cmd_func(args_probe)
if not outputs: if not outputs:
logger.error("No outputs returned.") logger.error("No outputs returned.")
return False return False
@@ -1290,7 +1285,7 @@ def cmd_test(args):
args = ArgsBuffer() args = ArgsBuffer()
args.input = file_path args.input = file_path
tags = {} tags = {}
cmd_show_metadata(args,return_tags=tags) cmd_show_metadata(args, return_tags=tags)
return_value = True return_value = True
@@ -1409,7 +1404,9 @@ def cmd_test(args):
logger.debug(f"Subtitle tracks: {subtitle_tracks}") logger.debug(f"Subtitle tracks: {subtitle_tracks}")
for subtitle_track in subtitle_tracks: for subtitle_track in subtitle_tracks:
if is_russian_track(subtitle_track): if is_russian_track(subtitle_track):
logger.error(f"Found russian track: {subtitle_track} in file {file_path}") logger.error(
f"Found russian track: {subtitle_track} in file {file_path}"
)
return return_value and False return return_value and False
logger.info(f"{green_tag('PASS')} {file_path}") logger.info(f"{green_tag('PASS')} {file_path}")