Surely cleaning up all this mess wont break anything. surely
This commit is contained in:
parent
563299ba21
commit
4dd9d2bbd4
38 changed files with 16162 additions and 15976 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -18,8 +18,8 @@ __pycache__
|
||||||
|
|
||||||
!assets/*.png
|
!assets/*.png
|
||||||
!.pre-commit-config.yaml
|
!.pre-commit-config.yaml
|
||||||
!prompt.txt
|
!data/prompt.txt
|
||||||
!vocab_base.txt
|
!data/vocab_base.txt
|
||||||
!requirements.txt
|
!requirements.txt
|
||||||
!util/tl_inspector/TLInspector.js
|
!util/tl_inspector/TLInspector.js
|
||||||
!util/forge/Forge_MZ.js
|
!util/forge/Forge_MZ.js
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@
|
||||||
'hooks':
|
'hooks':
|
||||||
- 'id': 'set-defaults'
|
- 'id': 'set-defaults'
|
||||||
'name': 'Set Defaults in rpgmakermvmz.py'
|
'name': 'Set Defaults in rpgmakermvmz.py'
|
||||||
'entry': 'python set_defaults.py'
|
'entry': 'python -m util.defaults'
|
||||||
'language': 'python'
|
'language': 'python'
|
||||||
'files': '^modules/rpgmakermvmz.py$'
|
'files': '^modules/rpgmakermvmz.py$'
|
||||||
|
|
|
||||||
10
DazedMTLTool.desktop
Normal file
10
DazedMTLTool.desktop
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=DazedMTLTool
|
||||||
|
GenericName=Translation Tool
|
||||||
|
Comment=AI translation tool for visual novels and RPG games
|
||||||
|
Exec=bash -c 'exec "$(cd "$(dirname "%k")" && pwd)/scripts/launch.sh"'
|
||||||
|
Icon=%k/assets/icon.png
|
||||||
|
Terminal=false
|
||||||
|
Categories=Development;Utility;
|
||||||
|
StartupWMClass=DazedMTLTool
|
||||||
23
README.md
23
README.md
|
|
@ -124,12 +124,11 @@ This means Python wasn't added to your PATH. You have two options:
|
||||||
|
|
||||||
### 3. Launch the GUI
|
### 3. Launch the GUI
|
||||||
|
|
||||||
**Double-click `START.bat`**. It will:
|
**Windows:** Double-click `START.bat`. It will create a virtual environment, install dependencies, and open the GUI.
|
||||||
- Create a virtual environment automatically.
|
|
||||||
- Install all dependencies.
|
|
||||||
- Launch the GUI.
|
|
||||||
|
|
||||||
That's it! From now on, just double-click `START.bat` to open the tool.
|
**Linux/macOS:** Run `./START.sh`, or double-click `DazedMTLTool.desktop` (choose **Allow Launching** when your file manager asks). From then on, either method works.
|
||||||
|
|
||||||
|
That's it! Use the same launcher each time you want to open the tool.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -160,10 +159,10 @@ Specialized tabs with extra options for those specific engines.
|
||||||
|
|
||||||
## Vocab & Prompt
|
## Vocab & Prompt
|
||||||
|
|
||||||
### vocab.txt
|
### data/vocab.txt
|
||||||
This file gives the AI context about your game — character names, genders, recurring terms, etc. The better your vocab file, the more consistent the translation.
|
This file gives the AI context about your game — character names, genders, recurring terms, etc. The better your vocab file, the more consistent the translation.
|
||||||
|
|
||||||
Open `vocab.txt` (or copy `vocab.txt.example` to `vocab.txt` if it doesn't exist) and add entries like:
|
On first run, `data/vocab.txt` is created automatically from `data/vocab_base.txt` if it does not exist yet. Add entries like:
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
# Game Characters
|
# Game Characters
|
||||||
|
|
@ -176,14 +175,14 @@ Format: Japanese name, English name in parentheses, then gender.
|
||||||
|
|
||||||
> **Note:** A very large vocab file can increase API costs and potentially reduce quality. Focus on the most important characters and terms.
|
> **Note:** A very large vocab file can increase API costs and potentially reduce quality. Focus on the most important characters and terms.
|
||||||
|
|
||||||
### prompt.txt
|
### data/prompt.txt
|
||||||
This is the system prompt sent to the AI. A default `prompt.txt` is included and works well for most games. You generally don't need to edit it unless you want to customize the translation style.
|
This is the system prompt sent to the AI. A default `data/prompt.txt` is included and works well for most games. You generally don't need to edit it unless you want to customize the translation style.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
- **Check `log/translations.txt`** after a run to see what was translated. You can copy useful terms from it into `vocab.txt` for consistency in future runs.
|
- **Check `log/translations.txt`** after a run to see what was translated. You can copy useful terms from it into `data/vocab.txt` for consistency in future runs.
|
||||||
- **Start small** — Translate a few files first to make sure the output looks good before doing the whole game.
|
- **Start small** — Translate a few files first to make sure the output looks good before doing the whole game.
|
||||||
- **Wordwrap** — If text overflows or looks awkward in-game, adjust the `width` setting in `.env` or the Config tab. `60` is a good default for most RPG Maker games.
|
- **Wordwrap** — If text overflows or looks awkward in-game, adjust the `width` setting in `.env` or the Config tab. `60` is a good default for most RPG Maker games.
|
||||||
- **Version control** — Using [Git](https://git-scm.com/) with the game folder is highly recommended. It lets you track every change the translation makes, compare with original files, and roll back if needed.
|
- **Version control** — Using [Git](https://git-scm.com/) with the game folder is highly recommended. It lets you track every change the translation makes, compare with original files, and roll back if needed.
|
||||||
|
|
@ -300,8 +299,8 @@ Here's the recommended step-by-step process for translating an RPG Maker MV/MZ g
|
||||||
|
|
||||||
| Step | Action |
|
| Step | Action |
|
||||||
|------|--------|
|
|------|--------|
|
||||||
| **1** | **Parse speakers → vocab.txt** — Use the Parse Speakers feature to pull character names from the game files into `vocab.txt`. |
|
| **1** | **Parse speakers → vocab** — Use the Parse Speakers feature to pull character names from the game files into `data/vocab.txt`. |
|
||||||
| **2** | **Identify speaker genders** — Figure out which characters are male/female and update `vocab.txt` accordingly. This helps the AI use correct pronouns. |
|
| **2** | **Identify speaker genders** — Figure out which characters are male/female and update `data/vocab.txt` accordingly. This helps the AI use correct pronouns. |
|
||||||
| **3** | **Translate Actors.json, MapInfos.json** — These are small files with character and map names. Good to do first. |
|
| **3** | **Translate Actors.json, MapInfos.json** — These are small files with character and map names. Good to do first. |
|
||||||
| **4** | **Translate Items, System, Weapons, etc.** — All the data files that aren't maps or events. Place them in `files/`, translate, then copy results back. |
|
| **4** | **Translate Items, System, Weapons, etc.** — All the data files that aren't maps or events. Place them in `files/`, translate, then copy results back. |
|
||||||
| **5** | **Find speaker names** — Enable CODE 101 (Speakers), check for bracketed names, or use the "First Line = Speaker" option to capture speaker names properly. |
|
| **5** | **Find speaker names** — Enable CODE 101 (Speakers), check for bracketed names, or use the "First Line = Speaker" option to capture speaker names properly. |
|
||||||
|
|
|
||||||
25
START.bat
25
START.bat
|
|
@ -184,28 +184,29 @@ echo Launching DazedMTLTool GUI...
|
||||||
echo ==========================================
|
echo ==========================================
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
:: Ensure vocab.txt exists (create from example if available)
|
:: Ensure data/vocab.txt exists (create from vocab_base.txt if available)
|
||||||
if not exist "vocab.txt" (
|
if not exist "data\\vocab.txt" (
|
||||||
if exist "vocab.txt.example" (
|
if not exist "data" mkdir data
|
||||||
echo vocab.txt not found - creating from vocab.txt.example...
|
if exist "data\\vocab_base.txt" (
|
||||||
copy /Y "vocab.txt.example" "vocab.txt" >nul 2>&1
|
echo data\vocab.txt not found - creating from data\vocab_base.txt...
|
||||||
|
copy /Y "data\vocab_base.txt" "data\vocab.txt" >nul 2>&1
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ERROR: Failed to copy vocab.txt.example to vocab.txt.
|
echo ERROR: Failed to copy data\vocab_base.txt to data\vocab.txt.
|
||||||
) else (
|
) else (
|
||||||
echo Created vocab.txt from vocab.txt.example
|
echo Created data\vocab.txt from data\vocab_base.txt
|
||||||
)
|
)
|
||||||
) else (
|
) else (
|
||||||
echo vocab.txt and vocab.txt.example not found - creating empty vocab.txt to avoid import errors...
|
echo data\vocab.txt not found - creating empty file to avoid import errors...
|
||||||
type NUL > "vocab.txt"
|
type NUL > "data\vocab.txt"
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ERROR: Failed to create empty vocab.txt.
|
echo ERROR: Failed to create empty data\vocab.txt.
|
||||||
) else (
|
) else (
|
||||||
echo Created empty vocab.txt
|
echo Created empty data\vocab.txt
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
python start_gui.py
|
python scripts/start_gui.py
|
||||||
|
|
||||||
:: Check if GUI launched successfully
|
:: Check if GUI launched successfully
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
|
|
|
||||||
23
START.sh
23
START.sh
|
|
@ -127,23 +127,24 @@ activate_venv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_vocab_file() {
|
ensure_vocab_file() {
|
||||||
if [[ -f "vocab.txt" ]]; then
|
mkdir -p data
|
||||||
|
if [[ -f "data/vocab.txt" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "vocab.txt.example" ]]; then
|
if [[ -f "data/vocab_base.txt" ]]; then
|
||||||
echo "vocab.txt not found - creating from vocab.txt.example..."
|
echo "data/vocab.txt not found - creating from data/vocab_base.txt..."
|
||||||
if cp "vocab.txt.example" "vocab.txt"; then
|
if cp "data/vocab_base.txt" "data/vocab.txt"; then
|
||||||
echo "Created vocab.txt from vocab.txt.example"
|
echo "Created data/vocab.txt from data/vocab_base.txt"
|
||||||
else
|
else
|
||||||
echo "ERROR: Failed to copy vocab.txt.example to vocab.txt."
|
echo "ERROR: Failed to copy data/vocab_base.txt to data/vocab.txt."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "vocab.txt and vocab.txt.example not found - creating empty vocab.txt to avoid import errors..."
|
echo "data/vocab.txt not found - creating empty file to avoid import errors..."
|
||||||
if : > "vocab.txt"; then
|
if : > "data/vocab.txt"; then
|
||||||
echo "Created empty vocab.txt"
|
echo "Created empty data/vocab.txt"
|
||||||
else
|
else
|
||||||
echo "ERROR: Failed to create empty vocab.txt."
|
echo "ERROR: Failed to create empty data/vocab.txt."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +228,7 @@ echo
|
||||||
|
|
||||||
ensure_vocab_file
|
ensure_vocab_file
|
||||||
|
|
||||||
if ! python start_gui.py; then
|
if ! exec python "$SCRIPT_DIR/scripts/start_gui.py"; then
|
||||||
echo
|
echo
|
||||||
echo "ERROR: Failed to launch GUI."
|
echo "ERROR: Failed to launch GUI."
|
||||||
echo "Check the error messages above."
|
echo "Check the error messages above."
|
||||||
|
|
|
||||||
29
gui/main.py
29
gui/main.py
|
|
@ -19,20 +19,16 @@ from PyQt5.QtWidgets import (
|
||||||
QTextEdit, QSplitter, QGroupBox, QStatusBar, QStackedWidget, QToolButton,
|
QTextEdit, QSplitter, QGroupBox, QStatusBar, QStackedWidget, QToolButton,
|
||||||
QDialog
|
QDialog
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSettings
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QSettings, QCoreApplication
|
||||||
from PyQt5.QtGui import QIcon, QFont, QPixmap, QScreen
|
from PyQt5.QtGui import QIcon, QFont, QPixmap, QScreen, QGuiApplication
|
||||||
|
|
||||||
|
|
||||||
def _project_root() -> Path:
|
from util.paths import PROJECT_ROOT, ICON_PATH, LAST_UPDATE_SHA_PATH
|
||||||
"""Directory containing start_gui.py / requirements.txt (parent of gui/)."""
|
|
||||||
return Path(__file__).resolve().parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
def load_application_icon() -> QIcon:
|
def load_application_icon() -> QIcon:
|
||||||
"""Prefer ICO on Windows; PNG is fine for all platforms. Empty if missing."""
|
"""Prefer ICO on Windows; PNG is fine for all platforms. Empty if missing."""
|
||||||
root = _project_root()
|
for path in (PROJECT_ROOT / "assets/icon.ico", ICON_PATH):
|
||||||
for rel in ("assets/icon.ico", "assets/icon.png"):
|
|
||||||
path = root / rel
|
|
||||||
if path.is_file():
|
if path.is_file():
|
||||||
icon = QIcon(str(path))
|
icon = QIcon(str(path))
|
||||||
if not icon.isNull():
|
if not icon.isNull():
|
||||||
|
|
@ -126,14 +122,13 @@ class UpdateThread(QThread):
|
||||||
REPO_USER = "DazedAnon"
|
REPO_USER = "DazedAnon"
|
||||||
REPO_NAME = "DazedMTLTool"
|
REPO_NAME = "DazedMTLTool"
|
||||||
REPO_BRANCH = "main"
|
REPO_BRANCH = "main"
|
||||||
SHA_FILE = "last_update_sha.txt"
|
SHA_FILE = str(LAST_UPDATE_SHA_PATH)
|
||||||
|
|
||||||
# Paths (relative, top-level) that should never be touched during update
|
# Paths (relative, top-level) that should never be touched during update
|
||||||
PROTECTED = {".env", "venv", "log", "files", "translated",
|
PROTECTED = {".env", "venv", "log", "files", "translated", "data"}
|
||||||
"vocab.txt", "last_update_sha.txt"}
|
|
||||||
|
|
||||||
# GitLab zip archives do not preserve Unix execute bits; restore after apply.
|
# GitLab zip archives do not preserve Unix execute bits; restore after apply.
|
||||||
EXECUTABLE_SUFFIXES = {".sh"}
|
EXECUTABLE_SUFFIXES = {".sh", ".desktop"}
|
||||||
|
|
||||||
progress = pyqtSignal(str) # status message
|
progress = pyqtSignal(str) # status message
|
||||||
finished = pyqtSignal(bool, str) # (success, message)
|
finished = pyqtSignal(bool, str) # (success, message)
|
||||||
|
|
@ -903,6 +898,14 @@ def main():
|
||||||
if hasattr(QApplication, 'setHighDpiScaleFactorRoundingPolicy'):
|
if hasattr(QApplication, 'setHighDpiScaleFactorRoundingPolicy'):
|
||||||
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
||||||
|
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
|
from util.linux_desktop import ensure_linux_desktop_entry
|
||||||
|
ensure_linux_desktop_entry()
|
||||||
|
QGuiApplication.setDesktopFileName("DazedMTLTool")
|
||||||
|
|
||||||
|
QCoreApplication.setOrganizationName("DazedTranslations")
|
||||||
|
QCoreApplication.setApplicationName("DazedMTLTool")
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
# Windows taskbar groups by executable (python.exe) unless we set an explicit
|
# Windows taskbar groups by executable (python.exe) unless we set an explicit
|
||||||
|
|
@ -934,9 +937,7 @@ def main():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Set application properties
|
# Set application properties
|
||||||
app.setApplicationName("DazedMTLTool")
|
|
||||||
app.setApplicationVersion("1.0")
|
app.setApplicationVersion("1.0")
|
||||||
app.setOrganizationName("DazedTranslations")
|
|
||||||
|
|
||||||
# Apply dark theme with cleaner, more compact styling
|
# Apply dark theme with cleaner, more compact styling
|
||||||
app.setStyleSheet("""
|
app.setStyleSheet("""
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ except ImportError:
|
||||||
ConfigIntegration = None
|
ConfigIntegration = None
|
||||||
try:
|
try:
|
||||||
# Prefer importing the project's canonical defaults if available
|
# Prefer importing the project's canonical defaults if available
|
||||||
import set_defaults
|
from util.defaults import DEFAULTS as CANONICAL_DEFAULTS
|
||||||
CANONICAL_DEFAULTS = getattr(set_defaults, 'DEFAULTS', None)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
CANONICAL_DEFAULTS = None
|
CANONICAL_DEFAULTS = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,7 @@ import re
|
||||||
from gui.platform_glyph import platform_glyph
|
from gui.platform_glyph import platform_glyph
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import set_defaults
|
from util.defaults import DEFAULTS as CANONICAL_DEFAULTS
|
||||||
CANONICAL_DEFAULTS = getattr(set_defaults, 'DEFAULTS', None)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
CANONICAL_DEFAULTS = None
|
CANONICAL_DEFAULTS = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@ from PyQt5.QtCore import pyqtSignal
|
||||||
import re
|
import re
|
||||||
from gui.platform_glyph import platform_glyph
|
from gui.platform_glyph import platform_glyph
|
||||||
try:
|
try:
|
||||||
import set_defaults
|
from util.defaults import DEFAULTS as CANONICAL_DEFAULTS
|
||||||
CANONICAL_DEFAULTS = getattr(set_defaults, 'DEFAULTS', None)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
CANONICAL_DEFAULTS = None
|
CANONICAL_DEFAULTS = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import sys
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from util.paths import VOCAB_BASE_PATH, VOCAB_PATH
|
||||||
|
|
||||||
import jsbeautifier
|
import jsbeautifier
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QEvent, QSettings, QThread, QTimer, pyqtSignal
|
from PyQt5.QtCore import Qt, QEvent, QSettings, QThread, QTimer, pyqtSignal
|
||||||
|
|
@ -3576,7 +3578,7 @@ class WorkflowTab(QWidget):
|
||||||
|
|
||||||
def _read_vocab_speakers(self) -> list[tuple[str, str]]:
|
def _read_vocab_speakers(self) -> list[tuple[str, str]]:
|
||||||
"""Parse the '# Speakers' section from vocab.txt and return (orig, tl) pairs."""
|
"""Parse the '# Speakers' section from vocab.txt and return (orig, tl) pairs."""
|
||||||
vocab_path = Path("vocab.txt")
|
vocab_path = VOCAB_PATH
|
||||||
if not vocab_path.exists():
|
if not vocab_path.exists():
|
||||||
return []
|
return []
|
||||||
try:
|
try:
|
||||||
|
|
@ -3606,7 +3608,7 @@ class WorkflowTab(QWidget):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _reload_vocab(self):
|
def _reload_vocab(self):
|
||||||
vocab_path = Path("vocab.txt")
|
vocab_path = VOCAB_PATH
|
||||||
try:
|
try:
|
||||||
if vocab_path.exists():
|
if vocab_path.exists():
|
||||||
text = vocab_path.read_text(encoding="utf-8")
|
text = vocab_path.read_text(encoding="utf-8")
|
||||||
|
|
@ -3623,10 +3625,10 @@ class WorkflowTab(QWidget):
|
||||||
def _save_vocab(self):
|
def _save_vocab(self):
|
||||||
try:
|
try:
|
||||||
game_text = self.vocab_editor.toPlainText().rstrip("\n")
|
game_text = self.vocab_editor.toPlainText().rstrip("\n")
|
||||||
base_path = Path("vocab_base.txt")
|
base_path = VOCAB_BASE_PATH
|
||||||
base_text = base_path.read_text(encoding="utf-8") if base_path.exists() else ""
|
base_text = base_path.read_text(encoding="utf-8") if base_path.exists() else ""
|
||||||
combined = game_text + "\n\n" + self._BASE_SEPARATOR + base_text
|
combined = game_text + "\n\n" + self._BASE_SEPARATOR + base_text
|
||||||
Path("vocab.txt").write_text(combined, encoding="utf-8")
|
VOCAB_PATH.write_text(combined, encoding="utf-8")
|
||||||
self._log("✅ vocab.txt saved (base terms from vocab_base.txt appended).")
|
self._log("✅ vocab.txt saved (base terms from vocab_base.txt appended).")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._log(f"❌ Could not save vocab.txt: {exc}")
|
self._log(f"❌ Could not save vocab.txt: {exc}")
|
||||||
|
|
@ -3795,7 +3797,7 @@ class WorkflowTab(QWidget):
|
||||||
self._log("⚠ No game folder set. Complete Step 0 first.")
|
self._log("⚠ No game folder set. Complete Step 0 first.")
|
||||||
return
|
return
|
||||||
|
|
||||||
src = Path("vocab.txt")
|
src = VOCAB_PATH
|
||||||
if not src.exists():
|
if not src.exists():
|
||||||
self._log("⚠ vocab.txt not found — save it in Step 3 first.")
|
self._log("⚠ vocab.txt not found — save it in Step 3 first.")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
ESTIMATE = ""
|
ESTIMATE = ""
|
||||||
TOKENS = [0, 0]
|
TOKENS = [0, 0]
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@ from util.translation import TranslationConfig, translateAI as sharedtranslateAI
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
PBAR = None
|
PBAR = None
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,10 @@ from dotenv import load_dotenv
|
||||||
# This needs to be before the module imports as some of them currently try to read and use some of these values
|
# This needs to be before the module imports as some of them currently try to read and use some of these values
|
||||||
# upon import, in which case if they are unset the script will crash before we can output these messages.
|
# upon import, in which case if they are unset the script will crash before we can output these messages.
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
from util.paths import migrate_root_data_files, ensure_vocab_file
|
||||||
|
|
||||||
|
migrate_root_data_files()
|
||||||
|
ensure_vocab_file()
|
||||||
_missing_envs = [
|
_missing_envs = [
|
||||||
env for env in [
|
env for env in [
|
||||||
"api",
|
"api",
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ from util.translation import TranslationConfig, translateAI as sharedtranslateAI
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
THREAD_CTX = threading.local()
|
THREAD_CTX = threading.local()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
|
|
@ -803,7 +805,7 @@ def update_vocab_section(category: str, pairs: list[tuple[str, str]]):
|
||||||
The existing section is replaced entirely; other sections are preserved.
|
The existing section is replaced entirely; other sections are preserved.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
vocab_path = Path("vocab.txt")
|
vocab_path = VOCAB_PATH
|
||||||
|
|
||||||
# Helper: normalized comparison to detect no-op translations
|
# Helper: normalized comparison to detect no-op translations
|
||||||
def _norm(s: str) -> str:
|
def _norm(s: str) -> str:
|
||||||
|
|
@ -5071,7 +5073,7 @@ def finalizeSpeakerParse():
|
||||||
_speakerCache[orig] = norm
|
_speakerCache[orig] = norm
|
||||||
NAMESLIST.append([orig, norm])
|
NAMESLIST.append([orig, norm])
|
||||||
|
|
||||||
vocab_path = Path("vocab.txt")
|
vocab_path = VOCAB_PATH
|
||||||
if not vocab_path.exists():
|
if not vocab_path.exists():
|
||||||
return
|
return
|
||||||
content = vocab_path.read_text(encoding="utf-8")
|
content = vocab_path.read_text(encoding="utf-8")
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ from util.translation import TranslationConfig, translateAI as sharedtranslateAI
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
VOCAB_LOCK = threading.Lock() # Dedicated lock for vocab.txt updates
|
VOCAB_LOCK = threading.Lock() # Dedicated lock for vocab.txt updates
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
|
|
@ -118,7 +120,7 @@ def update_vocab_section(category: str, pairs: list[tuple[str, str]]):
|
||||||
The existing section is replaced entirely; other sections are preserved.
|
The existing section is replaced entirely; other sections are preserved.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
vocab_path = Path("vocab.txt")
|
vocab_path = VOCAB_PATH
|
||||||
|
|
||||||
# Helper: normalized comparison to detect no-op translations
|
# Helper: normalized comparison to detect no-op translations
|
||||||
def _norm(s: str) -> str:
|
def _norm(s: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ from util.translation import TranslationConfig, translateAI as sharedtranslateAI
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@ import tempfile
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
TIMEOUT = int(os.getenv("timeout"))
|
TIMEOUT = int(os.getenv("timeout"))
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
LISTWIDTH = int(os.getenv("listWidth"))
|
LISTWIDTH = int(os.getenv("listWidth"))
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ from util.translation import (
|
||||||
|
|
||||||
MODEL = os.getenv("model")
|
MODEL = os.getenv("model")
|
||||||
LANGUAGE = os.getenv("language").capitalize()
|
LANGUAGE = os.getenv("language").capitalize()
|
||||||
PROMPT = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
VOCAB = Path("vocab.txt").read_text(encoding="utf-8")
|
|
||||||
|
PROMPT = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
|
VOCAB = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
LOCK = threading.Lock()
|
LOCK = threading.Lock()
|
||||||
WIDTH = int(os.getenv("width"))
|
WIDTH = int(os.getenv("width"))
|
||||||
MAXHISTORY = 10
|
MAXHISTORY = 10
|
||||||
|
|
|
||||||
13
scripts/launch.sh
Normal file
13
scripts/launch.sh
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Launcher for DazedMTLTool.desktop (finds venv, then starts the GUI).
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$ROOT" || exit 1
|
||||||
|
|
||||||
|
PYTHON="python3"
|
||||||
|
if [[ -x "$ROOT/.venv/bin/python" ]]; then
|
||||||
|
PYTHON="$ROOT/.venv/bin/python"
|
||||||
|
elif [[ -x "$ROOT/venv/bin/python" ]]; then
|
||||||
|
PYTHON="$ROOT/venv/bin/python"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$PYTHON" "$ROOT/scripts/start_gui.py"
|
||||||
|
|
@ -1,27 +1,24 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""Launch script for DazedMTLTool GUI."""
|
||||||
Launch script for DazedMTLTool GUI
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Add the project root to Python path
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
project_root = Path(__file__).parent
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||||||
sys.path.insert(0, str(project_root))
|
|
||||||
|
|
||||||
def check_dependencies():
|
def check_dependencies():
|
||||||
"""Check if required dependencies are installed."""
|
"""Check if required dependencies are installed."""
|
||||||
missing_deps = []
|
missing_deps = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import PyQt5
|
import PyQt5 # noqa: F401
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing_deps.append("PyQt5")
|
missing_deps.append("PyQt5")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv # noqa: F401
|
||||||
except ImportError:
|
except ImportError:
|
||||||
missing_deps.append("python-dotenv")
|
missing_deps.append("python-dotenv")
|
||||||
|
|
||||||
|
|
@ -30,17 +27,22 @@ def check_dependencies():
|
||||||
for dep in missing_deps:
|
for dep in missing_deps:
|
||||||
print(f" - {dep}")
|
print(f" - {dep}")
|
||||||
print("\nPlease install them using:")
|
print("\nPlease install them using:")
|
||||||
print(" pip install -r requirements_gui.txt")
|
print(" pip install -r requirements.txt")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main entry point."""
|
"""Main entry point."""
|
||||||
print("DazedMTLTool GUI Launcher")
|
print("DazedMTLTool GUI Launcher")
|
||||||
print("=" * 40)
|
print("=" * 40)
|
||||||
|
|
||||||
# Check dependencies
|
from util.paths import ensure_vocab_file, migrate_root_data_files
|
||||||
|
|
||||||
|
migrate_root_data_files()
|
||||||
|
ensure_vocab_file()
|
||||||
|
|
||||||
if not check_dependencies():
|
if not check_dependencies():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
@ -56,7 +58,6 @@ def main():
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f"Warning: Forge plugin setup failed ({exc}). Playtest Forge may be unavailable.")
|
print(f"Warning: Forge plugin setup failed ({exc}). Playtest Forge may be unavailable.")
|
||||||
|
|
||||||
# Import and run GUI
|
|
||||||
try:
|
try:
|
||||||
from gui.main import main as gui_main
|
from gui.main import main as gui_main
|
||||||
gui_main()
|
gui_main()
|
||||||
|
|
@ -68,5 +69,6 @@ def main():
|
||||||
print(f"Error starting GUI: {e}")
|
print(f"Error starting GUI: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import re
|
import re
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
# Canonical defaults used by the GUI and the `set_defaults` script.
|
from util.paths import PROJECT_ROOT
|
||||||
|
|
||||||
|
# Canonical defaults used by the GUI and the pre-commit hook.
|
||||||
# Stored as booleans for easier consumption by the GUI.
|
# Stored as booleans for easier consumption by the GUI.
|
||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
'FIRSTLINESPEAKERS': False,
|
'FIRSTLINESPEAKERS': False,
|
||||||
|
|
@ -56,4 +58,4 @@ def set_defaults(file_path):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
set_defaults('modules/rpgmakermvmz.py')
|
set_defaults(PROJECT_ROOT / "modules/rpgmakermvmz.py")
|
||||||
72
util/linux_desktop.py
Normal file
72
util/linux_desktop.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""Install/update the Linux .desktop entry for taskbar and window icons."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from util.paths import ICON_PATH, PROJECT_ROOT
|
||||||
|
|
||||||
|
DESKTOP_ID = "DazedMTLTool"
|
||||||
|
LAUNCH_SCRIPT = PROJECT_ROOT / "scripts" / "launch.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def installed_desktop_path() -> Path:
|
||||||
|
data_home = os.environ.get("XDG_DATA_HOME", "")
|
||||||
|
if data_home:
|
||||||
|
base = Path(data_home)
|
||||||
|
else:
|
||||||
|
base = Path.home() / ".local" / "share"
|
||||||
|
return base / "applications" / f"{DESKTOP_ID}.desktop"
|
||||||
|
|
||||||
|
|
||||||
|
def _desktop_content(root: Path, icon: Path, launch: Path) -> str:
|
||||||
|
return (
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
f"Name={DESKTOP_ID}\n"
|
||||||
|
"GenericName=Translation Tool\n"
|
||||||
|
"Comment=AI translation tool for visual novels and RPG games\n"
|
||||||
|
f"Exec={launch}\n"
|
||||||
|
f"Icon={icon}\n"
|
||||||
|
f"Path={root}\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
"Categories=Development;Utility;\n"
|
||||||
|
f"StartupWMClass={DESKTOP_ID}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_linux_desktop_entry() -> None:
|
||||||
|
"""Write ~/.local/share/applications/DazedMTLTool.desktop with absolute paths."""
|
||||||
|
if not sys.platform.startswith("linux"):
|
||||||
|
return
|
||||||
|
if not LAUNCH_SCRIPT.is_file() or not ICON_PATH.is_file():
|
||||||
|
return
|
||||||
|
|
||||||
|
desktop_path = installed_desktop_path()
|
||||||
|
content = _desktop_content(PROJECT_ROOT, ICON_PATH, LAUNCH_SCRIPT)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if desktop_path.is_file() and desktop_path.read_text(encoding="utf-8") == content:
|
||||||
|
return
|
||||||
|
desktop_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
desktop_path.write_text(content, encoding="utf-8")
|
||||||
|
desktop_path.chmod(0o755)
|
||||||
|
except OSError as exc:
|
||||||
|
print(f"Warning: could not install Linux desktop entry ({exc})")
|
||||||
|
return
|
||||||
|
|
||||||
|
_refresh_desktop_database(desktop_path.parent)
|
||||||
|
|
||||||
|
|
||||||
|
def _refresh_desktop_database(applications_dir: Path) -> None:
|
||||||
|
for cmd in (
|
||||||
|
["update-desktop-database", str(applications_dir)],
|
||||||
|
["kbuildsycoca5", "--noincremental"],
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
45
util/paths.py
Normal file
45
util/paths.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""Canonical project paths (repo root, data files, config)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
DATA_DIR = PROJECT_ROOT / "data"
|
||||||
|
VOCAB_PATH = DATA_DIR / "vocab.txt"
|
||||||
|
VOCAB_BASE_PATH = DATA_DIR / "vocab_base.txt"
|
||||||
|
PROMPT_PATH = DATA_DIR / "prompt.txt"
|
||||||
|
LAST_UPDATE_SHA_PATH = DATA_DIR / "last_update_sha.txt"
|
||||||
|
ENV_PATH = PROJECT_ROOT / ".env"
|
||||||
|
ICON_PATH = PROJECT_ROOT / "assets" / "icon.png"
|
||||||
|
|
||||||
|
_ROOT_DATA_FILES = (
|
||||||
|
"vocab.txt",
|
||||||
|
"vocab_base.txt",
|
||||||
|
"prompt.txt",
|
||||||
|
"last_update_sha.txt",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_root_data_files() -> None:
|
||||||
|
"""Move legacy root-level data files into data/ on first run."""
|
||||||
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
for name in _ROOT_DATA_FILES:
|
||||||
|
src = PROJECT_ROOT / name
|
||||||
|
dst = DATA_DIR / name
|
||||||
|
if src.is_file() and not dst.exists():
|
||||||
|
src.rename(dst)
|
||||||
|
|
||||||
|
|
||||||
|
migrate_root_data_files()
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_vocab_file() -> None:
|
||||||
|
"""Create data/vocab.txt from vocab_base.txt when missing."""
|
||||||
|
migrate_root_data_files()
|
||||||
|
if VOCAB_PATH.is_file():
|
||||||
|
return
|
||||||
|
if VOCAB_BASE_PATH.is_file():
|
||||||
|
VOCAB_PATH.write_text(VOCAB_BASE_PATH.read_text(encoding="utf-8"), encoding="utf-8")
|
||||||
|
else:
|
||||||
|
VOCAB_PATH.write_text("", encoding="utf-8")
|
||||||
|
|
@ -1316,7 +1316,8 @@ class TranslationConfig:
|
||||||
# Load prompt and vocab files if not provided
|
# Load prompt and vocab files if not provided
|
||||||
if prompt is None:
|
if prompt is None:
|
||||||
try:
|
try:
|
||||||
self.prompt = Path("prompt.txt").read_text(encoding="utf-8")
|
from util.paths import PROMPT_PATH, VOCAB_PATH
|
||||||
|
self.prompt = PROMPT_PATH.read_text(encoding="utf-8")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.prompt = ""
|
self.prompt = ""
|
||||||
else:
|
else:
|
||||||
|
|
@ -1324,7 +1325,7 @@ class TranslationConfig:
|
||||||
|
|
||||||
if vocab is None:
|
if vocab is None:
|
||||||
try:
|
try:
|
||||||
self.vocab = Path("vocab.txt").read_text(encoding="utf-8")
|
self.vocab = VOCAB_PATH.read_text(encoding="utf-8")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.vocab = ""
|
self.vocab = ""
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue