Spaces:
Paused
Paused
Update translator_runner.py
Browse files- translator_runner.py +63 -1
translator_runner.py
CHANGED
|
@@ -45,7 +45,7 @@ import subprocess
|
|
| 45 |
import tempfile
|
| 46 |
import time
|
| 47 |
import zipfile
|
| 48 |
-
from dataclasses import dataclass, field
|
| 49 |
from pathlib import Path
|
| 50 |
from typing import Callable, Optional
|
| 51 |
|
|
@@ -57,6 +57,50 @@ MT_TIMEOUT = int(os.environ.get("MT_TIMEOUT", "600"))
|
|
| 57 |
|
| 58 |
ALLOWED_ENGINES = ("local_llm", "google", "openai", "deepseek", "libretranslate", "custom")
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
@dataclass
|
| 62 |
class TranslationJob:
|
|
@@ -239,15 +283,21 @@ def run_translation(
|
|
| 239 |
RenLocalizer'ı senkron olarak çalıştırır (Gradio callback'i içinde
|
| 240 |
çağrılır — Gradio kendi tarafında bu fonksiyonu bir thread/worker'da
|
| 241 |
yürütür). on_log her yeni log satırında çağrılır (canlı akış için).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
"""
|
| 243 |
job.status = "running"
|
| 244 |
job.started_at = time.time()
|
|
|
|
| 245 |
|
| 246 |
run_cli_path = os.path.join(RENLOCALIZER_DIR, "run_cli.py")
|
| 247 |
if not os.path.isfile(run_cli_path):
|
| 248 |
job.status = "failed"
|
| 249 |
job.error_summary = f"RenLocalizer bulunamadı: {run_cli_path}"
|
| 250 |
on_log(job.error_summary)
|
|
|
|
| 251 |
return job
|
| 252 |
|
| 253 |
if job.engine not in ALLOWED_ENGINES:
|
|
@@ -290,9 +340,11 @@ def run_translation(
|
|
| 290 |
job.status = "failed"
|
| 291 |
job.error_summary = f"Süreç başlatılamadı: {exc}"
|
| 292 |
on_log(job.error_summary)
|
|
|
|
| 293 |
return job
|
| 294 |
|
| 295 |
assert process.stdout is not None
|
|
|
|
| 296 |
for line in process.stdout:
|
| 297 |
line = line.rstrip("\n")
|
| 298 |
if not line:
|
|
@@ -306,6 +358,15 @@ def run_translation(
|
|
| 306 |
if total > 0:
|
| 307 |
job.progress_percent = min(100, round((current / total) * 100))
|
| 308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
process.wait()
|
| 310 |
job.finished_at = time.time()
|
| 311 |
|
|
@@ -329,6 +390,7 @@ def run_translation(
|
|
| 329 |
job.error_summary = f"Çeviri başarısız: {reason}"
|
| 330 |
on_log(job.error_summary)
|
| 331 |
|
|
|
|
| 332 |
return job
|
| 333 |
|
| 334 |
|
|
|
|
| 45 |
import tempfile
|
| 46 |
import time
|
| 47 |
import zipfile
|
| 48 |
+
from dataclasses import asdict, dataclass, field
|
| 49 |
from pathlib import Path
|
| 50 |
from typing import Callable, Optional
|
| 51 |
|
|
|
|
| 57 |
|
| 58 |
ALLOWED_ENGINES = ("local_llm", "google", "openai", "deepseek", "libretranslate", "custom")
|
| 59 |
|
| 60 |
+
# Sekme kapatılıp geri açıldığında (ya da Space yeniden başladığında) son
|
| 61 |
+
# işin durumunu okuyabilmek için basit bir diske-yazma kalıcılığı. Sadece
|
| 62 |
+
# TEK bir "son iş" tutuyoruz (tek kullanıcı senaryosu için yeterli) —
|
| 63 |
+
# çoklu iş geçmişi gerekmiyor.
|
| 64 |
+
_JOB_STATE_FILE = os.path.join(tempfile.gettempdir(), "rlmt_current_job.json")
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def save_job_state(job: "TranslationJob") -> None:
|
| 68 |
+
"""
|
| 69 |
+
Job durumunu diske yazar. Her önemli güncellemede (durum değişimi,
|
| 70 |
+
ilerleme, log satırı) çağrılır — sekme kapatılsa/yenilense bile son
|
| 71 |
+
bilinen durum kaybolmasın diye. Yazma hatası sessizce yutulur (bu
|
| 72 |
+
özellik best-effort'tur, ana akışı bloklamamalı).
|
| 73 |
+
"""
|
| 74 |
+
try:
|
| 75 |
+
with open(_JOB_STATE_FILE, "w", encoding="utf-8") as f:
|
| 76 |
+
json.dump(asdict(job), f, ensure_ascii=False)
|
| 77 |
+
except OSError:
|
| 78 |
+
pass
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def load_job_state() -> Optional["TranslationJob"]:
|
| 82 |
+
"""
|
| 83 |
+
Diskte kayıtlı son job durumunu okur. Dosya yoksa veya bozuksa None
|
| 84 |
+
döner. Gradio arayüzü açılışta bunu çağırıp "hâlâ çalışan/tamamlanmış
|
| 85 |
+
bir iş var mı" diye kontrol eder.
|
| 86 |
+
"""
|
| 87 |
+
if not os.path.isfile(_JOB_STATE_FILE):
|
| 88 |
+
return None
|
| 89 |
+
try:
|
| 90 |
+
with open(_JOB_STATE_FILE, "r", encoding="utf-8") as f:
|
| 91 |
+
data = json.load(f)
|
| 92 |
+
return TranslationJob(**data)
|
| 93 |
+
except (OSError, json.JSONDecodeError, TypeError):
|
| 94 |
+
return None
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def clear_job_state() -> None:
|
| 98 |
+
"""Kayıtlı job durumunu siler (kullanıcı yeni bir iş başlattığında)."""
|
| 99 |
+
try:
|
| 100 |
+
os.remove(_JOB_STATE_FILE)
|
| 101 |
+
except OSError:
|
| 102 |
+
pass
|
| 103 |
+
|
| 104 |
|
| 105 |
@dataclass
|
| 106 |
class TranslationJob:
|
|
|
|
| 283 |
RenLocalizer'ı senkron olarak çalıştırır (Gradio callback'i içinde
|
| 284 |
çağrılır — Gradio kendi tarafında bu fonksiyonu bir thread/worker'da
|
| 285 |
yürütür). on_log her yeni log satırında çağrılır (canlı akış için).
|
| 286 |
+
|
| 287 |
+
Her önemli durum değişikliğinde save_job_state() çağrılır — sekme
|
| 288 |
+
kapatılıp geri açıldığında (ya da Space yeniden başladığında) son
|
| 289 |
+
bilinen durum diskten okunabilsin diye.
|
| 290 |
"""
|
| 291 |
job.status = "running"
|
| 292 |
job.started_at = time.time()
|
| 293 |
+
save_job_state(job)
|
| 294 |
|
| 295 |
run_cli_path = os.path.join(RENLOCALIZER_DIR, "run_cli.py")
|
| 296 |
if not os.path.isfile(run_cli_path):
|
| 297 |
job.status = "failed"
|
| 298 |
job.error_summary = f"RenLocalizer bulunamadı: {run_cli_path}"
|
| 299 |
on_log(job.error_summary)
|
| 300 |
+
save_job_state(job)
|
| 301 |
return job
|
| 302 |
|
| 303 |
if job.engine not in ALLOWED_ENGINES:
|
|
|
|
| 340 |
job.status = "failed"
|
| 341 |
job.error_summary = f"Süreç başlatılamadı: {exc}"
|
| 342 |
on_log(job.error_summary)
|
| 343 |
+
save_job_state(job)
|
| 344 |
return job
|
| 345 |
|
| 346 |
assert process.stdout is not None
|
| 347 |
+
line_count_since_save = 0
|
| 348 |
for line in process.stdout:
|
| 349 |
line = line.rstrip("\n")
|
| 350 |
if not line:
|
|
|
|
| 358 |
if total > 0:
|
| 359 |
job.progress_percent = min(100, round((current / total) * 100))
|
| 360 |
|
| 361 |
+
# Her satırda disk yazmak gereksiz I/O yaratır (RenLocalizer
|
| 362 |
+
# binlerce satır üretebiliyor) — periyodik olarak kaydediyoruz,
|
| 363 |
+
# yeterince sık ki sekme yeniden açıldığında güncel bir durum
|
| 364 |
+
# görünsün.
|
| 365 |
+
line_count_since_save += 1
|
| 366 |
+
if line_count_since_save >= 10:
|
| 367 |
+
save_job_state(job)
|
| 368 |
+
line_count_since_save = 0
|
| 369 |
+
|
| 370 |
process.wait()
|
| 371 |
job.finished_at = time.time()
|
| 372 |
|
|
|
|
| 390 |
job.error_summary = f"Çeviri başarısız: {reason}"
|
| 391 |
on_log(job.error_summary)
|
| 392 |
|
| 393 |
+
save_job_state(job)
|
| 394 |
return job
|
| 395 |
|
| 396 |
|