|
""" |
|
Module `Settings` provides a versatile configuration management system |
|
for your application. |
|
It includes a `Settings` class that allows users to customize various options |
|
and save them to a JSON file. |
|
|
|
* Example: First, create an instance of the `Settings` class: |
|
settings = Settings() |
|
|
|
* Example usage of the `Settings` class: |
|
settings.change_settings_save_to_file('settings.json') |
|
- If the file doesn't exist, it will be created with default settings. |
|
- If the file exists, it will be overwritten with the current settings. |
|
- If the file exists with incomplete settings, missing settings will be added with default values. |
|
|
|
* Example default settings in 'settings.json': |
|
{ |
|
"translator": "Google Translator", |
|
"deepl_api_key": "null", |
|
"chat_gpt_access_token": null, |
|
"translated_line_count": "50", |
|
"tts": "TTS - Agnieszka - Ivona", |
|
"tts_speed": "5", |
|
"tts_volume": "65", |
|
"output": "Ogl\u0105dam w MM_AVH_Players (wynik: napisy i audio)" |
|
} |
|
""" |
|
|
|
from dataclasses import asdict, dataclass |
|
from json import decoder, dump, load |
|
from typing import Dict, List, Optional, Tuple |
|
import webbrowser |
|
|
|
from constants import SETTINGS_PATH, console |
|
from data.config import Config |
|
|
|
|
|
@dataclass(slots=True) |
|
class Settings: |
|
""" |
|
A class representing application settings. |
|
|
|
Attributes: |
|
- translator (Optional[str]): The selected translator. |
|
- deepl_api_key (Optional[str]): The API key for DeepL translation service. |
|
- chat_gpt_access_token (Optional[str]): The access token for ChatGPT API. |
|
- translated_line_count (Optional[str]): The number of translated lines. |
|
- tts (Optional[str]): The selected TTS engine. |
|
- tts_speed (Optional[str]): The speed of the TTS voice. |
|
- tts_volume (Optional[str]): The volume of the TTS voice. |
|
- output (Optional[str]): The selected output option. |
|
|
|
Methods: |
|
- load_from_file(cls, settings_path: str) -> 'Settings': Load settings from a file. |
|
- _set_option(prompt: str, options: List[Dict[str, str]]) -> str | None: Set an option from a list of options. |
|
- _is_valid_speed(speed: str, tts: str) -> bool: Check if a given speed value is valid for the selected TTS engine. |
|
- _is_valid_volume(volume: str, tts: str) -> bool: Check if a given volume value is valid for the selected TTS engine. |
|
- _get_translator(settings: Optional['Settings']) -> Optional[str]: Get the selected translator. |
|
- _get_deepl_api_key(settings: Optional['Settings']) -> Optional[str]: Get the DeepL API key. |
|
- _get_chat_gpt_access_token(settings: Optional['Settings']) -> Optional[str]: Get the ChatGPT access token. |
|
- _get_translated_line_count(settings: Optional['Settings']) -> Optional[str]: Get the number of translated lines. |
|
- _get_tts(settings: Optional['Settings']) -> Optional[str]: Get the selected TTS engine. |
|
- _get_default_speed_volume(tts: str) -> Tuple[Optional[str], Optional[str]]: Get the default speed and volume for a TTS engine. |
|
- _get_tts_speed(tts: str, default_speed: Optional[str]) -> Optional[str]: Get the TTS speed. |
|
- _get_tts_volume(tts: str, default_volume: Optional[str]) -> Optional[str]: Get the TTS volume. |
|
- _get_output(settings: Optional['Settings']) -> Optional[str]: Get the selected output option. |
|
- get_user_settings(settings_path: str) -> Optional['Settings']: Get user settings from a file. |
|
- change_settings_save_to_file(settings_path: str) -> None: Change and save settings to a file. |
|
""" |
|
translator: Optional[str] = None |
|
deepl_api_key: Optional[str] = None |
|
chat_gpt_access_token: Optional[str] = None |
|
translated_line_count: Optional[str] = None |
|
tts: Optional[str] = None |
|
tts_speed: Optional[str] = None |
|
tts_volume: Optional[str] = None |
|
output: Optional[str] = None |
|
|
|
@classmethod |
|
def load_from_file(cls, settings_path: str = SETTINGS_PATH) -> 'Settings': |
|
""" |
|
Load settings from a JSON file. |
|
|
|
Args: |
|
- settings_path (str): The name of the file to load settings from. |
|
|
|
Returns: |
|
- Settings: An instance of the Settings class with the loaded settings. |
|
|
|
Raises: |
|
- FileNotFoundError: If the file does not exist. |
|
- decoder.JSONDecodeError: If the file has an invalid JSON format. |
|
""" |
|
|
|
def get_default_settings(): |
|
return cls( |
|
translator='Google Translator', |
|
deepl_api_key=None, |
|
chat_gpt_access_token=None, |
|
translated_line_count='50', |
|
tts='TTS - Agnieszka - Ivona', |
|
tts_speed='5', |
|
tts_volume='65', |
|
output='Oglądam w MM_AVH_Players (wynik: napisy i audio)' |
|
) |
|
|
|
try: |
|
with open(settings_path, 'r', encoding='utf-8') as file: |
|
data = load(file) |
|
except FileNotFoundError: |
|
console.print( |
|
f'Nie znaleziono pliku {settings_path}', style='red_bold') |
|
console.print('Musisz najpierw ustawić ustawienia.', |
|
style='red_bold') |
|
return get_default_settings() |
|
|
|
except decoder.JSONDecodeError: |
|
console.print( |
|
f'Niepoprawny format pliku {settings_path}', style='red_bold') |
|
return get_default_settings() |
|
|
|
return Settings( |
|
translator=data.get('translator'), |
|
deepl_api_key=data.get('deepl_api_key'), |
|
chat_gpt_access_token=data.get('chat_gpt_access_token'), |
|
translated_line_count=data.get('translated_line_count'), |
|
tts=data.get('tts'), |
|
tts_speed=data.get('tts_speed'), |
|
tts_volume=data.get('tts_volume'), |
|
output=data.get('output') |
|
) |
|
|
|
@staticmethod |
|
def _set_option(prompt: str, options: List[Dict[str, str]]) -> str | None: |
|
""" |
|
Set an option from a list of options. |
|
|
|
Args: |
|
- prompt (str): The prompt to display to the user. |
|
- options (List[Dict[str, str]]): The list of options to choose from. |
|
|
|
Returns: |
|
- str | None: The selected option, or None if the selection is invalid. |
|
""" |
|
console.print(f'\n{prompt}', style='yellow_bold') |
|
for i, option in enumerate(options): |
|
console.print( |
|
f'[yellow_bold]{i + 1}.[/yellow_bold] [white]{option["name"]}') |
|
if 'suboptions' in option: |
|
for j, suboption in enumerate(option['suboptions']): |
|
console.print( |
|
f'[yellow_bold] {i + 1}.{j + 1}.[/yellow_bold] [white]{suboption["name"]}') |
|
elif 'description' in option: |
|
console.print( |
|
f' [white]{option["description"]["speed"]}') |
|
console.print( |
|
f' [white]{option["description"]["volume"]}') |
|
console.print('Wybierz opcję: ', style='green_bold', end='') |
|
choice = input() |
|
if '.' in choice: |
|
major_choice, minor_choice = map(int, choice.split('.')) |
|
if 1 <= major_choice <= len(options) and 1 <= minor_choice <= len(options[major_choice - 1]['suboptions']): |
|
return options[major_choice - 1]['suboptions'][minor_choice - 1]['name'] |
|
elif choice.isdigit(): |
|
choice_num = int(choice) |
|
if 1 <= choice_num <= len(options): |
|
return options[choice_num - 1]['name'] |
|
else: |
|
for option in options: |
|
if option['name'] == choice: |
|
return option['name'] |
|
console.print( |
|
'Niepoprawny wybór. Nie zmieniono wartości!', style='red_bold') |
|
return None |
|
|
|
@staticmethod |
|
def _is_valid_speed(speed: str, tts: str) -> bool: |
|
""" |
|
Check if a given speed value is valid for the selected TTS engine. |
|
|
|
Args: |
|
- speed (str): The speed value to check. |
|
- tts (str): The selected TTS engine. |
|
|
|
Returns: |
|
- bool: True if the speed value is valid, False otherwise. |
|
""" |
|
if tts == 'TTS - Zosia - Harpo': |
|
return int(speed) >= 0 |
|
if tts == 'TTS - Agnieszka - Ivona': |
|
return -10 <= int(speed) <= 10 |
|
if tts in {'TTS - Zofia - Edge', 'TTS - Marek - Edge'}: |
|
return speed.startswith(('+', '-')) and speed[1:-1].isdigit() and -100 <= int( |
|
speed[1:-1]) <= 100 and speed.endswith('%') |
|
return False |
|
|
|
@staticmethod |
|
def _is_valid_volume(volume: str, tts: str) -> bool: |
|
""" |
|
Check if a given volume value is valid for the selected TTS engine. |
|
|
|
Args: |
|
- volume (str): The volume value to check. |
|
- tts (str): The selected TTS engine. |
|
|
|
Returns: |
|
- bool: True if the volume value is valid, False otherwise. |
|
""" |
|
if tts == 'TTS - Zosia - Harpo': |
|
return 0 <= float(volume) <= 1 |
|
if tts == 'TTS - Agnieszka - Ivona': |
|
return -100 <= int(volume) <= 100 |
|
if tts in {'TTS - Zofia - Edge', 'TTS - Marek - Edge'}: |
|
return volume.startswith(('+', '-')) and volume[1:-1].isdigit() and -100 <= int( |
|
volume[1:-1]) <= 100 and volume.endswith('%') |
|
return False |
|
|
|
@staticmethod |
|
def _get_translator(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Retrieve the selected translator from the user settings or prompt the user to choose one. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The selected translator or None if not found. |
|
""" |
|
|
|
translator = Settings._set_option( |
|
'Wybierz tłumacza: ', Config.get_translators()) |
|
if translator is None: |
|
translator = settings.translator if settings else None |
|
return translator |
|
|
|
@staticmethod |
|
def _get_deepl_api_key(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Prompt the user to set the DeepL API key or retrieve it from the user settings. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The DeepL API key or None if not set. |
|
""" |
|
|
|
console.print( |
|
'\nCzy chcesz ustawić klucz API DeepL?', style="yellow_bold") |
|
console.print('(T lub Y - tak): ', style='green_bold', end='') |
|
if input().lower() in ('t', 'y'): |
|
console.print( |
|
'Klucz API DeepL można wygenerować na stronie https://www.deepl.com/pro-api', style='yellow_bold') |
|
console.print('Podaj klucz API DeepL: ', |
|
style='green_bold', end='') |
|
deepl_api_key = input('') |
|
if deepl_api_key == '': |
|
deepl_api_key = settings.deepl_api_key if settings else None |
|
console.print( |
|
'Niepoprawna wartość. Nie zmieniono wartości!', style='red_bold') |
|
else: |
|
deepl_api_key = settings.deepl_api_key if settings else None |
|
return deepl_api_key |
|
|
|
@staticmethod |
|
def _get_chat_gpt_access_token(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Prompt the user to set the Chat GPT access token or retrieve it from the user settings. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The Chat GPT access token or None if not set. |
|
""" |
|
|
|
console.print( |
|
'\nCzy chcesz ustawić token dostępu do chat GPT?', style='yellow_bold') |
|
console.print('(T lub Y - tak): ', style='green_bold', |
|
end='') |
|
if input().lower() in ('t', 'y'): |
|
console.print( |
|
'Token dostępu (accessToken): https://chat.openai.com/api/auth/session', style='yellow_bold') |
|
webbrowser.open('https://chat.openai.com/api/auth/session') |
|
console.print( |
|
'Podaj token dostępu do chat GPT: ', style='green_bold', end='') |
|
chat_gpt_access_token = input() |
|
if chat_gpt_access_token == '': |
|
chat_gpt_access_token = settings.chat_gpt_access_token if settings else None |
|
console.print( |
|
'Niepoprawna wartość. Nie zmieniono wartości!', style='red_bold') |
|
else: |
|
chat_gpt_access_token = settings.chat_gpt_access_token if settings else None |
|
return chat_gpt_access_token |
|
|
|
@staticmethod |
|
def _get_translated_line_count(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Prompt the user to set the number of translated lines or retrieve it from the user settings. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The number of translated lines or None if not set. (Optional[str] future maybe change) |
|
""" |
|
|
|
translated_line_count = Settings._set_option('Wybierz liczbę przetłumaczonych linii: ', |
|
Config.get_translation_options()) |
|
if translated_line_count is None: |
|
translated_line_count = settings.translated_line_count if settings else None |
|
return translated_line_count |
|
|
|
@staticmethod |
|
def _get_tts(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Prompt the user to choose a TTS engine or retrieve it from the user settings. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The selected TTS engine or None if not set. |
|
""" |
|
|
|
tts = Settings._set_option( |
|
'Wybierz silnik TTS: ', Config.get_voice_actors()) |
|
if tts is None: |
|
tts = settings.tts if settings else None |
|
return tts |
|
|
|
@staticmethod |
|
def _get_default_speed_volume(tts: str) -> Tuple[Optional[str], Optional[str]]: |
|
""" |
|
Retrieve the default voice speed and volume for the specified TTS engine. |
|
|
|
Args: |
|
- tts (str): The selected TTS engine. |
|
|
|
Returns: |
|
- Tuple[Optional[str], Optional[str]]: A tuple containing the default voice speed and volume, |
|
or (None, None) if not found. |
|
""" |
|
|
|
default_speed = None |
|
default_volume = None |
|
voice_actor = next( |
|
(actor for actor in Config.get_voice_actors() if actor['name'] == tts), None) |
|
if voice_actor: |
|
default_speed = voice_actor['default_options']['default_voice_speed'] |
|
default_volume = voice_actor['default_options']['default_voice_volume'] |
|
return default_speed, default_volume |
|
|
|
@staticmethod |
|
def _get_tts_speed(tts: str, default_speed: Optional[str]) -> Optional[str]: |
|
""" |
|
Prompt the user to enter the voice speed for the selected TTS engine, or use the default speed. |
|
|
|
Args: |
|
- tts (str): The selected TTS engine. |
|
- default_speed (Optional[str]): The default voice speed for the TTS engine. |
|
|
|
Returns: |
|
- Optional[str]: The selected voice speed or the default speed if not set. |
|
""" |
|
|
|
console.print('Wpisz szybkość głosu: ', style='green_bold', end='') |
|
tts_speed_choice = input() |
|
try: |
|
tts_speed = tts_speed_choice if ( |
|
Settings._is_valid_speed( |
|
tts_speed_choice, tts) and tts_speed_choice.strip() != '' |
|
) else default_speed |
|
except ValueError: |
|
console.print( |
|
'Niepoprawna wartość szybkości. Używam domyślnej wartości.', style='red_bold') |
|
tts_speed = default_speed |
|
return tts_speed |
|
|
|
@staticmethod |
|
def _get_tts_volume(tts: str, default_volume: Optional[str]) -> Optional[str]: |
|
""" |
|
Prompt the user to enter the voice volume for the selected TTS engine, or use the default volume. |
|
|
|
Args: |
|
- tts (str): The selected TTS engine. |
|
- default_volume (Optional[str]): The default voice volume for the TTS engine. |
|
|
|
Returns: |
|
- Optional[str]: The selected voice volume or the default volume if not set. |
|
""" |
|
|
|
console.print('Wpisz głośność głosu: ', |
|
style='green_bold', end='') |
|
tts_volume_choice = input() |
|
try: |
|
tts_volume = tts_volume_choice if ( |
|
Settings._is_valid_volume( |
|
tts_volume_choice, tts) and tts_volume_choice.strip() != '' |
|
) else default_volume |
|
except ValueError: |
|
console.print( |
|
'Niepoprawna wartość głośności. Używam domyślnej wartości.', style='red_bold') |
|
tts_volume = default_volume |
|
return tts_volume |
|
|
|
@staticmethod |
|
def _get_output(settings: Optional['Settings']) -> Optional[str]: |
|
""" |
|
Prompt the user to choose an output option or retrieve it from the user settings. |
|
|
|
Args: |
|
- settings (Optional['Settings']): The user settings object. |
|
|
|
Returns: |
|
- Optional[str]: The selected output option or None if not set. |
|
""" |
|
|
|
output = Settings._set_option( |
|
'Wybierz wyjście: ', Config.get_output()) |
|
if output is None: |
|
output = settings.output if settings else None |
|
return output |
|
|
|
@staticmethod |
|
def get_user_settings(settings_path: str = SETTINGS_PATH) -> Optional['Settings']: |
|
""" |
|
Get the user settings from a file or prompt the user to enter them. |
|
|
|
Args: |
|
- settings_path (str): The name of the settings file. |
|
|
|
Returns: |
|
- Optional['Settings']: The user settings object or None if not found. |
|
""" |
|
|
|
settings = Settings.load_from_file(settings_path) |
|
|
|
translator = Settings._get_translator(settings) |
|
deepl_api_key = Settings._get_deepl_api_key(settings) |
|
chat_gpt_access_token = Settings._get_chat_gpt_access_token(settings) |
|
translated_line_count = Settings._get_translated_line_count(settings) |
|
tts = Settings._get_tts(settings) |
|
default_speed, default_volume = Settings._get_default_speed_volume(tts) |
|
console.print(f'\nWybrałeś: {tts}', style='yellow_bold') |
|
tts_speed = Settings._get_tts_speed(tts, default_speed) |
|
tts_volume = Settings._get_tts_volume(tts, default_volume) |
|
output = Settings._get_output(settings) |
|
|
|
return Settings( |
|
translator=translator, |
|
deepl_api_key=deepl_api_key, |
|
chat_gpt_access_token=chat_gpt_access_token, |
|
translated_line_count=translated_line_count, |
|
tts=tts, |
|
tts_speed=tts_speed, |
|
tts_volume=tts_volume, |
|
output=output |
|
) |
|
|
|
@staticmethod |
|
def change_settings_save_to_file(settings_path: str = SETTINGS_PATH) -> None: |
|
""" |
|
Prompt the user to change the settings and save them to a file. |
|
If the file does not exist, it will be created. |
|
If the file exists, it will be overwritten. |
|
|
|
Args: |
|
- settings_path (str): The name of the settings file. |
|
""" |
|
|
|
settings = Settings.get_user_settings(settings_path) |
|
|
|
with open(settings_path, 'w', encoding='utf-8') as file: |
|
dump(asdict(settings), file, indent=4) |
|
|