|
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: |
|
|
|
|
|
|
|
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() |
|
|