Files
live-app-translator/packaging/livesub.spec
EJClaw bede94ee48 fix: 포터블에 transformers 포함 + 실제 모델로 드러난 용어집 결함 수정
리뷰 지적(포터블에서 transformers 제외 -> NLLB 로드 실패)을 고치려고 실제
NLLB 를 받아 돌려봤고, 그 과정에서 용어집이 사실상 동작하지 않고 있었다는
것을 발견했다. 단위 테스트는 "모델이 자리표시자를 통과시킨다"는 틀린 전제
위에 서 있었다.

1) 포터블 패키징 (리뷰 지적)
   transformers 를 제외 목록에서 빼고 hiddenimports 에 넣었다. NLLB
   토크나이저가 AutoTokenizer 를 쓰기 때문이다. transformers 는 torch 가
   없으면 토크나이저 전용 모드로 뜨며 그게 우리 용도와 정확히 맞는다.
   torch 없는 환경에서 ctranslate2/transformers/faster-whisper import 와
   앱 기동을 검증하는 test_portable.py 를 추가했다.

2) 자리표시자 형식 (실측으로 발견)
   `⟦0⟧` 는 NLLB 가 괄호를 날려 생존률 0/3 이었다. 용어가 자막에서 그냥
   사라지고 있었다 ("Third party incoming" -> "0 들어오는"). 후보 8종을
   실제 모델로 비교해 `#0#` 로 교체 (3/3, 다중 4/5).

3) 소실 대비
   모델이 문장 일부를 누락하면 자리표시자도 사라진다. 그대로 복원하면
   용어가 증발하므로, 하나라도 없으면 보호 없이 재번역한다.

4) 서술어는 문장 전체일 때만 (whole_only)
   절/서술어를 문장 중간에서 치환하면 문법이 무너진다.
     before: "탄 필요해와 구급상자"
     after : "탄약과 구급상자가 필요합니다"
   해당 56개 항목을 whole_only 로 지정해 단독 발화일 때만 적용한다.
   ("Cover me!" -> "엄호해줘" 는 그대로 유지)

5) 조사 교정
   역어 받침이 달라 "자기장를" 이 남던 것을 fix_particles() 로 고친다.
   을/를, 이/가, 은/는, 과/와, (으)로 — 한글 코드에서 받침을 읽어 판정.

검증: pytest 189개 통과, ruff clean
      실제 NLLB-600M(torch 없이 CPU)로 번역 품질 직접 확인

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-09-23 01:20:42 +09:00

98 lines
3.7 KiB
Python

# -*- mode: python ; coding: utf-8 -*-
"""포터블 LiveSub 빌드 명세 (PyInstaller).
설치 없이 exe 하나만 두고 실행하는 것이 목표다. 다만 그냥 전부 넣으면
안 된다. torch+CUDA 만 2.5GB 라 --onefile 로 묶으면 **실행할 때마다**
그 2.5GB 를 임시폴더에 풀어야 해서 시작에 1분 넘게 걸린다.
그래서 포터블 빌드는 torch 를 뺀다. 음성인식(faster-whisper)과 번역
(NLLB) 모두 CTranslate2 위에서 돌아가고 CTranslate2 는 torch 를 요구하지
않기 때문에, 1~3티어는 그대로 다 동작한다.
다만 NLLB 토크나이저는 transformers 의 AutoTokenizer 를 쓰므로 transformers
자체는 넣어야 한다. transformers 는 torch 가 없으면 "토크나이저/설정만 사용
가능" 모드로 뜨며, 우리가 쓰는 용도가 정확히 그것이다 (실측 확인). 4~5티어(Seed-X / Qwen3)만
빠지며, 앱이 '모델' 화면에서 그 사실을 표시한다.
빌드:
pip install -r packaging/requirements-portable.txt pyinstaller
pyinstaller packaging/livesub.spec --noconfirm
결과:
dist/LiveSub.exe (약 400~600MB, 첫 실행 20~40초 / 이후 10초 내외)
모델은 exe 에 넣지 않는다. 처음 쓸 때 %APPDATA%\\LiveSub\\models 로
내려받으므로 exe 를 USB 로 옮겨도 모델은 그 PC 에 남는다.
"""
from pathlib import Path
from PyInstaller.utils.hooks import collect_dynamic_libs
BLOCK_CIPHER = None
ROOT = Path(SPECPATH).parent # noqa: F821 - PyInstaller 가 주입
SRC = ROOT / "src"
# 게임 용어집 JSON 과 (빌드돼 있다면) 프로세스 캡처 보조 프로그램을 함께 담는다.
datas = [(str(SRC / "livesub" / "resources" / "glossaries"), "livesub/resources/glossaries")]
capture_exe = SRC / "livesub" / "resources" / "bin" / "livesub_capture.exe"
if capture_exe.is_file():
datas.append((str(capture_exe), "livesub/resources/bin"))
# CTranslate2 는 CUDA/cuDNN DLL 을 곁에 두고 로드한다. 자동 수집에서 빠지기 쉽다.
binaries = collect_dynamic_libs("ctranslate2")
hiddenimports = [
"livesub.audio.process_loopback",
"livesub.audio.wasapi_loopback",
"ctranslate2",
"faster_whisper",
"tokenizers",
"sentencepiece",
"transformers",
"transformers.models.nllb",
"huggingface_hub",
]
# 넣으면 용량만 키우고 쓰지 않는 것들. torch 계열은 의도적으로 제외한다.
excludes = [
# transformers 는 제외하지 않는다. NLLB 토크나이저(AutoTokenizer)가 필요하고,
# torch 없이 토크나이저 전용으로 동작하는 것을 실제로 확인했다.
"torch", "torchvision", "torchaudio", "peft",
"accelerate", "datasets", "bitsandbytes", "autoawq",
"matplotlib", "scipy", "pandas", "IPython", "notebook",
"tkinter", "PySide6.QtWebEngineCore", "PySide6.Qt3DCore",
"PySide6.QtCharts", "PySide6.QtDataVisualization", "PySide6.QtMultimedia",
"PySide6.QtQuick", "PySide6.QtQml", "PySide6.QtPdf", "PySide6.QtDesigner",
]
a = Analysis( # noqa: F821
[str(SRC / "livesub" / "__main__.py")],
pathex=[str(SRC)],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
runtime_hooks=[],
excludes=excludes,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=BLOCK_CIPHER) # noqa: F821
exe = EXE( # noqa: F821
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name="LiveSub",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # UPX 로 압축하면 백신이 오탐하는 일이 잦다
runtime_tmpdir=None,
console=False, # 콘솔 창 없이 GUI 로만
icon=str(ROOT / "src" / "livesub" / "resources" / "icon.ico"),
version=None,
)