regenerate something when the sentences changed

This commit is contained in:
2026-10-03 21:12:12 +08:00
parent d5609c5c42
commit 4fd0fd36a4
11 changed files with 245 additions and 13 deletions

View File

@@ -20,6 +20,7 @@ class _Job:
done: threading.Event
lock: threading.Lock
running: bool = True
cancelled: bool = False
error: str | None = None
thread: threading.Thread | None = None
@@ -89,6 +90,26 @@ class SubtitleTranslationService:
return None
return self.snapshot(document)
def invalidate(self, video_hash: str) -> None:
"""Stop an in-flight job so stale translations cannot be written back."""
with self._lock:
job = self._jobs.pop(video_hash, None)
if job is None:
return
with job.lock:
job.cancelled = True
def refresh(self, video_hash: str) -> None:
"""Drop saved Chinese subtitles and translate the current sentences again."""
self.invalidate(video_hash)
self.repository.clear_sentence_translations(video_hash)
if not getattr(self.ai_tutor, "available", False):
return
try:
self._start_if_needed(video_hash)
except (KeyError, ValueError, AITutorError):
logger.exception("Could not restart subtitle translation: video=%s", video_hash)
def ensure(self, video_hash: str) -> SubtitleTranslationResponse:
if not getattr(self.ai_tutor, "available", False):
raise AITutorError("AI translation is not configured.")
@@ -119,8 +140,15 @@ class SubtitleTranslationService:
existing = self._jobs.get(video_hash)
if existing is not None:
with existing.lock:
still_running = existing.running or (
existing.thread is not None and existing.thread.is_alive()
still_running = (
not existing.cancelled
and (
existing.running
or (
existing.thread is not None
and existing.thread.is_alive()
)
)
)
if still_running:
return existing
@@ -140,8 +168,17 @@ class SubtitleTranslationService:
thread.start()
return job
def _persist(self, job: _Job, translations: dict[int, str]) -> None:
with job.lock:
if job.cancelled:
return
self.repository.save_sentence_translations(job.video_hash, translations)
def _run_job(self, job: _Job) -> None:
try:
with job.lock:
if job.cancelled:
return
document = self.repository.get_document(job.video_hash)
if document is None:
raise AITutorError("Course is not available.")
@@ -162,16 +199,18 @@ class SubtitleTranslationService:
payload,
course_title=(video["title"] if video is not None else "") or "",
existing=existing,
persist=lambda part: self.repository.save_sentence_translations(
job.video_hash,
part,
),
persist=lambda part: self._persist(job, part),
)
if translated:
self.repository.save_sentence_translations(job.video_hash, translated)
self._persist(job, translated)
with job.lock:
job.error = None
if not job.cancelled:
job.error = None
except Exception as exc: # noqa: BLE001 - surface job failure to clients
with job.lock:
cancelled = job.cancelled
if cancelled:
return
logger.exception("Subtitle translation job failed: video=%s", job.video_hash)
with job.lock:
job.error = str(exc)