diff --git a/.gitignore b/.gitignore index 892341a..c3a1c81 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ __pycache__/ .venv/ .uv/ +dist/* +build/* + + # Local SQLite Databases # *.db # *.db-journal diff --git a/Santiago_cathedral_2021_Sunset.jpg b/Santiago_cathedral_2021_Sunset.jpg new file mode 100644 index 0000000..c1ba3f3 Binary files /dev/null and b/Santiago_cathedral_2021_Sunset.jpg differ diff --git a/anki_exporter.py b/anki_exporter.py index 9e301d2..96a13d7 100644 --- a/anki_exporter.py +++ b/anki_exporter.py @@ -2,9 +2,9 @@ import os import tempfile import asyncio +import time import genanki import edge_tts -import shutil import database from tts_utils import parse_text_for_edgetts, get_configured_tts_rate @@ -24,9 +24,13 @@ def compile_anki_package(records, output_path, deck_name): Resolves voice models dynamically by gender selection parameters and applies global speed coefficient rates from the active configurations. """ - # Incremented IDs to force a fresh schema mapping without legacy 'Notes' fields - model_id = 1684329060 - deck_id = 1684329060 + # Generate deterministic positive 32-bit integers from deck name and model name + # to avoid collisions across different decks while keeping imports stable + deck_id = abs(hash(deck_name)) % (2**31) + model_id = abs(hash("Spanish Bidirectional Multi-Note HTML Model")) % (2**31) + + # Unique timestamp prefix for media files to prevent overwriting prior exports in Anki + run_prefix = int(time.time()) # Global Configuration Pace Resolver Mapping settings = database.load_all_settings() or {} @@ -95,9 +99,9 @@ def compile_anki_package(records, output_path, deck_name): # Safely isolate the raw text/HTML data string down to Anki notes field anki_notes_html = record['anki_notes'].strip() if record.get('anki_notes') else "" - # Standard Unique Media Filenames - en_audio_filename = f"edge_en_{idx}_{model_id}.mp3" - es_audio_filename = f"edge_es_{idx}_{model_id}.mp3" + # Unique Media Filenames combining execution timestamp and index + en_audio_filename = f"edge_en_{run_prefix}_{idx}.mp3" + es_audio_filename = f"edge_es_{run_prefix}_{idx}.mp3" en_audio_path = os.path.join(tmpdir, en_audio_filename) es_audio_path = os.path.join(tmpdir, es_audio_filename) diff --git a/app.icns b/app.icns new file mode 100644 index 0000000..2b3410f Binary files /dev/null and b/app.icns differ diff --git a/app_icon.iconset/icon_128x128.png b/app_icon.iconset/icon_128x128.png new file mode 100644 index 0000000..5edaf9a Binary files /dev/null and b/app_icon.iconset/icon_128x128.png differ diff --git a/app_icon.iconset/icon_128x128@2x.png b/app_icon.iconset/icon_128x128@2x.png new file mode 100644 index 0000000..5322e17 Binary files /dev/null and b/app_icon.iconset/icon_128x128@2x.png differ diff --git a/app_icon.iconset/icon_16x16.png b/app_icon.iconset/icon_16x16.png new file mode 100644 index 0000000..fa464ff Binary files /dev/null and b/app_icon.iconset/icon_16x16.png differ diff --git a/app_icon.iconset/icon_16x16@2x.png b/app_icon.iconset/icon_16x16@2x.png new file mode 100644 index 0000000..5c75cb3 Binary files /dev/null and b/app_icon.iconset/icon_16x16@2x.png differ diff --git a/app_icon.iconset/icon_256x256.png b/app_icon.iconset/icon_256x256.png new file mode 100644 index 0000000..5322e17 Binary files /dev/null and b/app_icon.iconset/icon_256x256.png differ diff --git a/app_icon.iconset/icon_256x256@2x.png b/app_icon.iconset/icon_256x256@2x.png new file mode 100644 index 0000000..9fbb2e2 Binary files /dev/null and b/app_icon.iconset/icon_256x256@2x.png differ diff --git a/app_icon.iconset/icon_32x32.png b/app_icon.iconset/icon_32x32.png new file mode 100644 index 0000000..5c75cb3 Binary files /dev/null and b/app_icon.iconset/icon_32x32.png differ diff --git a/app_icon.iconset/icon_32x32@2x.png b/app_icon.iconset/icon_32x32@2x.png new file mode 100644 index 0000000..c7b3578 Binary files /dev/null and b/app_icon.iconset/icon_32x32@2x.png differ diff --git a/app_icon.iconset/icon_512x512.png b/app_icon.iconset/icon_512x512.png new file mode 100644 index 0000000..9fbb2e2 Binary files /dev/null and b/app_icon.iconset/icon_512x512.png differ diff --git a/app_icon.iconset/icon_512x512@2x.png b/app_icon.iconset/icon_512x512@2x.png new file mode 100644 index 0000000..32e63ab Binary files /dev/null and b/app_icon.iconset/icon_512x512@2x.png differ diff --git a/base_icon.png b/base_icon.png new file mode 100644 index 0000000..32e63ab Binary files /dev/null and b/base_icon.png differ diff --git a/database.py b/database.py index 01cf016..aabba1a 100644 --- a/database.py +++ b/database.py @@ -1,24 +1,79 @@ # database.py -import sqlite3 import os +import sys +import sqlite3 +from PyQt6.QtCore import QSettings + +APP_NAME = "SpanishVoiceTrainer" +DEFAULT_DB_FILENAME = "spanish_trainer.db" + + +def get_default_db_path() -> str: + """Returns standard macOS Application Support path: + ~/Library/Application Support/SpanishVoiceTrainer/spanish_trainer.db + """ + app_support_dir = os.path.expanduser( + f"~/Library/Application Support/{APP_NAME}" + ) + os.makedirs(app_support_dir, exist_ok=True) + return os.path.join(app_support_dir, DEFAULT_DB_FILENAME) + + +def get_db_path() -> str: + """Retrieves database path cleanly based on execution environment. + + - In packaged app mode (sys.frozen): strictly isolates data inside + Application Support unless a valid custom production path is chosen. + Heals stale settings pointing to local dev source paths. + - In dev mode: allows fallback to local project directory. + """ + qs = QSettings(APP_NAME, "Settings") + custom_path = qs.value("database_path", type=str) + + # 1. Check custom path saved in QSettings + if custom_path and os.path.exists(custom_path): + # Safeguard for packaged production app: + # Ignore custom paths that point back into local development source folders + if getattr(sys, "frozen", False) and "01_Projects" in custom_path: + default_path = get_default_db_path() + qs.setValue("database_path", default_path) # Repair stale setting + return default_path + return custom_path + + # 2. Development mode fallback (uncompiled python runtime) + if not getattr(sys, "frozen", False): + local_dev_db = os.path.join( + os.path.dirname(os.path.abspath(__file__)), DEFAULT_DB_FILENAME + ) + if os.path.exists(local_dev_db): + return local_dev_db + + # 3. Default production fallback + default_path = get_default_db_path() + qs.setValue("database_path", default_path) + return default_path + + +def set_db_path(new_path: str): + """Updates active database path in user preferences.""" + qs = QSettings(APP_NAME, "Settings") + qs.setValue("database_path", new_path) -DB_NAME = "spanish_trainer.db" def get_connection(): - """Returns a connection to the SQLite database with row factory enabled.""" - conn = sqlite3.connect(DB_NAME) + """Establishes connection to the active SQLite database.""" + db_path = get_db_path() + conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row return conn + def ensure_database_populated(): - """ - Creates empty tables using the unified schema if running - in a fresh environment without a database file. - """ + """Initializes tables if running against a new or empty database file.""" conn = get_connection() cursor = conn.cursor() try: - # 1. Unified translations table with dedicated anki_notes and gender tracks + # 1. Unified translations table cursor.execute(""" CREATE TABLE IF NOT EXISTS translations ( translation_id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -31,7 +86,7 @@ def ensure_database_populated(): gender TEXT DEFAULT 'Female' ); """) - + # 2. Key-value configuration table cursor.execute(""" CREATE TABLE IF NOT EXISTS settings ( @@ -43,10 +98,12 @@ def ensure_database_populated(): finally: conn.close() + # ========================================== # SETTINGS CRUD FUNCTIONS # ========================================== + def load_all_settings(): """Fetches all system configuration properties into a flat Python dictionary.""" conn = get_connection() @@ -60,28 +117,31 @@ def load_all_settings(): conn.close() return settings_dict + def save_setting_to_db(key, value): """Inserts or replaces an application configuration entry.""" conn = get_connection() cursor = conn.cursor() try: - cursor.execute(""" + cursor.execute( + """ INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?); - """, (key, value)) + """, + (key, value), + ) conn.commit() finally: conn.close() + # ========================================== # TRANSLATIONS CRUD FUNCTIONS # ========================================== + def get_all_translations_explicit(): - """ - Retrieves all records using completely explicit, - table-qualified column declarations for the engines. - """ + """Retrieves all records using explicit, table-qualified column declarations.""" conn = get_connection() cursor = conn.cursor() try: @@ -102,12 +162,14 @@ def get_all_translations_explicit(): finally: conn.close() + def get_translation_by_id(translation_id): """Loads a single unified record row for specific inspection or editing.""" conn = get_connection() cursor = conn.cursor() try: - cursor.execute(""" + cursor.execute( + """ SELECT translations.translation_id, translations.es_text, @@ -119,19 +181,31 @@ def get_translation_by_id(translation_id): translations.gender FROM translations WHERE translations.translation_id = ?; - """, (translation_id,)) + """, + (translation_id,), + ) row = cursor.fetchone() return dict(row) if row else None finally: conn.close() -def update_translation_record(translation_id, es_text, en_text, source_context, tags, notes, gender, anki_notes): - """Saves sandbox interface edits directly back down into the table using named arguments.""" + +def update_translation_record( + translation_id, + es_text, + en_text, + source_context, + tags, + notes, + gender, + anki_notes, +): + """Saves interface edits directly back into the table using named arguments.""" conn = get_connection() cursor = conn.cursor() try: - # The SQL uses :key syntax instead of ? - cursor.execute(""" + cursor.execute( + """ UPDATE translations SET es_text = :es, @@ -142,53 +216,62 @@ def update_translation_record(translation_id, es_text, en_text, source_context, gender = :gender, anki_notes = :anki WHERE translation_id = :id; - """, { - # The order inside this dictionary does not matter at all! - "id": translation_id, - "es": es_text, - "en": en_text, - "ctx": source_context, - "tags": tags, - "notes": notes, - "gender": gender, - "anki": anki_notes - }) + """, + { + "id": translation_id, + "es": es_text, + "en": en_text, + "ctx": source_context, + "tags": tags, + "notes": notes, + "gender": gender, + "anki": anki_notes, + }, + ) conn.commit() finally: conn.close() + def delete_translation_record(translation_id): """Permanently drops a phrase card row from the data index.""" conn = get_connection() cursor = conn.cursor() try: - cursor.execute(""" + cursor.execute( + """ DELETE FROM translations WHERE translations.translation_id = ?; - """, (translation_id,)) + """, + (translation_id,), + ) conn.commit() finally: conn.close() -def insert_translation_record(es_text, en_text, source_context, tags, notes, gender, anki_notes): - """Inserts a new record using named arguments so positional order doesn't matter.""" - conn = get_connection() # Corrected from get_db_connection + +def insert_translation_record( + es_text, en_text, source_context, tags, notes, gender, anki_notes +): + """Inserts a new record using named arguments.""" + conn = get_connection() cursor = conn.cursor() try: - cursor.execute(""" + cursor.execute( + """ INSERT INTO translations (es_text, en_text, source_context, tags, notes, gender, anki_notes) VALUES (:es, :en, :ctx, :tags, :notes, :gender, :anki); - """, { - # SQLite maps these keys directly to the tokens above by name - "en": en_text, - "es": es_text, - "ctx": source_context, - "tags": tags, - "notes": notes, - "anki": anki_notes, - "gender": gender - }) + """, + { + "en": en_text, + "es": es_text, + "ctx": source_context, + "tags": tags, + "notes": notes, + "anki": anki_notes, + "gender": gender, + }, + ) conn.commit() finally: - conn.close() - + conn.close() \ No newline at end of file diff --git a/doc/Notes.md b/doc/Notes.md index 63064ad..579baf5 100644 --- a/doc/Notes.md +++ b/doc/Notes.md @@ -19,4 +19,49 @@ git switch -c refactor/simplified-schema-modules # How to start using uv -(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer % uv run main.py \ No newline at end of file +(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer % uv run main.py + + +To trim non-relevant files from your directory tree (such as build artifacts, cache files, and non-source assets), you can configure flags for the `tree` command itself or use git-based filtering. + +**Tree Command Options** + +* **`-I pattern` (Ignore matching patterns):** Exclude build directories, caches, virtual environments, binaries, and temporary files. +* **`-d` (Directories only):** Show only directory structures if file-level detail is unnecessary. +* **`-L level` (Limit depth):** Limit how deep `tree` traverses (e.g., `-L 2` or `-L 3`) to prevent showing deep PyInstaller distribution bundles like `dist/` or `build/`. +* **`--gitignore`:** Automatically respects your `.gitignore` rules (supported in modern `tree` versions). + +--- + +**Recommended Command Patterns** + +**1. Ignore standard build & cache artifacts** + +```bash +tree -I "__pycache__|*.pyc|build|dist|*.app|*.iconset|*.png|*.pdf|*.jpg|database_legacy" + +``` + +**2. Combine pattern filtering with limited depth** + +```bash +tree -L 2 -I "__pycache__|build|dist|database_legacy" + +``` + +**3. Use `.gitignore` directly** +If you already have build outputs, images, and backups ignored in `.gitignore`: + +```bash +tree --gitignore + +``` + +--- + +**Summary of Files/Folders to Exclude** + +* **Build & Bundle Outputs:** `build/`, `dist/`, `main.app` +* **Python Caches:** `__pycache__/`, `*.pyc` +* **Static / Temp Media:** `*.png`, `*.jpg`, `*.pdf`, `app_icon.iconset/` +* **Legacy / Backups:** `database_legacy/` (containing database backups like `*.db`) \ No newline at end of file diff --git a/doc/Notes.pdf b/doc/Notes.pdf index e0bb8c6..4dfe3e0 100644 Binary files a/doc/Notes.pdf and b/doc/Notes.pdf differ diff --git a/main.spec b/main.spec new file mode 100644 index 0000000..9f0716a --- /dev/null +++ b/main.spec @@ -0,0 +1,51 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='main', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['app.icns'], +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='main', +) +app = BUNDLE( + coll, + name='main.app', + icon='app.icns', + bundle_identifier=None, +) diff --git a/pyproject.toml b/pyproject.toml index 087ccc9..21c89e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "librosa>=0.11.0", "numpy>=2.4.6", "pillow>=12.2.0", + "pyinstaller>=6.21.0", "pyqt6>=6.11.0", "scipy>=1.17.1", "sounddevice>=0.5.5", diff --git a/spanish_trainer-26-08-21.db b/spanish_trainer-26-08-21.db new file mode 100644 index 0000000..1fc4531 Binary files /dev/null and b/spanish_trainer-26-08-21.db differ diff --git a/spanish_trainer-backup.db b/spanish_trainer-backup.db new file mode 100644 index 0000000..a61b7e0 Binary files /dev/null and b/spanish_trainer-backup.db differ diff --git a/spanish_trainer.db b/spanish_trainer.db index 1fc4531..d7990c4 100644 Binary files a/spanish_trainer.db and b/spanish_trainer.db differ diff --git a/tabs/review_tab.py b/tabs/review_tab.py index 75fc449..32e3570 100644 --- a/tabs/review_tab.py +++ b/tabs/review_tab.py @@ -1,10 +1,12 @@ # tabs/review_tab.py import random import os +import re import subprocess import tempfile import threading import asyncio +from datetime import datetime from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, @@ -338,14 +340,14 @@ class ReviewTab(QWidget): @pyqtSlot() def generate_deck_action(self): - """Generates a specialized lightweight .apkg Anki deck matching active filter parameters, - respecting exact user database configuration keys for target folders and naming chains.""" + """Generates a specialized .apkg Anki deck matching active filter parameters, + prepending yyyy-mm-dd-hhmm timestamp and matching Sub-Deck-Namespace-Hierarchy without brackets/parentheses.""" if not self.filtered_review_pool: QMessageBox.warning(self, "Export Aborted", "The current matching review deck queue is empty. Cannot compile an empty deck.") return try: - # Load active settings dictionary directly from your database configurations + # Load active settings dictionary directly from database configurations settings = database.load_all_settings() or {} # Extract configurations targeting exact database schema names found in settings @@ -364,17 +366,27 @@ class ReviewTab(QWidget): if root_deck_name and str(root_deck_name).strip(): deck_tree_parts.append(str(root_deck_name).strip()) - else: - deck_tree_parts.append("DefaultDeck") # Baseline structural root name fallback if sub_deck_hierarchy and str(sub_deck_hierarchy).strip(): deck_tree_parts.append(str(sub_deck_hierarchy).strip()) - - # Join parts using Anki double-colon syntax (::) + else: + if not deck_tree_parts: + deck_tree_parts.append("DefaultDeck") + + # Join parts using Anki double-colon syntax (::) for internal Anki hierarchy full_deck_namespace = "::".join(deck_tree_parts) - # Establish absolute output filename file path anchor - filename = "Spanish_Filtered_Review.apkg" + # --- Format Timestamp and Clean Filename --- + # Format: YYYY-MM-DD-HHMM + timestamp = datetime.now().strftime("%Y-%m-%d-%H%M") + + # Explicitly strip out parentheses/brackets before regex normalization + clean_namespace = full_deck_namespace.replace('(', '').replace(')', '').replace('[', '').replace(']', '') + clean_namespace = re.sub(r'[^a-zA-Z0-9]', '-', clean_namespace) + clean_namespace = re.sub(r'-+', '-', clean_namespace).strip('-') + + # Complete output filename pattern: yyyy-mm-dd-hhmm-Sub-Deck-Namespace-Hierarchy.apkg + filename = f"{timestamp}-{clean_namespace}.apkg" file_path = os.path.join(target_dir, filename) # Execute actual compilation algorithm pipeline mapping filtered records cleanly @@ -383,9 +395,10 @@ class ReviewTab(QWidget): QMessageBox.information( self, "Export Complete", - f"Successfully exported Anki package to your configured target directory!\n\n" - f"Full Namespace Tree: {full_deck_namespace}\n" - f"Destination Path: {file_path}" + f"Successfully exported Anki package!\n\n" + f"Deck Hierarchy: {full_deck_namespace}\n" + f"File Name: {filename}\n" + f"Path: {file_path}" ) except Exception as e: QMessageBox.critical(self, "Compiler Fault Safeguard", f"An exception occurred building your deck container package:\n{str(e)}") diff --git a/tabs/settings_tab.py b/tabs/settings_tab.py index 40eec84..fe4f5f5 100644 --- a/tabs/settings_tab.py +++ b/tabs/settings_tab.py @@ -19,6 +19,29 @@ class SettingsTab(QWidget): main_layout = QVBoxLayout(self) main_layout.setSpacing(15) + + # --- SECTION 0: DATABASE FILE CONFIGURATION --- + db_group = QGroupBox("Database Storage Configuration") + db_form = QFormLayout(db_group) + db_form.setSpacing(10) + + db_picker_layout = QHBoxLayout() + self.txt_db_path = QLineEdit() + self.txt_db_path.setReadOnly(True) + self.txt_db_path.setStyleSheet("background-color: #F8F9F9; color: #34495E;") + + btn_browse_db = QPushButton("Browse...") + btn_browse_db.clicked.connect(self.browse_database_file) + + btn_reset_db = QPushButton("Reset Default") + btn_reset_db.clicked.connect(self.reset_default_database) + + db_picker_layout.addWidget(self.txt_db_path) + db_picker_layout.addWidget(btn_browse_db) + db_picker_layout.addWidget(btn_reset_db) + + db_form.addRow("Active Database File:", db_picker_layout) + main_layout.addWidget(db_group) # --- SECTION 1: GLOBAL ANKI PACKAGING CONFIGURATIONS --- anki_group = QGroupBox("Anki Compilation Settings") @@ -59,7 +82,7 @@ class SettingsTab(QWidget): self.txt_tts_voice.textChanged.connect(lambda text: self.update_setting("tts_preferred_voice", text.strip())) self.txt_tts_speed = QLineEdit() - self.txt_tts_speed.setPlaceholderText("1.15") + self.txt_tts_speed.setPlaceholderText("0.75") self.txt_tts_speed.textChanged.connect(lambda text: self.update_setting("tts_playback_speed", text.strip())) engine_form.addRow("Fallback System Voice Name:", self.txt_tts_voice) @@ -106,13 +129,16 @@ class SettingsTab(QWidget): # Block signals briefly so loading state doesn't trigger write-back loops self.blockSignals(True) + # Populate active database path + self.txt_db_path.setText(database.get_db_path()) + stored_settings = database.load_all_settings() self.txt_root_deck.setText(stored_settings.get("anki_root_deck_name", "Spanish")) self.txt_sub_deck.setText(stored_settings.get("anki_sub_deck_name", "")) self.txt_export_dir.setText(stored_settings.get("anki_export_directory", os.path.expanduser("~"))) self.txt_tts_voice.setText(stored_settings.get("tts_preferred_voice", "Apple_Monica")) - self.txt_tts_speed.setText(stored_settings.get("tts_playback_speed", "1.15")) + self.txt_tts_speed.setText(stored_settings.get("tts_playback_speed", "0.75")) self.blockSignals(False) @@ -120,6 +146,37 @@ class SettingsTab(QWidget): """Internal helper to communicate state mutations instantly upward.""" self.settings_changed.emit(key, value) + def browse_database_file(self): + """Allows user to choose an existing SQLite database file or create a new one.""" + current_db = self.txt_db_path.text() + file_path, _ = QFileDialog.getOpenFileName( + self, + "Select SQLite Database File", + current_db, + "SQLite Database (*.db *.sqlite *.sqlite3);;All Files (*)", + ) + if file_path: + database.set_db_path(file_path) + database.ensure_database_populated() + self.populate_fields_from_db_state() + QMessageBox.information( + self, + "Database Switched", + f"Active database switched to:\n{file_path}", + ) + + def reset_default_database(self): + """Resets the database path back to macOS Application Support default directory.""" + default_path = database.get_default_db_path() + database.set_db_path(default_path) + database.ensure_database_populated() + self.populate_fields_from_db_state() + QMessageBox.information( + self, + "Database Reset", + f"Reset database path to default location:\n{default_path}", + ) + def browse_export_directory(self): """Invokes a native macOS directory finder path browser window.""" current_dir = self.txt_export_dir.text() or os.path.expanduser("~") diff --git a/tree.txt b/tree.txt new file mode 100644 index 0000000..62b2776 --- /dev/null +++ b/tree.txt @@ -0,0 +1,981 @@ +. +├── LICENSE +├── README.md +├── README.pdf +├── Santiago_cathedral_2021_Sunset.jpg +├── __pycache__ +│   ├── anki_exporter.cpython-313.pyc +│   ├── database.cpython-313.pyc +│   ├── tts_utils.cpython-313.pyc +│   └── video_generator.cpython-313.pyc +├── anki_exporter.py +├── app.icns +├── app_icon.iconset +│   ├── icon_128x128.png +│   ├── icon_128x128@2x.png +│   ├── icon_16x16.png +│   ├── icon_16x16@2x.png +│   ├── icon_256x256.png +│   ├── icon_256x256@2x.png +│   ├── icon_32x32.png +│   ├── icon_32x32@2x.png +│   ├── icon_512x512.png +│   └── icon_512x512@2x.png +├── aula_int_plus_1_glos_en_alfa.pdf +├── base_icon.png +├── build +│   └── main +│   ├── Analysis-00.toc +│   ├── BUNDLE-00.toc +│   ├── COLLECT-00.toc +│   ├── EXE-00.toc +│   ├── PKG-00.toc +│   ├── PYZ-00.pyz +│   ├── PYZ-00.toc +│   ├── base_library.zip +│   ├── localpycs +│   │   ├── pyimod01_archive.pyc +│   │   ├── pyimod02_importers.pyc +│   │   ├── pyimod03_ctypes.pyc +│   │   └── struct.pyc +│   ├── main +│   ├── main.pkg +│   ├── warn-main.txt +│   └── xref-main.html +├── core +│   ├── __init__.py +│   ├── __pycache__ +│   │   ├── __init__.cpython-313.pyc +│   │   ├── asset_generator.cpython-313.pyc +│   │   ├── bulk_importer.cpython-313.pyc +│   │   ├── clean_glossary.cpython-313.pyc +│   │   └── phrase_manager.cpython-313.pyc +│   ├── asset_generator.py +│   ├── audio_engine.py +│   ├── bulk_importer.py +│   ├── clean_glossary.py +│   └── phrase_manager.py +├── database.py +├── database_legacy +│   ├── __init__.py +│   ├── __pycache__ +│   │   ├── __init__.cpython-313.pyc +│   │   └── connection.cpython-313.pyc +│   ├── connection.py +│   ├── trainer_backup_20260618_220043.db +│   └── trainer_backup_20260618_224811.db +├── dist +│   ├── main +│   │   ├── _internal +│   │   │   ├── 81d243bd2c585b0f4821__mypyc.cpython-313-darwin.so +│   │   │   ├── PIL +│   │   │   │   ├── _avif.cpython-313-darwin.so +│   │   │   │   ├── _imaging.cpython-313-darwin.so +│   │   │   │   ├── _imagingcms.cpython-313-darwin.so +│   │   │   │   ├── _imagingft.cpython-313-darwin.so +│   │   │   │   ├── _imagingmath.cpython-313-darwin.so +│   │   │   │   ├── _imagingtk.cpython-313-darwin.so +│   │   │   │   └── _webp.cpython-313-darwin.so +│   │   │   ├── PyQt6 +│   │   │   │   ├── Qt6 +│   │   │   │   │   ├── lib +│   │   │   │   │   │   ├── QtCore.framework +│   │   │   │   │   │   │   ├── QtCore -> Versions/Current/QtCore +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtCore +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   ├── QtDBus.framework +│   │   │   │   │   │   │   ├── QtDBus -> Versions/Current/QtDBus +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtDBus +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   ├── QtGui.framework +│   │   │   │   │   │   │   ├── QtGui -> Versions/Current/QtGui +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtGui +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   ├── QtNetwork.framework +│   │   │   │   │   │   │   ├── QtNetwork -> Versions/Current/QtNetwork +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtNetwork +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   ├── QtPdf.framework +│   │   │   │   │   │   │   ├── QtPdf -> Versions/Current/QtPdf +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtPdf +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   ├── QtSvg.framework +│   │   │   │   │   │   │   ├── QtSvg -> Versions/Current/QtSvg +│   │   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   │   └── Versions +│   │   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   │   ├── QtSvg +│   │   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── Current -> A +│   │   │   │   │   │   └── QtWidgets.framework +│   │   │   │   │   │   ├── QtWidgets -> Versions/Current/QtWidgets +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtWidgets +│   │   │   │   │   │   │   └── Resources +│   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── plugins +│   │   │   │   │   │   ├── generic +│   │   │   │   │   │   │   └── libqtuiotouchplugin.dylib +│   │   │   │   │   │   ├── iconengines +│   │   │   │   │   │   │   └── libqsvgicon.dylib +│   │   │   │   │   │   ├── imageformats +│   │   │   │   │   │   │   ├── libqgif.dylib +│   │   │   │   │   │   │   ├── libqicns.dylib +│   │   │   │   │   │   │   ├── libqico.dylib +│   │   │   │   │   │   │   ├── libqjpeg.dylib +│   │   │   │   │   │   │   ├── libqmacheif.dylib +│   │   │   │   │   │   │   ├── libqmacjp2.dylib +│   │   │   │   │   │   │   ├── libqpdf.dylib +│   │   │   │   │   │   │   ├── libqsvg.dylib +│   │   │   │   │   │   │   ├── libqtga.dylib +│   │   │   │   │   │   │   ├── libqtiff.dylib +│   │   │   │   │   │   │   ├── libqwbmp.dylib +│   │   │   │   │   │   │   └── libqwebp.dylib +│   │   │   │   │   │   ├── platforms +│   │   │   │   │   │   │   ├── libqcocoa.dylib +│   │   │   │   │   │   │   ├── libqminimal.dylib +│   │   │   │   │   │   │   └── libqoffscreen.dylib +│   │   │   │   │   │   └── styles +│   │   │   │   │   │   └── libqmacstyle.dylib +│   │   │   │   │   └── translations +│   │   │   │   │   ├── qt_ar.qm +│   │   │   │   │   ├── qt_bg.qm +│   │   │   │   │   ├── qt_ca.qm +│   │   │   │   │   ├── qt_cs.qm +│   │   │   │   │   ├── qt_da.qm +│   │   │   │   │   ├── qt_de.qm +│   │   │   │   │   ├── qt_en.qm +│   │   │   │   │   ├── qt_es.qm +│   │   │   │   │   ├── qt_fa.qm +│   │   │   │   │   ├── qt_fi.qm +│   │   │   │   │   ├── qt_fr.qm +│   │   │   │   │   ├── qt_gd.qm +│   │   │   │   │   ├── qt_gl.qm +│   │   │   │   │   ├── qt_he.qm +│   │   │   │   │   ├── qt_help_ar.qm +│   │   │   │   │   ├── qt_help_bg.qm +│   │   │   │   │   ├── qt_help_ca.qm +│   │   │   │   │   ├── qt_help_cs.qm +│   │   │   │   │   ├── qt_help_da.qm +│   │   │   │   │   ├── qt_help_de.qm +│   │   │   │   │   ├── qt_help_en.qm +│   │   │   │   │   ├── qt_help_es.qm +│   │   │   │   │   ├── qt_help_fr.qm +│   │   │   │   │   ├── qt_help_gl.qm +│   │   │   │   │   ├── qt_help_hr.qm +│   │   │   │   │   ├── qt_help_hu.qm +│   │   │   │   │   ├── qt_help_it.qm +│   │   │   │   │   ├── qt_help_ja.qm +│   │   │   │   │   ├── qt_help_ka.qm +│   │   │   │   │   ├── qt_help_ko.qm +│   │   │   │   │   ├── qt_help_nl.qm +│   │   │   │   │   ├── qt_help_nn.qm +│   │   │   │   │   ├── qt_help_pl.qm +│   │   │   │   │   ├── qt_help_pt_BR.qm +│   │   │   │   │   ├── qt_help_ru.qm +│   │   │   │   │   ├── qt_help_sk.qm +│   │   │   │   │   ├── qt_help_sl.qm +│   │   │   │   │   ├── qt_help_sv.qm +│   │   │   │   │   ├── qt_help_tr.qm +│   │   │   │   │   ├── qt_help_uk.qm +│   │   │   │   │   ├── qt_help_zh_CN.qm +│   │   │   │   │   ├── qt_help_zh_TW.qm +│   │   │   │   │   ├── qt_hr.qm +│   │   │   │   │   ├── qt_hu.qm +│   │   │   │   │   ├── qt_it.qm +│   │   │   │   │   ├── qt_ja.qm +│   │   │   │   │   ├── qt_ka.qm +│   │   │   │   │   ├── qt_ko.qm +│   │   │   │   │   ├── qt_lg.qm +│   │   │   │   │   ├── qt_lt.qm +│   │   │   │   │   ├── qt_lv.qm +│   │   │   │   │   ├── qt_nl.qm +│   │   │   │   │   ├── qt_nn.qm +│   │   │   │   │   ├── qt_pl.qm +│   │   │   │   │   ├── qt_pt_BR.qm +│   │   │   │   │   ├── qt_pt_PT.qm +│   │   │   │   │   ├── qt_ru.qm +│   │   │   │   │   ├── qt_sk.qm +│   │   │   │   │   ├── qt_sl.qm +│   │   │   │   │   ├── qt_sv.qm +│   │   │   │   │   ├── qt_tr.qm +│   │   │   │   │   ├── qt_uk.qm +│   │   │   │   │   ├── qt_zh_CN.qm +│   │   │   │   │   ├── qt_zh_TW.qm +│   │   │   │   │   ├── qtbase_ar.qm +│   │   │   │   │   ├── qtbase_bg.qm +│   │   │   │   │   ├── qtbase_ca.qm +│   │   │   │   │   ├── qtbase_cs.qm +│   │   │   │   │   ├── qtbase_da.qm +│   │   │   │   │   ├── qtbase_de.qm +│   │   │   │   │   ├── qtbase_en.qm +│   │   │   │   │   ├── qtbase_es.qm +│   │   │   │   │   ├── qtbase_fa.qm +│   │   │   │   │   ├── qtbase_fi.qm +│   │   │   │   │   ├── qtbase_fr.qm +│   │   │   │   │   ├── qtbase_gd.qm +│   │   │   │   │   ├── qtbase_he.qm +│   │   │   │   │   ├── qtbase_hr.qm +│   │   │   │   │   ├── qtbase_hu.qm +│   │   │   │   │   ├── qtbase_it.qm +│   │   │   │   │   ├── qtbase_ja.qm +│   │   │   │   │   ├── qtbase_ka.qm +│   │   │   │   │   ├── qtbase_ko.qm +│   │   │   │   │   ├── qtbase_lg.qm +│   │   │   │   │   ├── qtbase_lv.qm +│   │   │   │   │   ├── qtbase_nl.qm +│   │   │   │   │   ├── qtbase_nn.qm +│   │   │   │   │   ├── qtbase_pl.qm +│   │   │   │   │   ├── qtbase_pt_BR.qm +│   │   │   │   │   ├── qtbase_ru.qm +│   │   │   │   │   ├── qtbase_sk.qm +│   │   │   │   │   ├── qtbase_sv.qm +│   │   │   │   │   ├── qtbase_tr.qm +│   │   │   │   │   ├── qtbase_uk.qm +│   │   │   │   │   ├── qtbase_zh_CN.qm +│   │   │   │   │   └── qtbase_zh_TW.qm +│   │   │   │   ├── QtCore.abi3.so +│   │   │   │   ├── QtDBus.abi3.so +│   │   │   │   ├── QtGui.abi3.so +│   │   │   │   ├── QtWidgets.abi3.so +│   │   │   │   └── sip.cpython-313-darwin.so +│   │   │   ├── QtCore -> PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore +│   │   │   ├── QtDBus -> PyQt6/Qt6/lib/QtDBus.framework/Versions/A/QtDBus +│   │   │   ├── QtGui -> PyQt6/Qt6/lib/QtGui.framework/Versions/A/QtGui +│   │   │   ├── QtNetwork -> PyQt6/Qt6/lib/QtNetwork.framework/Versions/A/QtNetwork +│   │   │   ├── QtPdf -> PyQt6/Qt6/lib/QtPdf.framework/Versions/A/QtPdf +│   │   │   ├── QtSvg -> PyQt6/Qt6/lib/QtSvg.framework/Versions/A/QtSvg +│   │   │   ├── QtWidgets -> PyQt6/Qt6/lib/QtWidgets.framework/Versions/A/QtWidgets +│   │   │   ├── aiohttp +│   │   │   │   ├── _http_parser.cpython-313-darwin.so +│   │   │   │   ├── _http_writer.cpython-313-darwin.so +│   │   │   │   └── _websocket +│   │   │   │   ├── mask.cpython-313-darwin.so +│   │   │   │   └── reader_c.cpython-313-darwin.so +│   │   │   ├── attrs-26.1.0.dist-info +│   │   │   │   ├── INSTALLER +│   │   │   │   ├── METADATA +│   │   │   │   ├── RECORD +│   │   │   │   ├── REQUESTED +│   │   │   │   ├── WHEEL +│   │   │   │   └── licenses +│   │   │   │   └── LICENSE +│   │   │   ├── base_library.zip +│   │   │   ├── certifi +│   │   │   │   ├── cacert.pem +│   │   │   │   └── py.typed +│   │   │   ├── charset_normalizer +│   │   │   │   ├── cd.cpython-313-darwin.so +│   │   │   │   └── md.cpython-313-darwin.so +│   │   │   ├── frozenlist +│   │   │   │   └── _frozenlist.cpython-313-darwin.so +│   │   │   ├── libXau.6.dylib -> PIL/.dylibs/libXau.6.dylib +│   │   │   ├── libavif.16.4.1.dylib -> PIL/.dylibs/libavif.16.4.1.dylib +│   │   │   ├── libbrotlicommon.1.2.0.dylib -> PIL/.dylibs/libbrotlicommon.1.2.0.dylib +│   │   │   ├── libbrotlidec.1.2.0.dylib -> PIL/.dylibs/libbrotlidec.1.2.0.dylib +│   │   │   ├── libfreetype.6.dylib -> PIL/.dylibs/libfreetype.6.dylib +│   │   │   ├── libharfbuzz.0.dylib -> PIL/.dylibs/libharfbuzz.0.dylib +│   │   │   ├── libjpeg.62.4.0.dylib -> PIL/.dylibs/libjpeg.62.4.0.dylib +│   │   │   ├── liblcms2.2.dylib -> PIL/.dylibs/liblcms2.2.dylib +│   │   │   ├── liblzma.5.dylib -> PIL/.dylibs/liblzma.5.dylib +│   │   │   ├── libopenjp2.2.5.4.dylib -> PIL/.dylibs/libopenjp2.2.5.4.dylib +│   │   │   ├── libpng16.16.dylib -> PIL/.dylibs/libpng16.16.dylib +│   │   │   ├── libpython3.13.dylib +│   │   │   ├── libsharpyuv.0.dylib -> PIL/.dylibs/libsharpyuv.0.dylib +│   │   │   ├── libtiff.6.dylib -> PIL/.dylibs/libtiff.6.dylib +│   │   │   ├── libwebp.7.dylib -> PIL/.dylibs/libwebp.7.dylib +│   │   │   ├── libwebpdemux.2.dylib -> PIL/.dylibs/libwebpdemux.2.dylib +│   │   │   ├── libwebpmux.3.dylib -> PIL/.dylibs/libwebpmux.3.dylib +│   │   │   ├── libxcb.1.1.0.dylib -> PIL/.dylibs/libxcb.1.1.0.dylib +│   │   │   ├── libz.1.3.1.zlib-ng.dylib -> PIL/.dylibs/libz.1.3.1.zlib-ng.dylib +│   │   │   ├── lxml +│   │   │   │   ├── _elementpath.cpython-313-darwin.so +│   │   │   │   ├── builder.cpython-313-darwin.so +│   │   │   │   ├── etree.cpython-313-darwin.so +│   │   │   │   ├── html +│   │   │   │   │   ├── _difflib.cpython-313-darwin.so +│   │   │   │   │   └── diff.cpython-313-darwin.so +│   │   │   │   ├── isoschematron +│   │   │   │   │   └── resources +│   │   │   │   │   ├── rng +│   │   │   │   │   │   └── iso-schematron.rng +│   │   │   │   │   └── xsl +│   │   │   │   │   ├── RNG2Schtrn.xsl +│   │   │   │   │   ├── XSD2Schtrn.xsl +│   │   │   │   │   └── iso-schematron-xslt1 +│   │   │   │   │   ├── iso_abstract_expand.xsl +│   │   │   │   │   ├── iso_dsdl_include.xsl +│   │   │   │   │   ├── iso_schematron_message.xsl +│   │   │   │   │   ├── iso_schematron_skeleton_for_xslt1.xsl +│   │   │   │   │   ├── iso_svrl_for_xslt1.xsl +│   │   │   │   │   └── readme.txt +│   │   │   │   ├── objectify.cpython-313-darwin.so +│   │   │   │   └── sax.cpython-313-darwin.so +│   │   │   ├── multidict +│   │   │   │   └── _multidict.cpython-313-darwin.so +│   │   │   ├── numpy +│   │   │   │   ├── _core +│   │   │   │   │   ├── _multiarray_tests.cpython-313-darwin.so +│   │   │   │   │   └── _multiarray_umath.cpython-313-darwin.so +│   │   │   │   ├── fft +│   │   │   │   │   └── _pocketfft_umath.cpython-313-darwin.so +│   │   │   │   ├── linalg +│   │   │   │   │   └── _umath_linalg.cpython-313-darwin.so +│   │   │   │   └── random +│   │   │   │   ├── _bounded_integers.cpython-313-darwin.so +│   │   │   │   ├── _common.cpython-313-darwin.so +│   │   │   │   ├── _generator.cpython-313-darwin.so +│   │   │   │   ├── _mt19937.cpython-313-darwin.so +│   │   │   │   ├── _pcg64.cpython-313-darwin.so +│   │   │   │   ├── _philox.cpython-313-darwin.so +│   │   │   │   ├── _sfc64.cpython-313-darwin.so +│   │   │   │   ├── bit_generator.cpython-313-darwin.so +│   │   │   │   └── mtrand.cpython-313-darwin.so +│   │   │   ├── numpy-2.4.6.dist-info +│   │   │   │   ├── INSTALLER +│   │   │   │   ├── METADATA +│   │   │   │   ├── RECORD +│   │   │   │   ├── REQUESTED +│   │   │   │   ├── WHEEL +│   │   │   │   ├── entry_points.txt +│   │   │   │   └── licenses +│   │   │   │   ├── LICENSE.txt +│   │   │   │   └── numpy +│   │   │   │   ├── _core +│   │   │   │   │   ├── include +│   │   │   │   │   │   └── numpy +│   │   │   │   │   │   └── libdivide +│   │   │   │   │   │   └── LICENSE.txt +│   │   │   │   │   └── src +│   │   │   │   │   ├── common +│   │   │   │   │   │   └── pythoncapi-compat +│   │   │   │   │   │   └── COPYING +│   │   │   │   │   ├── highway +│   │   │   │   │   │   └── LICENSE +│   │   │   │   │   ├── multiarray +│   │   │   │   │   │   └── dragon4_LICENSE.txt +│   │   │   │   │   ├── npysort +│   │   │   │   │   │   └── x86-simd-sort +│   │   │   │   │   │   └── LICENSE.md +│   │   │   │   │   └── umath +│   │   │   │   │   └── svml +│   │   │   │   │   └── LICENSE +│   │   │   │   ├── fft +│   │   │   │   │   └── pocketfft +│   │   │   │   │   └── LICENSE.md +│   │   │   │   ├── linalg +│   │   │   │   │   └── lapack_lite +│   │   │   │   │   └── LICENSE.txt +│   │   │   │   ├── ma +│   │   │   │   │   └── LICENSE +│   │   │   │   └── random +│   │   │   │   ├── LICENSE.md +│   │   │   │   └── src +│   │   │   │   ├── distributions +│   │   │   │   │   └── LICENSE.md +│   │   │   │   ├── mt19937 +│   │   │   │   │   └── LICENSE.md +│   │   │   │   ├── pcg64 +│   │   │   │   │   └── LICENSE.md +│   │   │   │   ├── philox +│   │   │   │   │   └── LICENSE.md +│   │   │   │   ├── sfc64 +│   │   │   │   │   └── LICENSE.md +│   │   │   │   └── splitmix64 +│   │   │   │   └── LICENSE.md +│   │   │   ├── propcache +│   │   │   │   └── _helpers_c.cpython-313-darwin.so +│   │   │   ├── psutil +│   │   │   │   └── _psutil_osx.abi3.so +│   │   │   ├── pydantic-2.13.4.dist-info +│   │   │   │   ├── INSTALLER +│   │   │   │   ├── METADATA +│   │   │   │   ├── RECORD +│   │   │   │   ├── REQUESTED +│   │   │   │   ├── WHEEL +│   │   │   │   └── licenses +│   │   │   │   └── LICENSE +│   │   │   ├── pydantic_core +│   │   │   │   └── _pydantic_core.cpython-313-darwin.so +│   │   │   ├── setuptools +│   │   │   │   └── _vendor +│   │   │   │   ├── importlib_metadata-8.7.1.dist-info +│   │   │   │   │   ├── INSTALLER +│   │   │   │   │   ├── METADATA +│   │   │   │   │   ├── RECORD +│   │   │   │   │   ├── REQUESTED +│   │   │   │   │   ├── WHEEL +│   │   │   │   │   ├── licenses +│   │   │   │   │   │   └── LICENSE +│   │   │   │   │   └── top_level.txt +│   │   │   │   └── jaraco +│   │   │   │   └── text +│   │   │   │   └── Lorem ipsum.txt +│   │   │   ├── yaml +│   │   │   │   └── _yaml.cpython-313-darwin.so +│   │   │   └── yarl +│   │   │   └── _quoting_c.cpython-313-darwin.so +│   │   └── main +│   └── main.app +│   └── Contents +│   ├── Frameworks +│   │   ├── 81d243bd2c585b0f4821__mypyc.cpython-313-darwin.so +│   │   ├── PIL +│   │   │   ├── __dot__dylibs +│   │   │   │   ├── libXau.6.dylib +│   │   │   │   ├── libavif.16.4.1.dylib +│   │   │   │   ├── libbrotlicommon.1.2.0.dylib +│   │   │   │   ├── libbrotlidec.1.2.0.dylib +│   │   │   │   ├── libfreetype.6.dylib +│   │   │   │   ├── libharfbuzz.0.dylib +│   │   │   │   ├── libjpeg.62.4.0.dylib +│   │   │   │   ├── liblcms2.2.dylib +│   │   │   │   ├── liblzma.5.dylib +│   │   │   │   ├── libopenjp2.2.5.4.dylib +│   │   │   │   ├── libpng16.16.dylib +│   │   │   │   ├── libsharpyuv.0.dylib +│   │   │   │   ├── libtiff.6.dylib +│   │   │   │   ├── libwebp.7.dylib +│   │   │   │   ├── libwebpdemux.2.dylib +│   │   │   │   ├── libwebpmux.3.dylib +│   │   │   │   ├── libxcb.1.1.0.dylib +│   │   │   │   └── libz.1.3.1.zlib-ng.dylib +│   │   │   ├── _avif.cpython-313-darwin.so +│   │   │   ├── _imaging.cpython-313-darwin.so +│   │   │   ├── _imagingcms.cpython-313-darwin.so +│   │   │   ├── _imagingft.cpython-313-darwin.so +│   │   │   ├── _imagingmath.cpython-313-darwin.so +│   │   │   ├── _imagingtk.cpython-313-darwin.so +│   │   │   └── _webp.cpython-313-darwin.so +│   │   ├── PyQt6 +│   │   │   ├── Qt6 +│   │   │   │   ├── lib +│   │   │   │   │   ├── QtCore.framework +│   │   │   │   │   │   ├── QtCore -> Versions/Current/QtCore +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtCore +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── QtDBus.framework +│   │   │   │   │   │   ├── QtDBus -> Versions/Current/QtDBus +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtDBus +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── QtGui.framework +│   │   │   │   │   │   ├── QtGui -> Versions/Current/QtGui +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtGui +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── QtNetwork.framework +│   │   │   │   │   │   ├── QtNetwork -> Versions/Current/QtNetwork +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtNetwork +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── QtPdf.framework +│   │   │   │   │   │   ├── QtPdf -> Versions/Current/QtPdf +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtPdf +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   ├── QtSvg.framework +│   │   │   │   │   │   ├── QtSvg -> Versions/Current/QtSvg +│   │   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   │   └── Versions +│   │   │   │   │   │   ├── A +│   │   │   │   │   │   │   ├── QtSvg +│   │   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   │   └── CodeResources +│   │   │   │   │   │   └── Current -> A +│   │   │   │   │   └── QtWidgets.framework +│   │   │   │   │   ├── QtWidgets -> Versions/Current/QtWidgets +│   │   │   │   │   ├── Resources -> Versions/Current/Resources +│   │   │   │   │   └── Versions +│   │   │   │   │   ├── A +│   │   │   │   │   │   ├── QtWidgets +│   │   │   │   │   │   ├── Resources +│   │   │   │   │   │   │   └── Info.plist +│   │   │   │   │   │   └── _CodeSignature +│   │   │   │   │   │   └── CodeResources +│   │   │   │   │   └── Current -> A +│   │   │   │   ├── plugins +│   │   │   │   │   ├── generic +│   │   │   │   │   │   └── libqtuiotouchplugin.dylib +│   │   │   │   │   ├── iconengines +│   │   │   │   │   │   └── libqsvgicon.dylib +│   │   │   │   │   ├── imageformats +│   │   │   │   │   │   ├── libqgif.dylib +│   │   │   │   │   │   ├── libqicns.dylib +│   │   │   │   │   │   ├── libqico.dylib +│   │   │   │   │   │   ├── libqjpeg.dylib +│   │   │   │   │   │   ├── libqmacheif.dylib +│   │   │   │   │   │   ├── libqmacjp2.dylib +│   │   │   │   │   │   ├── libqpdf.dylib +│   │   │   │   │   │   ├── libqsvg.dylib +│   │   │   │   │   │   ├── libqtga.dylib +│   │   │   │   │   │   ├── libqtiff.dylib +│   │   │   │   │   │   ├── libqwbmp.dylib +│   │   │   │   │   │   └── libqwebp.dylib +│   │   │   │   │   ├── platforms +│   │   │   │   │   │   ├── libqcocoa.dylib +│   │   │   │   │   │   ├── libqminimal.dylib +│   │   │   │   │   │   └── libqoffscreen.dylib +│   │   │   │   │   └── styles +│   │   │   │   │   └── libqmacstyle.dylib +│   │   │   │   └── translations -> ../../../Resources/PyQt6/Qt6/translations +│   │   │   ├── QtCore.abi3.so +│   │   │   ├── QtDBus.abi3.so +│   │   │   ├── QtGui.abi3.so +│   │   │   ├── QtWidgets.abi3.so +│   │   │   └── sip.cpython-313-darwin.so +│   │   ├── QtCore -> PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore +│   │   ├── QtDBus -> PyQt6/Qt6/lib/QtDBus.framework/Versions/A/QtDBus +│   │   ├── QtGui -> PyQt6/Qt6/lib/QtGui.framework/Versions/A/QtGui +│   │   ├── QtNetwork -> PyQt6/Qt6/lib/QtNetwork.framework/Versions/A/QtNetwork +│   │   ├── QtPdf -> PyQt6/Qt6/lib/QtPdf.framework/Versions/A/QtPdf +│   │   ├── QtSvg -> PyQt6/Qt6/lib/QtSvg.framework/Versions/A/QtSvg +│   │   ├── QtWidgets -> PyQt6/Qt6/lib/QtWidgets.framework/Versions/A/QtWidgets +│   │   ├── aiohttp +│   │   │   ├── _http_parser.cpython-313-darwin.so +│   │   │   ├── _http_writer.cpython-313-darwin.so +│   │   │   └── _websocket +│   │   │   ├── mask.cpython-313-darwin.so +│   │   │   └── reader_c.cpython-313-darwin.so +│   │   ├── attrs-26.1.0.dist-info -> ../Resources/attrs-26.1.0.dist-info +│   │   ├── base_library.zip -> ../Resources/base_library.zip +│   │   ├── certifi -> ../Resources/certifi +│   │   ├── charset_normalizer +│   │   │   ├── cd.cpython-313-darwin.so +│   │   │   └── md.cpython-313-darwin.so +│   │   ├── frozenlist +│   │   │   └── _frozenlist.cpython-313-darwin.so +│   │   ├── libXau.6.dylib -> PIL/.dylibs/libXau.6.dylib +│   │   ├── libavif.16.4.1.dylib -> PIL/.dylibs/libavif.16.4.1.dylib +│   │   ├── libbrotlicommon.1.2.0.dylib -> PIL/.dylibs/libbrotlicommon.1.2.0.dylib +│   │   ├── libbrotlidec.1.2.0.dylib -> PIL/.dylibs/libbrotlidec.1.2.0.dylib +│   │   ├── libfreetype.6.dylib -> PIL/.dylibs/libfreetype.6.dylib +│   │   ├── libharfbuzz.0.dylib -> PIL/.dylibs/libharfbuzz.0.dylib +│   │   ├── libjpeg.62.4.0.dylib -> PIL/.dylibs/libjpeg.62.4.0.dylib +│   │   ├── liblcms2.2.dylib -> PIL/.dylibs/liblcms2.2.dylib +│   │   ├── liblzma.5.dylib -> PIL/.dylibs/liblzma.5.dylib +│   │   ├── libopenjp2.2.5.4.dylib -> PIL/.dylibs/libopenjp2.2.5.4.dylib +│   │   ├── libpng16.16.dylib -> PIL/.dylibs/libpng16.16.dylib +│   │   ├── libpython3.13.dylib +│   │   ├── libsharpyuv.0.dylib -> PIL/.dylibs/libsharpyuv.0.dylib +│   │   ├── libtiff.6.dylib -> PIL/.dylibs/libtiff.6.dylib +│   │   ├── libwebp.7.dylib -> PIL/.dylibs/libwebp.7.dylib +│   │   ├── libwebpdemux.2.dylib -> PIL/.dylibs/libwebpdemux.2.dylib +│   │   ├── libwebpmux.3.dylib -> PIL/.dylibs/libwebpmux.3.dylib +│   │   ├── libxcb.1.1.0.dylib -> PIL/.dylibs/libxcb.1.1.0.dylib +│   │   ├── libz.1.3.1.zlib-ng.dylib -> PIL/.dylibs/libz.1.3.1.zlib-ng.dylib +│   │   ├── lxml +│   │   │   ├── _elementpath.cpython-313-darwin.so +│   │   │   ├── builder.cpython-313-darwin.so +│   │   │   ├── etree.cpython-313-darwin.so +│   │   │   ├── html +│   │   │   │   ├── _difflib.cpython-313-darwin.so +│   │   │   │   └── diff.cpython-313-darwin.so +│   │   │   ├── isoschematron -> ../../Resources/lxml/isoschematron +│   │   │   ├── objectify.cpython-313-darwin.so +│   │   │   └── sax.cpython-313-darwin.so +│   │   ├── multidict +│   │   │   └── _multidict.cpython-313-darwin.so +│   │   ├── numpy +│   │   │   ├── _core +│   │   │   │   ├── _multiarray_tests.cpython-313-darwin.so +│   │   │   │   └── _multiarray_umath.cpython-313-darwin.so +│   │   │   ├── fft +│   │   │   │   └── _pocketfft_umath.cpython-313-darwin.so +│   │   │   ├── linalg +│   │   │   │   └── _umath_linalg.cpython-313-darwin.so +│   │   │   └── random +│   │   │   ├── _bounded_integers.cpython-313-darwin.so +│   │   │   ├── _common.cpython-313-darwin.so +│   │   │   ├── _generator.cpython-313-darwin.so +│   │   │   ├── _mt19937.cpython-313-darwin.so +│   │   │   ├── _pcg64.cpython-313-darwin.so +│   │   │   ├── _philox.cpython-313-darwin.so +│   │   │   ├── _sfc64.cpython-313-darwin.so +│   │   │   ├── bit_generator.cpython-313-darwin.so +│   │   │   └── mtrand.cpython-313-darwin.so +│   │   ├── numpy-2.4.6.dist-info -> ../Resources/numpy-2.4.6.dist-info +│   │   ├── propcache +│   │   │   └── _helpers_c.cpython-313-darwin.so +│   │   ├── psutil +│   │   │   └── _psutil_osx.abi3.so +│   │   ├── pydantic-2.13.4.dist-info -> ../Resources/pydantic-2.13.4.dist-info +│   │   ├── pydantic_core +│   │   │   └── _pydantic_core.cpython-313-darwin.so +│   │   ├── setuptools -> ../Resources/setuptools +│   │   ├── yaml +│   │   │   └── _yaml.cpython-313-darwin.so +│   │   └── yarl +│   │   └── _quoting_c.cpython-313-darwin.so +│   ├── Info.plist +│   ├── MacOS +│   │   └── main +│   ├── Resources +│   │   ├── 81d243bd2c585b0f4821__mypyc.cpython-313-darwin.so -> ../Frameworks/81d243bd2c585b0f4821__mypyc.cpython-313-darwin.so +│   │   ├── PIL -> ../Frameworks/PIL +│   │   ├── PyQt6 +│   │   │   ├── Qt6 +│   │   │   │   ├── lib -> ../../../Frameworks/PyQt6/Qt6/lib +│   │   │   │   ├── plugins -> ../../../Frameworks/PyQt6/Qt6/plugins +│   │   │   │   └── translations +│   │   │   │   ├── qt_ar.qm +│   │   │   │   ├── qt_bg.qm +│   │   │   │   ├── qt_ca.qm +│   │   │   │   ├── qt_cs.qm +│   │   │   │   ├── qt_da.qm +│   │   │   │   ├── qt_de.qm +│   │   │   │   ├── qt_en.qm +│   │   │   │   ├── qt_es.qm +│   │   │   │   ├── qt_fa.qm +│   │   │   │   ├── qt_fi.qm +│   │   │   │   ├── qt_fr.qm +│   │   │   │   ├── qt_gd.qm +│   │   │   │   ├── qt_gl.qm +│   │   │   │   ├── qt_he.qm +│   │   │   │   ├── qt_help_ar.qm +│   │   │   │   ├── qt_help_bg.qm +│   │   │   │   ├── qt_help_ca.qm +│   │   │   │   ├── qt_help_cs.qm +│   │   │   │   ├── qt_help_da.qm +│   │   │   │   ├── qt_help_de.qm +│   │   │   │   ├── qt_help_en.qm +│   │   │   │   ├── qt_help_es.qm +│   │   │   │   ├── qt_help_fr.qm +│   │   │   │   ├── qt_help_gl.qm +│   │   │   │   ├── qt_help_hr.qm +│   │   │   │   ├── qt_help_hu.qm +│   │   │   │   ├── qt_help_it.qm +│   │   │   │   ├── qt_help_ja.qm +│   │   │   │   ├── qt_help_ka.qm +│   │   │   │   ├── qt_help_ko.qm +│   │   │   │   ├── qt_help_nl.qm +│   │   │   │   ├── qt_help_nn.qm +│   │   │   │   ├── qt_help_pl.qm +│   │   │   │   ├── qt_help_pt_BR.qm +│   │   │   │   ├── qt_help_ru.qm +│   │   │   │   ├── qt_help_sk.qm +│   │   │   │   ├── qt_help_sl.qm +│   │   │   │   ├── qt_help_sv.qm +│   │   │   │   ├── qt_help_tr.qm +│   │   │   │   ├── qt_help_uk.qm +│   │   │   │   ├── qt_help_zh_CN.qm +│   │   │   │   ├── qt_help_zh_TW.qm +│   │   │   │   ├── qt_hr.qm +│   │   │   │   ├── qt_hu.qm +│   │   │   │   ├── qt_it.qm +│   │   │   │   ├── qt_ja.qm +│   │   │   │   ├── qt_ka.qm +│   │   │   │   ├── qt_ko.qm +│   │   │   │   ├── qt_lg.qm +│   │   │   │   ├── qt_lt.qm +│   │   │   │   ├── qt_lv.qm +│   │   │   │   ├── qt_nl.qm +│   │   │   │   ├── qt_nn.qm +│   │   │   │   ├── qt_pl.qm +│   │   │   │   ├── qt_pt_BR.qm +│   │   │   │   ├── qt_pt_PT.qm +│   │   │   │   ├── qt_ru.qm +│   │   │   │   ├── qt_sk.qm +│   │   │   │   ├── qt_sl.qm +│   │   │   │   ├── qt_sv.qm +│   │   │   │   ├── qt_tr.qm +│   │   │   │   ├── qt_uk.qm +│   │   │   │   ├── qt_zh_CN.qm +│   │   │   │   ├── qt_zh_TW.qm +│   │   │   │   ├── qtbase_ar.qm +│   │   │   │   ├── qtbase_bg.qm +│   │   │   │   ├── qtbase_ca.qm +│   │   │   │   ├── qtbase_cs.qm +│   │   │   │   ├── qtbase_da.qm +│   │   │   │   ├── qtbase_de.qm +│   │   │   │   ├── qtbase_en.qm +│   │   │   │   ├── qtbase_es.qm +│   │   │   │   ├── qtbase_fa.qm +│   │   │   │   ├── qtbase_fi.qm +│   │   │   │   ├── qtbase_fr.qm +│   │   │   │   ├── qtbase_gd.qm +│   │   │   │   ├── qtbase_he.qm +│   │   │   │   ├── qtbase_hr.qm +│   │   │   │   ├── qtbase_hu.qm +│   │   │   │   ├── qtbase_it.qm +│   │   │   │   ├── qtbase_ja.qm +│   │   │   │   ├── qtbase_ka.qm +│   │   │   │   ├── qtbase_ko.qm +│   │   │   │   ├── qtbase_lg.qm +│   │   │   │   ├── qtbase_lv.qm +│   │   │   │   ├── qtbase_nl.qm +│   │   │   │   ├── qtbase_nn.qm +│   │   │   │   ├── qtbase_pl.qm +│   │   │   │   ├── qtbase_pt_BR.qm +│   │   │   │   ├── qtbase_ru.qm +│   │   │   │   ├── qtbase_sk.qm +│   │   │   │   ├── qtbase_sv.qm +│   │   │   │   ├── qtbase_tr.qm +│   │   │   │   ├── qtbase_uk.qm +│   │   │   │   ├── qtbase_zh_CN.qm +│   │   │   │   └── qtbase_zh_TW.qm +│   │   │   ├── QtCore.abi3.so -> ../../Frameworks/PyQt6/QtCore.abi3.so +│   │   │   ├── QtDBus.abi3.so -> ../../Frameworks/PyQt6/QtDBus.abi3.so +│   │   │   ├── QtGui.abi3.so -> ../../Frameworks/PyQt6/QtGui.abi3.so +│   │   │   ├── QtWidgets.abi3.so -> ../../Frameworks/PyQt6/QtWidgets.abi3.so +│   │   │   └── sip.cpython-313-darwin.so -> ../../Frameworks/PyQt6/sip.cpython-313-darwin.so +│   │   ├── QtCore -> PyQt6/Qt6/lib/QtCore.framework/Versions/A/QtCore +│   │   ├── QtDBus -> PyQt6/Qt6/lib/QtDBus.framework/Versions/A/QtDBus +│   │   ├── QtGui -> PyQt6/Qt6/lib/QtGui.framework/Versions/A/QtGui +│   │   ├── QtNetwork -> PyQt6/Qt6/lib/QtNetwork.framework/Versions/A/QtNetwork +│   │   ├── QtPdf -> PyQt6/Qt6/lib/QtPdf.framework/Versions/A/QtPdf +│   │   ├── QtSvg -> PyQt6/Qt6/lib/QtSvg.framework/Versions/A/QtSvg +│   │   ├── QtWidgets -> PyQt6/Qt6/lib/QtWidgets.framework/Versions/A/QtWidgets +│   │   ├── aiohttp -> ../Frameworks/aiohttp +│   │   ├── app.icns +│   │   ├── attrs-26.1.0.dist-info +│   │   │   ├── INSTALLER +│   │   │   ├── METADATA +│   │   │   ├── RECORD +│   │   │   ├── REQUESTED +│   │   │   ├── WHEEL +│   │   │   └── licenses +│   │   │   └── LICENSE +│   │   ├── base_library.zip +│   │   ├── certifi +│   │   │   ├── cacert.pem +│   │   │   └── py.typed +│   │   ├── charset_normalizer -> ../Frameworks/charset_normalizer +│   │   ├── frozenlist -> ../Frameworks/frozenlist +│   │   ├── libXau.6.dylib -> PIL/.dylibs/libXau.6.dylib +│   │   ├── libavif.16.4.1.dylib -> PIL/.dylibs/libavif.16.4.1.dylib +│   │   ├── libbrotlicommon.1.2.0.dylib -> PIL/.dylibs/libbrotlicommon.1.2.0.dylib +│   │   ├── libbrotlidec.1.2.0.dylib -> PIL/.dylibs/libbrotlidec.1.2.0.dylib +│   │   ├── libfreetype.6.dylib -> PIL/.dylibs/libfreetype.6.dylib +│   │   ├── libharfbuzz.0.dylib -> PIL/.dylibs/libharfbuzz.0.dylib +│   │   ├── libjpeg.62.4.0.dylib -> PIL/.dylibs/libjpeg.62.4.0.dylib +│   │   ├── liblcms2.2.dylib -> PIL/.dylibs/liblcms2.2.dylib +│   │   ├── liblzma.5.dylib -> PIL/.dylibs/liblzma.5.dylib +│   │   ├── libopenjp2.2.5.4.dylib -> PIL/.dylibs/libopenjp2.2.5.4.dylib +│   │   ├── libpng16.16.dylib -> PIL/.dylibs/libpng16.16.dylib +│   │   ├── libpython3.13.dylib -> ../Frameworks/libpython3.13.dylib +│   │   ├── libsharpyuv.0.dylib -> PIL/.dylibs/libsharpyuv.0.dylib +│   │   ├── libtiff.6.dylib -> PIL/.dylibs/libtiff.6.dylib +│   │   ├── libwebp.7.dylib -> PIL/.dylibs/libwebp.7.dylib +│   │   ├── libwebpdemux.2.dylib -> PIL/.dylibs/libwebpdemux.2.dylib +│   │   ├── libwebpmux.3.dylib -> PIL/.dylibs/libwebpmux.3.dylib +│   │   ├── libxcb.1.1.0.dylib -> PIL/.dylibs/libxcb.1.1.0.dylib +│   │   ├── libz.1.3.1.zlib-ng.dylib -> PIL/.dylibs/libz.1.3.1.zlib-ng.dylib +│   │   ├── lxml +│   │   │   ├── _elementpath.cpython-313-darwin.so -> ../../Frameworks/lxml/_elementpath.cpython-313-darwin.so +│   │   │   ├── builder.cpython-313-darwin.so -> ../../Frameworks/lxml/builder.cpython-313-darwin.so +│   │   │   ├── etree.cpython-313-darwin.so -> ../../Frameworks/lxml/etree.cpython-313-darwin.so +│   │   │   ├── html -> ../../Frameworks/lxml/html +│   │   │   ├── isoschematron +│   │   │   │   └── resources +│   │   │   │   ├── rng +│   │   │   │   │   └── iso-schematron.rng +│   │   │   │   └── xsl +│   │   │   │   ├── RNG2Schtrn.xsl +│   │   │   │   ├── XSD2Schtrn.xsl +│   │   │   │   └── iso-schematron-xslt1 +│   │   │   │   ├── iso_abstract_expand.xsl +│   │   │   │   ├── iso_dsdl_include.xsl +│   │   │   │   ├── iso_schematron_message.xsl +│   │   │   │   ├── iso_schematron_skeleton_for_xslt1.xsl +│   │   │   │   ├── iso_svrl_for_xslt1.xsl +│   │   │   │   └── readme.txt +│   │   │   ├── objectify.cpython-313-darwin.so -> ../../Frameworks/lxml/objectify.cpython-313-darwin.so +│   │   │   └── sax.cpython-313-darwin.so -> ../../Frameworks/lxml/sax.cpython-313-darwin.so +│   │   ├── multidict -> ../Frameworks/multidict +│   │   ├── numpy -> ../Frameworks/numpy +│   │   ├── numpy-2.4.6.dist-info +│   │   │   ├── INSTALLER +│   │   │   ├── METADATA +│   │   │   ├── RECORD +│   │   │   ├── REQUESTED +│   │   │   ├── WHEEL +│   │   │   ├── entry_points.txt +│   │   │   └── licenses +│   │   │   ├── LICENSE.txt +│   │   │   └── numpy +│   │   │   ├── _core +│   │   │   │   ├── include +│   │   │   │   │   └── numpy +│   │   │   │   │   └── libdivide +│   │   │   │   │   └── LICENSE.txt +│   │   │   │   └── src +│   │   │   │   ├── common +│   │   │   │   │   └── pythoncapi-compat +│   │   │   │   │   └── COPYING +│   │   │   │   ├── highway +│   │   │   │   │   └── LICENSE +│   │   │   │   ├── multiarray +│   │   │   │   │   └── dragon4_LICENSE.txt +│   │   │   │   ├── npysort +│   │   │   │   │   └── x86-simd-sort +│   │   │   │   │   └── LICENSE.md +│   │   │   │   └── umath +│   │   │   │   └── svml +│   │   │   │   └── LICENSE +│   │   │   ├── fft +│   │   │   │   └── pocketfft +│   │   │   │   └── LICENSE.md +│   │   │   ├── linalg +│   │   │   │   └── lapack_lite +│   │   │   │   └── LICENSE.txt +│   │   │   ├── ma +│   │   │   │   └── LICENSE +│   │   │   └── random +│   │   │   ├── LICENSE.md +│   │   │   └── src +│   │   │   ├── distributions +│   │   │   │   └── LICENSE.md +│   │   │   ├── mt19937 +│   │   │   │   └── LICENSE.md +│   │   │   ├── pcg64 +│   │   │   │   └── LICENSE.md +│   │   │   ├── philox +│   │   │   │   └── LICENSE.md +│   │   │   ├── sfc64 +│   │   │   │   └── LICENSE.md +│   │   │   └── splitmix64 +│   │   │   └── LICENSE.md +│   │   ├── propcache -> ../Frameworks/propcache +│   │   ├── psutil -> ../Frameworks/psutil +│   │   ├── pydantic-2.13.4.dist-info +│   │   │   ├── INSTALLER +│   │   │   ├── METADATA +│   │   │   ├── RECORD +│   │   │   ├── REQUESTED +│   │   │   ├── WHEEL +│   │   │   └── licenses +│   │   │   └── LICENSE +│   │   ├── pydantic_core -> ../Frameworks/pydantic_core +│   │   ├── setuptools +│   │   │   └── _vendor +│   │   │   ├── importlib_metadata-8.7.1.dist-info +│   │   │   │   ├── INSTALLER +│   │   │   │   ├── METADATA +│   │   │   │   ├── RECORD +│   │   │   │   ├── REQUESTED +│   │   │   │   ├── WHEEL +│   │   │   │   ├── licenses +│   │   │   │   │   └── LICENSE +│   │   │   │   └── top_level.txt +│   │   │   └── jaraco +│   │   │   └── text +│   │   │   └── Lorem ipsum.txt +│   │   ├── yaml -> ../Frameworks/yaml +│   │   └── yarl -> ../Frameworks/yarl +│   └── _CodeSignature +│   └── CodeResources +├── doc +│   ├── Notes.md +│   ├── Notes.pdf +│   └── images +│   ├── image-01.png +│   ├── image-02.png +│   └── image-03.png +├── docling_output.md +├── exported_phases.csv +├── main.py +├── main.spec +├── media +├── migrate_database.py +├── pyproject.toml +├── requirements.txt +├── spanish_alphabetical.txt +├── spanish_glossary_sorted.txt +├── spanish_glossary_sorted.xlsx +├── spanish_glossary_sorted_B.pdf +├── spanish_glossary_sorted_B.txt +├── spanish_glossary_sorted_B.xlsx +├── spanish_trainer-26-08-21.db +├── spanish_trainer-backup.db +├── spanish_trainer.db +├── spanish_trainer_backup.sql +├── spanish_trainer_legacy.db +├── tabs +│   ├── __init__.py +│   ├── __pycache__ +│   │   ├── __init__.cpython-313.pyc +│   │   ├── review_tab.cpython-313.pyc +│   │   ├── sandbox_tab.cpython-313.pyc +│   │   └── settings_tab.cpython-313.pyc +│   ├── review_tab.py +│   ├── sandbox_tab.py +│   └── settings_tab.py +├── test_dual.py +├── test_tts.py +├── tree.txt +├── tts_utils.py +├── ui +│   ├── __init__.py +│   ├── cli +│   │   └── interface.py +│   └── gui +│   ├── create_mode.py +│   ├── interface.py +│   └── trainer_mode.py +├── uv.lock +└── video_generator.py + +289 directories, 690 files diff --git a/tree2.txt b/tree2.txt new file mode 100644 index 0000000..14de4dc --- /dev/null +++ b/tree2.txt @@ -0,0 +1,69 @@ +. +├── LICENSE +├── README.md +├── README.pdf +├── Santiago_cathedral_2021_Sunset.jpg +├── anki_exporter.py +├── app.icns +├── app_icon.iconset +│   ├── icon_128x128.png +│   ├── icon_128x128@2x.png +│   ├── icon_16x16.png +│   ├── icon_16x16@2x.png +│   ├── icon_256x256.png +│   ├── icon_256x256@2x.png +│   ├── icon_32x32.png +│   ├── icon_32x32@2x.png +│   ├── icon_512x512.png +│   └── icon_512x512@2x.png +├── aula_int_plus_1_glos_en_alfa.pdf +├── base_icon.png +├── core +│   ├── __init__.py +│   ├── asset_generator.py +│   ├── audio_engine.py +│   ├── bulk_importer.py +│   ├── clean_glossary.py +│   └── phrase_manager.py +├── database.py +├── doc +│   ├── Notes.md +│   ├── Notes.pdf +│   └── images +├── docling_output.md +├── exported_phases.csv +├── main.py +├── main.spec +├── media +├── migrate_database.py +├── pyproject.toml +├── requirements.txt +├── spanish_alphabetical.txt +├── spanish_glossary_sorted.txt +├── spanish_glossary_sorted.xlsx +├── spanish_glossary_sorted_B.pdf +├── spanish_glossary_sorted_B.txt +├── spanish_glossary_sorted_B.xlsx +├── spanish_trainer-26-08-21.db +├── spanish_trainer-backup.db +├── spanish_trainer.db +├── spanish_trainer_backup.sql +├── spanish_trainer_legacy.db +├── tabs +│   ├── __init__.py +│   ├── review_tab.py +│   ├── sandbox_tab.py +│   └── settings_tab.py +├── test_dual.py +├── test_tts.py +├── tree.txt +├── tree2.txt +├── tts_utils.py +├── ui +│   ├── __init__.py +│   ├── cli +│   └── gui +├── uv.lock +└── video_generator.py + +10 directories, 57 files diff --git a/uv.lock b/uv.lock index 04d72ca..244f1be 100644 --- a/uv.lock +++ b/uv.lock @@ -24,6 +24,7 @@ dependencies = [ { name = "librosa" }, { name = "numpy" }, { name = "pillow" }, + { name = "pyinstaller" }, { name = "pyqt6" }, { name = "scipy" }, { name = "sounddevice" }, @@ -38,6 +39,7 @@ requires-dist = [ { name = "librosa", specifier = ">=0.11.0" }, { name = "numpy", specifier = ">=2.4.6" }, { name = "pillow", specifier = ">=12.2.0" }, + { name = "pyinstaller", specifier = ">=6.21.0" }, { name = "pyqt6", specifier = ">=6.11.0" }, { name = "scipy", specifier = ">=1.17.1" }, { name = "sounddevice", specifier = ">=0.5.5" }, @@ -163,6 +165,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "altgraph" +version = "0.17.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/f8/97fdf103f38fed6792a1601dbc16cc8aac56e7459a9fff08c812d8ae177a/altgraph-0.17.5.tar.gz", hash = "sha256:c87b395dd12fabde9c99573a9749d67da8d29ef9de0125c7f536699b4a9bc9e7", size = 48428, upload-time = "2025-11-21T20:35:50.583Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/ba/000a1996d4308bc65120167c21241a3b205464a2e0b58deda26ae8ac21d1/altgraph-0.17.5-py2.py3-none-any.whl", hash = "sha256:f3a22400bce1b0c701683820ac4f3b159cd301acab067c51c653e06961600597", size = 21228, upload-time = "2025-11-21T20:35:49.444Z" }, +] + [[package]] name = "annotated-doc" version = "0.0.4" @@ -1152,6 +1163,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/2c/0f1e93c636720e8a3eb59af2bfda99d98b55891e1c53bc30c2e0e865f01b/lxml-6.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:58bb955caba94e467d2a96da17660d2d704e0675894cba21ab8a775b8621fd1c", size = 3817223, upload-time = "2026-05-19T19:22:56.823Z" }, ] +[[package]] +name = "macholib" +version = "1.16.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph", marker = "sys_platform == 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/2f/97589876ea967487978071c9042518d28b958d87b17dceb7cdc1d881f963/macholib-1.16.4.tar.gz", hash = "sha256:f408c93ab2e995cd2c46e34fe328b130404be143469e41bc366c807448979362", size = 59427, upload-time = "2025-11-22T08:28:38.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/d1/a9f36f8ecdf0fb7c9b1e78c8d7af12b8c8754e74851ac7b94a8305540fc7/macholib-1.16.4-py2.py3-none-any.whl", hash = "sha256:da1a3fa8266e30f0ce7e97c6a54eefaae8edd1e5f86f3eb8b95457cae90265ea", size = 38117, upload-time = "2025-11-22T08:28:36.939Z" }, +] + [[package]] name = "mail-parser" version = "4.4.0" @@ -1744,6 +1767,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, ] +[[package]] +name = "pefile" +version = "2024.8.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/4f/2750f7f6f025a1507cd3b7218691671eecfd0bbebebe8b39aa0fe1d360b8/pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632", size = 76008, upload-time = "2024-08-26T20:58:38.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/16/12b82f791c7f50ddec566873d5bdd245baa1491bac11d15ffb98aecc8f8b/pefile-2024.8.26-py3-none-any.whl", hash = "sha256:76f8b485dcd3b1bb8166f1128d395fa3d87af26360c2358fb75b80019b957c6f", size = 74766, upload-time = "2024-08-26T21:01:02.632Z" }, +] + [[package]] name = "pillow" version = "12.2.0" @@ -2079,6 +2111,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pyinstaller" +version = "6.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altgraph" }, + { name = "macholib", marker = "sys_platform == 'darwin'" }, + { name = "packaging" }, + { name = "pefile", marker = "sys_platform == 'win32'" }, + { name = "pyinstaller-hooks-contrib" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "setuptools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/4d/ec706c3fcf39e26888c35b39615ff4d5865d184069666c47492cff1fbe50/pyinstaller-6.21.0.tar.gz", hash = "sha256:bb9fab705983e393a2d1cac77d6972513057ad800215fd861dc15ff5272e98fd", size = 4061519, upload-time = "2026-06-13T14:15:06.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/4a/53cf98bf66daed012dc9cd78c8203f19a675d696f2fc12afcf8c5049a0e0/pyinstaller-6.21.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:327d132389f37912609e01be62810cf96b5aa95b613903e4b8692e0d12fb0eda", size = 1052350, upload-time = "2026-06-13T14:13:55.88Z" }, + { url = "https://files.pythonhosted.org/packages/30/83/b591295c352ef464c50b4c6ffff1c4f771d875c9e833f578d1b9f564f6b3/pyinstaller-6.21.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7071d4b094d5b40deeef5fa3d3b98a1b846087f7562b49209663d5f9281fe251", size = 748477, upload-time = "2026-06-13T14:14:00.327Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8f/88fff4e403873b1e22286911350e75ff00db014aa08e57045da9d4328993/pyinstaller-6.21.0-py3-none-manylinux2014_i686.whl", hash = "sha256:6b6374d652107dd4a2eeece903ff82bb4045bb5e1006c5a158a6dcdbefe84bf2", size = 760877, upload-time = "2026-06-13T14:14:04.836Z" }, + { url = "https://files.pythonhosted.org/packages/8a/13/f0e48fbdfd1d05d948157121cea8b1b823dcb89efe6934b71fdd8bdb3f0f/pyinstaller-6.21.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:4e3108b3f02384560da70e39b8bf22b0ad597d02bd68a40d76ea91c1cfa00cad", size = 759194, upload-time = "2026-06-13T14:14:10.61Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d5/ea7878cf9924ed30d946d8288777424e6d069d94f5bde56b4d0890069664/pyinstaller-6.21.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:697532279f535ad572bda613db4f821540e235c7854ca6da4d3bf0373f4415ee", size = 754979, upload-time = "2026-06-13T14:14:15.226Z" }, + { url = "https://files.pythonhosted.org/packages/9f/09/51b8905714b733bac66dbc041a7821372d70d888d273ae474c4037d4202d/pyinstaller-6.21.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:605169523a6b5ace39f13dfbff21add9f2bc43df99c7daf9394fefb2c45e8b6f", size = 754812, upload-time = "2026-06-13T14:14:20.264Z" }, + { url = "https://files.pythonhosted.org/packages/4b/43/d77779439d8c6c2e27a77bcfbd1d5cc0f568ebb611bb472b11af81b5f177/pyinstaller-6.21.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5fa56746c1e76f93634d018502301378a2d0c382553d37d8c3c34ff436c12dd1", size = 753887, upload-time = "2026-06-13T14:14:25.268Z" }, + { url = "https://files.pythonhosted.org/packages/51/8f/c22df1f6837784ac349057ba693f08e7b1ca7a0e06f9c33c63bc6280007b/pyinstaller-6.21.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:42395ec76df8e8120c36b13339d9db8cab83e316a12839ee303cc00fc941bb74", size = 753779, upload-time = "2026-06-13T14:14:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/c9/76/1ce8a27ce62ba8cf3a87c9ce6d575610f4e55d7cb0123e7512fc3f4b921a/pyinstaller-6.21.0-py3-none-win32.whl", hash = "sha256:c6b28d30d8fd99ce162ff3aab5013ed44dbfb747566b1f01b9bed7964d7c14e9", size = 1336462, upload-time = "2026-06-13T14:14:35.785Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/ca1d7e5257dd8566a9dfc0dfb02f8a8075eeb53d4b2d3c579f1276759042/pyinstaller-6.21.0-py3-none-win_amd64.whl", hash = "sha256:7fae06c494ce0ebfe6bd3055c0e409def884f63af2e3705d06bd431ad9237fc7", size = 1397487, upload-time = "2026-06-13T14:14:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/dc/75/21b51523ce8d96629b71311775a0a65f5f5a872124ab0de33e5c848f8bff/pyinstaller-6.21.0-py3-none-win_arm64.whl", hash = "sha256:f13c95c9c03fb567217135919f93815c305813126780b0ed6e0123cb8acaf025", size = 1346094, upload-time = "2026-06-13T14:14:48.914Z" }, +] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2026.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/5b/c9fe0db5e83ee1c39b2258fa21d23b15e1a60786b6c5990ee5074ead8bb6/pyinstaller_hooks_contrib-2026.6.tar.gz", hash = "sha256:bef5002c32f4f50bd55b005da12cff64eca8783e7eaf86a06a62410164bab725", size = 173354, upload-time = "2026-06-08T22:37:16.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/31/f2d7343d8ed5f7c4678377886f6ce533e6eaaa131b252ce950114c2a7efa/pyinstaller_hooks_contrib-2026.6-py3-none-any.whl", hash = "sha256:fd13b8ac126b35361175edacd41a0d97080b75dd5f4b594ecefefff969509dd3", size = 457159, upload-time = "2026-06-08T22:37:14.722Z" }, +] + [[package]] name = "pylatexenc" version = "2.10" @@ -2227,6 +2299,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" }, ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3"