MattyMroz's picture
mm_avh_working_space
068ed60
from msvcrt import getch
from os import listdir, makedirs, path
from shutil import rmtree
from typing import Dict, List
from natsort import natsorted
from constants import (WORKING_SPACE,
WORKING_SPACE_OUTPUT,
WORKING_SPACE_TEMP,
WORKING_SPACE_TEMP_MAIN_SUBS,
WORKING_SPACE_TEMP_ALT_SUBS,
console)
from data.settings import Settings
from modules.mkvtoolnix import MkvToolNix
from modules.subtitle import SubtitleRefactor
from modules.subtitle_to_speech import SubtitleToSpeech
from modules.translator import SubtitleTranslator
from modules.mkv_processing import MKVProcessing
from utils.cool_animation import CoolAnimation
from utils.execution_timer import execution_timer
def check_and_create_directories(directories: List[str]): # ✅
"""
Checks if the given directories exist, and if not, creates them.
Args:
directories (List[str]): A list of directory paths to check and create.
"""
for directory in directories:
if not path.exists(directory):
makedirs(directory)
def display_logo(): # ✅
"""
Displays the logo of the application using the CoolAnimation class.
"""
mm_avh_logo: CoolAnimation = CoolAnimation()
mm_avh_logo.display()
console.print(
'╚═══ Multimedia Magic – Audio Visual Heaven ═══╝\n', style='white_bold')
def ask_user(question: str) -> bool:
"""
Asks the user a yes/no question and returns the response.
Args:
question (str): The question to ask the user.
Returns:
bool: True if the user answers yes, False otherwise.
"""
console.print(question, style='bold green', end=' ')
return input().lower() in ('t', 'y')
def update_settings() -> Settings: # ✅
"""
Asks the user if they want to update the settings. If yes, updates the settings and saves them to a file.
Returns:
Settings: The updated settings.
"""
if ask_user('💾 Czy chcesz zmienić ustawienia? (T lub Y - tak):'):
Settings.change_settings_save_to_file()
console.print('Zapisano ustawienia.\n', style='green_bold')
else:
console.print('Pomijam tę opcję.\n', style='red_bold')
return Settings.load_from_file()
def extract_tracks_from_mkv(): # ✅
"""
Asks the user if they want to extract tracks from MKV files. If yes, extracts the tracks.
"""
if ask_user('🧲 Czy chcesz wyciągnąć ścieżki z plików mkv? (T lub Y - tak):'):
files: List[str] = get_mkv_files(WORKING_SPACE)
sorted_files: List[str] = natsorted(files)
for filename in sorted_files:
mkv: MkvToolNix = MkvToolNix(filename)
mkv.mkv_extract_track(mkv.get_mkv_info())
else:
console.print('Pomijam tę opcję.\n', style='red_bold')
def get_mkv_files(directory: str) -> List[str]:
"""
Gets all MKV files in a directory.
Args:
directory (str): The directory to search for MKV files.
Returns:
List[str]: A list of MKV files in the directory.
"""
return [file for file in listdir(directory)
if path.isfile(path.join(directory, file)) and file.endswith('.mkv')]
def refactor_subtitles(): # ✅
"""
Refactors subtitles in various formats to a standard format.
"""
subtitle_extensions: List[str] = [
'.sup', '.txt', '.ogg',
'.ssa', '.ass', '.srt',
'.sub', '.usf', '.vtt',
]
files: List[str] = get_files_with_extensions(
WORKING_SPACE_TEMP, subtitle_extensions)
sorted_files = natsorted(files)
for filename in sorted_files:
refactor_subtitle_file(filename)
def get_files_with_extensions(directory: str, extensions: List[str]) -> List[str]:
"""
Gets all files in a directory with certain extensions.
Args:
directory (str): The directory to search for files.
extensions (List[str]): The extensions to look for.
Returns:
List[str]: A list of files in the directory with the specified extensions.
"""
return [
file for file in listdir(directory)
if (
path.isfile(path.join(directory, file)) and
any(file.endswith(ext) for ext in extensions)
)
]
def refactor_subtitle_file(filename: str):
"""
Refactors a subtitle file to a standard format.
Args:
filename (str): The name of the subtitle file to refactor.
"""
subtitle: SubtitleRefactor = SubtitleRefactor(filename)
if filename.endswith('.ass') or filename.endswith('.ssa'):
subtitle.split_ass()
subtitle.ass_to_srt()
if filename.endswith('.srt'):
subtitle.move_srt()
if filename.endswith('.txt'):
subtitle.txt_to_srt(10)
def translate_subtitles(settings: Settings): # ✅
"""
Asks the user if they want to translate subtitle files. If yes, translates the files.
Args:
settings (Settings): The settings to use for translation.
"""
if not ask_user('💭 Czy chcesz tłumaczyć pliki napisów? (T lub Y - tak):'):
console.print('Pomijam tę opcję.\n', style='red_bold')
return
main_subs_files = get_srt_files(WORKING_SPACE_TEMP_MAIN_SUBS)
files_to_translate = ask_to_translate_files(main_subs_files)
translate_files(files_to_translate, settings)
def get_srt_files(directory: str) -> List[str]:
"""
Gets all SRT files in a directory.
Args:
directory (str): The directory to search for SRT files.
Returns:
List[str]: A list of SRT files in the directory.
"""
return [
filename for filename in listdir(directory)
if path.isfile(path.join(directory, filename)) and filename.endswith('.srt')
]
def ask_to_translate_files(files: List[str]) -> dict:
"""
Asks the user which files they want to translate.
Args:
files (List[str]): A list of files to ask about.
Returns:
dict: A dictionary mapping file names to a boolean indicating whether the user wants to translate them.
"""
files_to_translate: dict = {}
for filename in files:
console.print("\nTŁUMACZENIE PLIKU:", style='yellow_bold')
console.print(filename, style='white_bold')
if ask_user("Czy chcesz przetłumaczyć (T lub Y - tak):"):
files_to_translate[filename] = True
else:
console.print('Pomijam tę opcję.\n', style='red_bold')
files_to_translate[filename] = False
return files_to_translate
def translate_files(files_to_translate: dict, settings: Settings):
"""
Translates the specified files.
Args:
files_to_translate (dict): A dictionary mapping file names to a boolean indicating whether to translate them.
settings (Settings): The settings to use for translation.
"""
translator_instance: SubtitleTranslator = SubtitleTranslator()
for filename, should_translate in files_to_translate.items():
if should_translate:
translator_instance.translate_srt(filename,
WORKING_SPACE_TEMP_MAIN_SUBS,
settings)
if path.exists(path.join(WORKING_SPACE_TEMP_ALT_SUBS, filename)):
translator_instance.translate_srt(filename,
WORKING_SPACE_TEMP_ALT_SUBS,
settings)
def convert_numbers_to_words(): # ✅
"""
Asks the user if they want to convert numbers to words in the text. If yes, performs the conversion.
"""
if not ask_user('🔢 Czy chcesz przekonwertować liczby na słowa w tekście? (T lub Y - tak):'):
console.print('Pomijam tę opcję.\n', style='red_bold')
return
srt_files = get_srt_files(WORKING_SPACE_TEMP_MAIN_SUBS)
convert_numbers_in_files(srt_files)
def get_srt_files(directory: str) -> List[str]:
"""
Gets all SRT files in a directory.
Args:
directory (str): The directory to search for SRT files.
Returns:
List[str]: A list of SRT files in the directory.
"""
return [
file for file in listdir(directory)
if path.isfile(path.join(directory, file)) and file.endswith('.srt')
]
def convert_numbers_in_files(files: List[str]):
"""
Converts numbers to words in the specified files.
Args:
files (List[str]): A list of files to convert numbers in.
"""
for filename in files:
console.print(
"\nKONWERSJA LICZB (BEZ POPRAWNOŚCI GRAMATYCZNEJ) W PLIKU:", style='yellow_bold')
console.print(filename, style='white_bold')
if ask_user("Czy chcesz przekonwertować liczby na słowa w tym pliku? (T lub Y - tak):"):
subtitle: SubtitleRefactor = SubtitleRefactor(filename)
subtitle.convert_numbers_in_srt()
else:
console.print(f'Pomijam plik {filename}.\n', style='red_bold')
def generate_audio_for_subtitles(settings: Settings) -> None: # ✅
"""
Asks the user if they want to generate audio for subtitles. If yes, generates the audio.
Args:
settings (Settings): The settings to use for audio generation.
"""
if not ask_user('🎤 Czy chcesz generować audio dla napisów? (T lub Y - tak):'):
console.print('Pomijam tę opcję.\n', style='red_bold')
return
main_subs_files: List[str] = get_srt_files(WORKING_SPACE_TEMP_MAIN_SUBS)
files_to_generate_audio: Dict[str, bool] = ask_to_generate_audio_files(
main_subs_files)
generate_audio_files(files_to_generate_audio, settings)
def ask_to_generate_audio_files(files: List[str]) -> Dict[str, bool]:
"""
Asks the user which files they want to generate audio for.
Args:
files (List[str]): A list of files to ask about.
Returns:
dict: A dictionary mapping file names to a boolean indicating whether the user wants to generate audio for them.
"""
files_to_generate_audio: Dict[str, bool] = {}
for filename in files:
console.print("\nGENEROWANIE AUDIO DLA PLIKU:", style='yellow_bold')
console.print(filename, style='white_bold')
if ask_user("Czy chcesz wygenerować audio dla tego pliku? (T lub Y - tak):"):
files_to_generate_audio[filename] = True
else:
console.print('Pomijam tę opcję.', style='red_bold')
files_to_generate_audio[filename] = False
return files_to_generate_audio
def generate_audio_files(files_to_generate_audio: Dict[str, bool], settings: Settings) -> None:
"""
Generates audio for the specified files.
Args:
files_to_generate_audio (Dict[str, bool]): A dictionary mapping file names to a boolean indicating whether the user wants to generate audio for them.
settings (Settings): The settings to use for audio generation.
"""
audio_generator: SubtitleToSpeech
if 'TTS - *Głos* - ElevenLans' in settings.tts:
audio_generator = SubtitleToSpeech('')
audio_generator.srt_to_eac3_elevenlabs()
else:
for filename, should_generate_audio in files_to_generate_audio.items():
if should_generate_audio:
audio_generator = SubtitleToSpeech(filename)
audio_generator.generate_audio(settings)
def refactor_alt_subtitles(): # ✅
"""
Refactors alternative subtitles to a standard format.
"""
files: List[str] = get_srt_files(WORKING_SPACE_TEMP_ALT_SUBS)
sorted_files = natsorted(files)
for filename in sorted_files:
subtitle: SubtitleRefactor = SubtitleRefactor(filename)
subtitle.srt_to_ass()
def process_output_files(settings: Settings):
"""
Processes output files based on user settings.
Args:
settings (Settings): The settings to use for processing.
"""
files = listdir(WORKING_SPACE_OUTPUT)
files_dict = {path.splitext(file)[0]: [] for file in files}
for file in files:
if not file.endswith(('.mkv', '.mp4')):
files_dict[path.splitext(file)[0]].append(file)
for base_name, files in files_dict.items():
if len(files) > 0:
# https://trac.ffmpeg.org/wiki/Encode/H.264
# crf_value => 0 ... 18 ... 23 ... 51 ... :(
# preset_value => 'ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow', 'placebo'
subtitle_processor = MKVProcessing(filename=base_name,
crf_value='18',
preset_value='medium')
subtitle_processor.process_mkv(settings)
def clear_temp_folders():
"""
Clears temporary folders used during processing.
"""
folders = [WORKING_SPACE_TEMP, WORKING_SPACE_TEMP_MAIN_SUBS,
WORKING_SPACE_TEMP_ALT_SUBS]
for folder in folders:
rmtree(folder, ignore_errors=True)
makedirs(folder, exist_ok=True)
@execution_timer # ✅
def main():
"""
Main function that runs the entire process.
"""
display_logo()
settings: Settings = update_settings()
extract_tracks_from_mkv()
refactor_subtitles()
translate_subtitles(settings)
convert_numbers_to_words()
generate_audio_for_subtitles(settings)
refactor_alt_subtitles()
process_output_files(settings)
clear_temp_folders()
if __name__ == '__main__':
"""
Ensures the main function is only run if the script is executed directly (not imported as a module).
"""
directories: List[str] = [WORKING_SPACE, WORKING_SPACE_OUTPUT,
WORKING_SPACE_TEMP, WORKING_SPACE_TEMP_MAIN_SUBS, WORKING_SPACE_TEMP_ALT_SUBS]
check_and_create_directories(directories)
main()
console.print(
'\n[green_italic]Naciśnij dowolny klawisz, aby zakończyć działanie programu...', end='')
getch()