From eb71883761644968bf74d33b1f0dd3652c6562f4 Mon Sep 17 00:00:00 2001 From: CodeByMrFinchum Date: Thu, 2 Jan 2025 17:06:36 +0100 Subject: [PATCH] Removing all files not needed to have a optima35 packge --- gui.py | 283 ----------------------------------------------- main.py | 53 --------- tui.py | 336 -------------------------------------------------------- 3 files changed, 672 deletions(-) delete mode 100644 gui.py delete mode 100644 main.py delete mode 100644 tui.py diff --git a/gui.py b/gui.py deleted file mode 100644 index 96cd561..0000000 --- a/gui.py +++ /dev/null @@ -1,283 +0,0 @@ -import sys -import os -from datetime import datetime - -from optima.optima35 import OPTIMA35 -from utils.utility import Utilities -from ui.main_window import Ui_MainWindow -from ui.exif_handler_window import ExifEditor - -from PySide6 import QtWidgets -from PySide6.QtWidgets import ( - QMessageBox, - QApplication, - QMainWindow, - QWidget, - QVBoxLayout, - QLabel, - QLineEdit, - QPushButton, - QCheckBox, - QFileDialog, - QHBoxLayout, - QSpinBox, -) - -class Optima35GUI(QMainWindow, Ui_MainWindow): - def __init__(self, exif_file): - super(Optima35GUI, self).__init__() - self.name = "GUI35" - self.version = "0.1.0" - self.ui = Ui_MainWindow() - self.ui.setupUi(self) - self.o = OPTIMA35() - self.u = Utilities() - self.exif_file = exif_file - self.available_exif_data = None - self.settings = {} - self.setWindowTitle(f"{self.name} v{self.version} for {self.o.name} {self.o.version}") - self._default_ui_layout() - self._define_gui_interaction() - - if exif_file == "config/exif_example.yaml": - self._change_statusbar("Using example exif...", 10000) - - def _default_ui_layout(self): - self.ui.png_quality_spinBox.setVisible(False) - - def _define_gui_interaction(self): - self.ui.input_folder_button.clicked.connect(self._browse_input_folder) - self.ui.output_folder_button.clicked.connect(self._browse_output_folder) - self.ui.start_button.clicked.connect(self._process) - self.ui.image_type.currentIndexChanged.connect(self._update_quality_options) - - self.ui.exif_checkbox.stateChanged.connect( - lambda state: self._handle_checkbox_state(state, 2, self._populate_exif) - ) - self.ui.tabWidget.currentChanged.connect(self._on_tab_changed) - self.ui.edit_exif_button.clicked.connect(self._open_exif_editor) - - def _process(self): - self.ui.start_button.setEnabled(False) - self._update_settings() # Get all user selected data - input_folder_valid = os.path.exists(self.settings["input_folder"]) - output_folder_valid = os.path.exists(self.settings["output_folder"]) - if not input_folder_valid or not output_folder_valid: - QMessageBox.warning(self, "Warning", f"Input location {input_folder_valid}\nOutput folder {output_folder_valid}...") - return - - input_folder = self.settings["input_folder"] - output_folder = self.settings["output_folder"] - - image_files = [ - f for f in os.listdir(input_folder) if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) - ] - i = 1 - for image_file in image_files: - input_path = os.path.join(input_folder, image_file) - if self.settings["new_file_names"] != False: - image_name = self.u.append_number_to_name(self.settings["new_file_names"], i, len(image_files), self.settings["invert_image_order"]) - else: - image_name = os.path.splitext(image_file)[0] - output_path = os.path.join(output_folder, image_name) - - self.o.process_image( - image_input_file = input_path, - image_output_file = output_path, - file_type = self.settings["file_format"], - quality = self.settings["jpg_quality"], - compressing = self.settings["png_compression"], - optimize = self.ui.optimize_checkBox.isChecked(), - resize = self.settings["resize"], - watermark = self.settings["watermark"], - font_size = self.settings["font_size"], - grayscale = self.settings["grayscale"], - brightness = self.settings["brightness"], - contrast = self.settings["contrast"], - dict_for_exif = self.user_selected_exif, - gps = self.settings["gps"], - copy_exif = self.settings["copy_exif"]) - self._handle_qprogressbar(i, len(image_files)) - i += 1 - - QMessageBox.information(self, "Information", "Finished") - self.ui.start_button.setEnabled(True) - self.ui.progressBar.setValue(0) - - def _open_exif_editor(self): - """Open the EXIF Editor.""" - self.exif_editor = ExifEditor(self.available_exif_data) - self.exif_editor.exif_data_updated.connect(self._update_exif_data) - self.exif_editor.show() - - def _update_exif_data(self, updated_exif_data): - """Update the EXIF data.""" - self.exif_data = updated_exif_data - self._populate_exif() - - def _handle_checkbox_state(self, state, desired_state, action): - """Perform an action based on the checkbox state and a desired state. Have to use lambda when calling.""" - if state == desired_state: - action() - - def _on_tab_changed(self, index): - """Handle tab changes.""" - # chatgpt - if index == 1: # EXIF Tab - self._handle_exif_file("read") - elif index == 0: # Main Tab - self._handle_exif_file("write") - - def _handle_exif_file(self, do): - if do == "read": - self.available_exif_data = self.u.read_yaml(self.exif_file) - elif do == "write": - self.u.write_yaml(self.exif_file, self.available_exif_data) - - def _populate_exif(self): - # partly chatGPT - # Mapping of EXIF fields to comboboxes in the UI - combo_mapping = { - "make": self.ui.make_comboBox, - "model": self.ui.model_comboBox, - "lens": self.ui.lens_comboBox, - "iso": self.ui.iso_comboBox, - "image_description": self.ui.image_description_comboBox, - "user_comment": self.ui.user_comment_comboBox, - "artist": self.ui.artist_comboBox, - "copyright_info": self.ui.copyright_info_comboBox, - } - self._populate_comboboxes(combo_mapping) - - def _populate_comboboxes(self, combo_mapping): - """Populate comboboxes with EXIF data.""" - # ChatGPT - for field, comboBox in combo_mapping.items(): - comboBox.clear() # Clear existing items - comboBox.addItems(map(str, self.available_exif_data.get(field, []))) - - def _update_quality_options(self): - """Update visibility of quality settings based on selected format.""" - # ChatGPT - selected_format = self.ui.image_type.currentText() - # Hide all quality settings - self.ui.png_quality_spinBox.setVisible(False) - self.ui.jpg_quality_spinBox.setVisible(False) - # Show relevant settings - if selected_format == "jpg": - self.ui.jpg_quality_spinBox.setVisible(True) - elif selected_format == "webp": - self.ui.jpg_quality_spinBox.setVisible(True) - elif selected_format == "png": - self.ui.png_quality_spinBox.setVisible(True) - - def _browse_input_folder(self): - folder = QFileDialog.getExistingDirectory(self, "Select Input Folder") - if folder: - self.ui.input_path.setText(folder) - - def _browse_output_folder(self): - folder = QFileDialog.getExistingDirectory(self, "Select Output Folder") - if folder: - self.ui.output_path.setText(folder) - - def _change_statusbar(self, msg, timeout = 500): - self.ui.statusBar.showMessage(msg, timeout) - - def _handle_qprogressbar(self, current, total): - progress = int((100 / total) * current) - self.ui.progressBar.setValue(progress) - - def _get_checkbox_value(self, checkbox, default=None): - """Helper function to get the value of a checkbox or a default value.""" - return checkbox.isChecked() if checkbox else default - - def _get_spinbox_value(self, spinbox, default=None): - """Helper function to get the value of a spinbox and handle empty input.""" - return int(spinbox.text()) if spinbox.text() else default - - def _get_combobox_value(self, combobox, default=None): - """Helper function to get the value of a combobox.""" - return combobox.currentIndex() + 1 if combobox.currentIndex() != -1 else default - - def _get_text_value(self, lineedit, default=None): - """Helper function to get the value of a text input field.""" - return lineedit.text() if lineedit.text() else default - - def _get_selected_exif(self): - """Collect selected EXIF data and handle date and GPS if necessary.""" - selected_exif = self._collect_selected_exif() if self.ui.exif_checkbox.isChecked() else None - if selected_exif: - if self.ui.add_date_checkBox.isChecked(): - selected_exif["date_time_original"] = self._get_date() - if self.ui.gps_checkBox.isChecked(): - self.settings["gps"] = [self.ui.lat_lineEdit.text(), self.ui.long_lineEdit.text()] - else: - self.settings["gps"] = None - return selected_exif - - def _update_settings(self): - """Update .settings from all GUI elements.""" - # General settings - self.settings["input_folder"] = self._get_text_value(self.ui.input_path) - self.settings["output_folder"] = self._get_text_value(self.ui.output_path) - self.settings["file_format"] = self.ui.image_type.currentText() - self.settings["jpg_quality"] = self._get_spinbox_value(self.ui.jpg_quality_spinBox) - self.settings["png_compression"] = self._get_spinbox_value(self.ui.png_quality_spinBox) - self.settings["invert_image_order"] = self._get_checkbox_value(self.ui.revert_checkbox) - self.settings["grayscale"] = self._get_checkbox_value(self.ui.grayscale_checkBox) - self.settings["copy_exif"] = self._get_checkbox_value(self.ui.exif_copy_checkBox) - self.settings["own_exif"] = self._get_checkbox_value(self.ui.exif_checkbox) - self.settings["font_size"] = self._get_combobox_value(self.ui.font_size_comboBox) - self.settings["optimize"] = self._get_checkbox_value(self.ui.optimize_checkBox) - self.settings["own_date"] = self._get_checkbox_value(self.ui.add_date_checkBox) - - # Conditional settings with logic - self.settings["resize"] = self._get_spinbox_value(self.ui.resize_spinBox) if self.ui.resize_checkbox.isChecked() else None - self.settings["brightness"] = self._get_spinbox_value(self.ui.brightness_spinBox) if self.ui.brightness_checkbox.isChecked() else None - self.settings["contrast"] = self._get_spinbox_value(self.ui.contrast_spinBox) if self.ui.contrast_checkbox.isChecked() else None - - self.settings["new_file_names"] = self._get_text_value(self.ui.filename, False) if self.ui.rename_checkbox.isChecked() else False - self.settings["watermark"] = self._get_text_value(self.ui.watermark_lineEdit) if self.ui.watermark_checkbox.isChecked() else None - - # Handle EXIF data selection - if self.settings["own_exif"]: - self.user_selected_exif = self._get_selected_exif() - else: - self.user_selected_exif = None - self.settings["gps"] = None - - def _get_date(self): - date_input = self.ui.dateEdit.date().toString("yyyy-MM-dd") - new_date = datetime.strptime(date_input, "%Y-%m-%d") - return new_date.strftime("%Y:%m:%d 00:00:00") - - def _collect_selected_exif(self): - user_data = {} - user_data["make"] = self.ui.make_comboBox.currentText() - user_data["model"] = self.ui.model_comboBox.currentText() - user_data["lens"] = self.ui.lens_comboBox.currentText() - user_data["iso"] = self.ui.iso_comboBox.currentText() - user_data["image_description"] = self.ui.image_description_comboBox.currentText() - user_data["user_comment"] = self.ui.user_comment_comboBox.currentText() - user_data["artist"] = self.ui.artist_comboBox.currentText() - user_data["copyright_info"] = self.ui.copyright_info_comboBox.currentText() - user_data["software"] = f"{self.o.name} {self.o.version}" - return user_data - -def main(exif_file): - app = QtWidgets.QApplication(sys.argv) - window = Optima35GUI(exif_file=exif_file) - window.show() - app.exec() - -if __name__ == "__main__": - if os.path.isfile("config/exif.yaml"): - exif_file = "config/exif.yaml" - print("Fall back to exif example file...") - elif os.path.isfile("config/exif_example.yaml"): - exif_file = "config/exif_example.yaml" - else: - print("Exif file missing, please ensure an exif file exist in config folder (exif.yaml, or exif_example_yaml)\nExiting...") - exit() - main(exif_file) diff --git a/main.py b/main.py deleted file mode 100644 index 3c79c78..0000000 --- a/main.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -from argparse import ArgumentParser - -# Mainly from ChatGPT -def check_pyside_installed(): - try: - import PySide6 # Replace with PySide2 if using that version - return True - except ImportError: - return False - -def start_gui(): - import gui - gui.main(exif_file) - -def start_tui(): - import tui - tui.main(exif_file, tui_settings_file) - -def main(): - parser = ArgumentParser(description="Start the Optima35 application.") - parser.add_argument("--tui", action="store_true", help="Start in terminal UI mode.") - args = parser.parse_args() - - if args.tui: - print("Starting TUI...") - start_tui() - return - - # Check OS and start GUI if on Windows - if os.name == "nt": - print("Detected Windows. Starting GUI...") - start_gui() - else: - # Non-Windows: Check if PySide is installed - if check_pyside_installed(): - print("PySide detected. Starting GUI...") - start_gui() - else: - print("PySide is not installed. Falling back to TUI...") - start_tui() - -if __name__ == "__main__": - if os.path.isfile("config/exif.yaml"): - exif_file = "config/exif.yaml" - elif os.path.isfile("config/exif_example.yaml"): - exif_file = "config/exif_example.yaml" - print("Fall back to exif example file...") - else: - print("Exif file missing, please ensure an exif file exist in config folder (exif.yaml, or exif_example_yaml)\nExiting...") - exit() - tui_settings_file = "config/tui_settings.yaml" - main() diff --git a/tui.py b/tui.py deleted file mode 100644 index 803f8ab..0000000 --- a/tui.py +++ /dev/null @@ -1,336 +0,0 @@ -import os -from datetime import datetime -# my packages -from optima.optima35 import OPTIMA35 -from utils.utility import Utilities -from ui.simple_tui import SimpleTUI - -class Optima35TUI(): - def __init__(self, exif_file, settings_file): - self.name = "TUI35" - self.version = "0.1.0" - self.o = OPTIMA35() - self.u = Utilities() - self.tui = SimpleTUI() - self.exif_file = exif_file - self.available_exif_data = self.u.read_yaml(exif_file) - self.setting_file = settings_file - self.settings = { - "input_folder": None, - "output_folder": None, - "file_format": None, - "resize": None, - "copy_exif": None, - "contrast": None, - "brightness": None, - "new_file_names": None, - "invert_image_order": False, - "watermark": None, - "gps": None, - "modifications": [], - } - self.settings_to_save = [ - "resize", - "jpg_quality", - "png_compression", - "optimize", - "contrast", - "brightness" - ] - - def _process(self): - self._check_options() # Get all user selected data - input_folder_valid = os.path.exists(self.settings["input_folder"]) - output_folder_valid = os.path.exists(self.settings["output_folder"]) - if not input_folder_valid or not output_folder_valid: - print("Warning", f"Input location {input_folder_valid}\nOutput folder {output_folder_valid}...") - return - - input_folder = self.settings["input_folder"] - output_folder = self.settings["output_folder"] - - image_files = [ - f for f in os.listdir(input_folder) if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) - ] - i = 1 - for image_file in image_files: - input_path = os.path.join(input_folder, image_file) - if self.settings["new_file_names"] != False: - image_name = self.u.append_number_to_name(self.settings["new_file_names"], i, len(image_files), self.settings["invert_image_order"]) - else: - image_name = os.path.splitext(image_file)[0] - output_path = os.path.join(output_folder, image_name) - self.o.process_image( - image_input_file = input_path, - image_output_file = output_path, - file_type = self.settings["file_format"], - quality = self.settings["jpg_quality"], - compressing = self.settings["png_compression"], - optimize = self.settings["optimize"], - resize = self.settings["resize"], - watermark = self.settings["watermark"], - font_size = self.settings["font_size"], - grayscale = self.settings["grayscale"], - brightness = self.settings["brightness"], - contrast = self.settings["contrast"], - dict_for_exif = self.selected_exif, - gps = self.settings["gps"], - copy_exif = self.settings["copy_exif"]) - self.u.progress_bar(i, len(image_files)) - i += 1 - - def _check_options(self): - try: - if "Resize image" in self.settings["modifications"]: - self.settings["resize"] = self.settings["resize"] - else: - self.settings["resize"] = None - - if "Convert to grayscale" in self.settings["modifications"]: - self.settings["grayscale"] = True - else: - self.settings["grayscale"] = False - - if "Change contrast" in self.settings["modifications"]: - self.settings["contrast"] = self.settings["contrast"] - else: - self.settings["contrast"] = None - - if "Change brightness" in self.settings["modifications"]: - self.settings["brightness"] = self.settings["brightness"] - else: - self.settings["brightness"] = None - - if "Rename images" in self.settings["modifications"]: - self.settings["new_file_names"] = self.settings["new_file_names"] - else: - self.settings["new_file_names"] = False - - if "Invert image order" in self.settings["modifications"]: - self.settings["invert_image_order"] = True - else: - self.settings["invert_image_order"] = False - - if "Add Watermark" in self.settings["modifications"]: - self.settings["watermark"] = self.settings["watermark"] - else: - self.settings["watermark"] = None - - self.settings["optimize"] = self.settings["optimize"] - self.settings["png_compression"] = self.settings["png_compression"] - self.settings["jpg_quality"] = self.settings["jpg_quality"] - - self.settings["input_folder"] = self.settings["input_folder"] - self.settings["output_folder"] = self.settings["output_folder"] - self.settings["file_format"] = self.settings["file_format"] - self.settings["font_size"] = 2 # need to add option to select size - - self.settings["copy_exif"] = self.settings["copy_exif"] - - if "Change EXIF" in self.settings["modifications"]: #missing - self.selected_exif = self._collect_exif_data() # - else: - self.selected_exif = None - - except Exception as e: - print(f"Whoops: {e}") - - def _load_or_ask_settings(self): - """Load settings from a YAML file or ask the user if not present or incomplete.""" - try: - if self._read_settings(self.settings_to_save): - for item in self.settings_to_save: - print(f"{item}: {self.settings[item]}") - use_saved = self.tui.yes_no_menu("Use these settings?") - if use_saved: - return - else: - print("No settings found...") - self._ask_for_settings() - except Exception as e: - print(f"Error: {e}") - self._ask_for_settings() - - def _ask_for_settings(self): - print("Asking for new settings...\n") - self.settings["resize"] = self.take_input_and_validate(question = "Default resize percentage (below 100 downscale, above upscale): ", accepted_type = int, min_value = 10, max_value = 200) - self.settings["contrast"] = self.take_input_and_validate(question = "Default contrast percentage (negative = decrease, positive = increase): ", accepted_type = int, min_value = -100, max_value = 100) - self.settings["brightness"] = self.take_input_and_validate(question = "Default brighness percentage (negative = decrease, positive = increase): ", accepted_type = int, min_value = -100, max_value = 100) - self.settings["jpg_quality"] = self.take_input_and_validate(question = "JPEG quality (1-100, 80 default): ", accepted_type = int, min_value = 1, max_value = 100) - self.settings["png_compression"] = self.take_input_and_validate(question = "PNG compression level (0-9, 6 default): ", accepted_type = int, min_value = 0, max_value = 9) - self.settings["optimize"] = self.tui.yes_no_menu("Optimize images i.e. compressing?") - - self._write_settings(self.settings_to_save) - - def _write_settings(self, keys_to_save): - """"Write self.setting, but only specific values""" - keys = keys_to_save - filtered_settings = {key: self.settings[key] for key in keys if key in self.settings} - self.u.write_yaml(self.setting_file, filtered_settings) - print("New settings saved successfully.") - - def _read_settings(self, keys_to_load): - """ - Read settings from the settings file and update self.settings - with the values for specific keys without overwriting existing values. - """ - # First draft by ChatGPT, adjusted to fit my needs. - keys = keys_to_load - if os.path.exists(self.setting_file): - loaded_settings = self.u.read_yaml(self.setting_file) - for key in keys: - if key in loaded_settings: - self.settings[key] = loaded_settings[key] - print("Settings loaded successfully.") - return True - else: - print("Settings file empty.") - return False - - def _collect_exif_data(self): - """Collect EXIF data based on user input.""" - user_data = {} - fields = [ - "make", "model", "lens", "iso", "image_description", - "user_comment", "artist", "copyright_info" - ] - for field in fields: - - choise = self.tui.choose_menu(f"Enter {field.replace('_', ' ').title()}", self.available_exif_data[field]) - user_data[field] = choise - - user_data["software"] = f"{self.o.name} {self.o.version}" - new_date = self._get_date_input() - - if new_date: - user_data["date_time_original"] = new_date - - self.settings["gps"] = self._get_gps_input(user_data) - - return user_data - - def _get_gps_input(self, test_exif): - while True: - lat = input("Enter Latitude (xx.xxxxxx): ") - if lat == "": - return None - long = input("Enter Longitude (xx.xxxxxx): ") - try: - self.o.exif_handler.add_geolocation_to_exif(test_exif, float(lat), float(long)) - return [lat, long] - except Exception: - print("Invalid GPS formate, try again...") - - def _get_date_input(self): - # Partially chatGPT - while True: - date_input = input("Enter a date (yyyy-mm-dd): ") - if date_input == "": - return None # Skip if input is empty - try: - new_date = datetime.strptime(date_input, "%Y-%m-%d") - return new_date.strftime("%Y:%m:%d 00:00:00") - except ValueError: - print("Invalid date format. Please enter the date in yyyy-mm-dd format.") - - def _get_user_settings(self): - """Get initial settings from the user.""" - menu_options = [ - "Resize image", - "Change EXIF", - "Convert to grayscale", - "Change contrast", - "Change brightness", - "Rename images", - "Invert image order", - "Add Watermark" - ] # new option can be added here. - - self.settings["input_folder"] = input("Enter path of input folder: ").strip() # Add: check if folder exists. - self.settings["output_folder"] = input("Enter path of output folder: ").strip() - self.settings["file_format"] = self.take_input_and_validate(question = "Enter export file format (jpg, png, webp): ", accepted_input = ["jpg", "png", "webp"], accepted_type = str) - self.settings["modifications"] = self.tui.multi_select_menu( - f"\n{self.name} v{self.version} for {self.o.name} v.{self.o.version} \nSelect what you want to do (esc or q to exit)", - menu_options - ) - if "Change EXIF" not in self.settings["modifications"]: - self.settings["copy_exif"] = self.tui.yes_no_menu("Do you want to copy exif info from original file?") - if "Rename images" in self.settings["modifications"]: - self.settings["new_file_names"] = input("What should be the name for the new images? ") # Need - else: - self.settings["new_file_names"] = False - if "Invert image order" in self.settings["modifications"]: - self.settings["invert_image_order"] = True - else: - self.settings["invert_image_order"] = False - if "Add Watermark" in self.settings["modifications"]: - self.settings["watermark"] = input("Enter text for watermark. ") - else: - self.settings["watermark"] = False - - os.makedirs(self.settings["output_folder"], exist_ok = True) - - def take_input_and_validate(self, question, accepted_input = None, accepted_type = str, min_value = None, max_value = None): - """ - Asks the user a question, validates the input, and ensures it matches the specified criteria. - Args: - question (str): The question to ask the user. - accepted_input (list): A list of acceptable inputs (optional for non-numeric types). - accepted_type (type): The expected type of input (e.g., str, int, float). - min_value (int/float): Minimum value for numeric inputs (optional). - max_value (int/float): Maximum value for numeric inputs (optional). - - Returns: - The validated user input. - """ - # Main layout by chatGPT, but modified. - while True: - user_input = input(question).strip() - - try: - # Convert input to the desired type - if accepted_type in [int, float]: - user_input = accepted_type(user_input) - # Validate range for numeric types - if (min_value is not None and user_input < min_value) or (max_value is not None and user_input > max_value): - print(f"Input must be between {min_value} and {max_value}.") - continue - elif accepted_type == str: - # No conversion needed for strings - user_input = str(user_input) - else: - raise ValueError(f"Unsupported type: {accepted_type}") - - # Validate against accepted inputs if provided - if accepted_input is not None and user_input not in accepted_input: - print(f"Invalid input. Must be one of: {', '.join(map(str, accepted_input))}.") - continue - - return user_input # Input is valid - - except ValueError: - print(f"Invalid input. Must be of type {accepted_type.__name__}.") - - def run(self): - """Run the main program.""" - self._load_or_ask_settings() - self._get_user_settings() - self._process() - print("Done") - - - -def main(exif_file, config_file): - app = Optima35TUI(exif_file, config_file) - app.run() - -if __name__ == "__main__": - if os.path.isfile("config/exif.yaml"): - exif_file = "config/exif.yaml" - elif os.path.isfile("config/exif_example.yaml"): - exif_file = "config/exif_example.yaml" - print("Fall back to exif example file...") - else: - print("Exif file missing, please ensure an exif file exist in config folder (exif.yaml, or exif_example_yaml)\nExiting...") - exit() - main(exif_file, "config/tui_settings.yaml")