later process
This commit is contained in:
@@ -50,6 +50,8 @@ from .models import (
|
||||
SentenceExplanationRequest,
|
||||
SentenceExplanationResponse,
|
||||
AdminSubtitleTranslationResponse,
|
||||
AdminSentenceBatchItem,
|
||||
AdminSentenceBatchUpdate,
|
||||
LocalSubtitleTranslationRequest,
|
||||
SubtitleRetranslateRequest,
|
||||
SubtitleTranslationResponse,
|
||||
@@ -789,6 +791,30 @@ def create_app(
|
||||
raise HTTPException(status_code=404, detail="Video was not found.")
|
||||
return _video_summary(video, service_settings)
|
||||
|
||||
@application.put(
|
||||
"/api/v1/admin/videos/{video_hash}/sentences/batch",
|
||||
response_model=List[SentenceBoundary],
|
||||
dependencies=[Depends(require_admin)],
|
||||
)
|
||||
def batch_admin_sentences(
|
||||
payload: AdminSentenceBatchUpdate,
|
||||
video_hash: str = SHA256_PATH,
|
||||
) -> List[SentenceBoundary]:
|
||||
try:
|
||||
result = _replace_admin_sentences(
|
||||
repository=video_repository,
|
||||
transcriber=moss,
|
||||
settings=service_settings,
|
||||
video_hash=video_hash,
|
||||
payload=payload,
|
||||
)
|
||||
except KeyError:
|
||||
raise HTTPException(status_code=404, detail="Course is not available.") from None
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
application.state.subtitle_translations.refresh(video_hash)
|
||||
return result
|
||||
|
||||
@application.put(
|
||||
"/api/v1/admin/videos/{video_hash}/sentences/{sentence_index}",
|
||||
response_model=SentenceBoundary,
|
||||
@@ -1202,6 +1228,119 @@ def _process_safely(processor: VideoProcessor, video_hash: str) -> None:
|
||||
logger.exception("Video processing failed for %s", video_hash)
|
||||
|
||||
|
||||
def _replace_admin_sentences(
|
||||
*,
|
||||
repository: VideoRepository,
|
||||
transcriber: Transcriber,
|
||||
settings: Settings,
|
||||
video_hash: str,
|
||||
payload: AdminSentenceBatchUpdate,
|
||||
) -> List[SentenceBoundary]:
|
||||
"""Apply queued admin edits once, instead of after every split/merge."""
|
||||
video = repository.get_video(video_hash)
|
||||
old_document = repository.get_document(video_hash)
|
||||
if video is None or old_document is None:
|
||||
raise KeyError(video_hash)
|
||||
|
||||
old_sentences = {
|
||||
(sentence.start_ms, sentence.end_ms): sentence
|
||||
for sentence in old_document.sentences
|
||||
}
|
||||
rows: List[SentenceBoundary] = []
|
||||
index = 0
|
||||
position = 0
|
||||
while position < len(payload.sentences):
|
||||
item = payload.sentences[position]
|
||||
if item.source_start_ms is None or item.source_end_ms is None:
|
||||
if "\n" in item.text or "\r" in item.text:
|
||||
raise ValueError("Unsplit batch sentences cannot contain line breaks.")
|
||||
rows.append(
|
||||
SentenceBoundary(
|
||||
index=index,
|
||||
start_ms=item.start_ms,
|
||||
end_ms=item.end_ms,
|
||||
text=item.text.strip(),
|
||||
language=item.language,
|
||||
reference_speech_duration_ms=item.reference_speech_duration_ms,
|
||||
)
|
||||
)
|
||||
index += 1
|
||||
position += 1
|
||||
continue
|
||||
|
||||
source_key = (item.source_start_ms, item.source_end_ms)
|
||||
group_end = position + 1
|
||||
while group_end < len(payload.sentences):
|
||||
following = payload.sentences[group_end]
|
||||
if following.source_start_ms != source_key[0] or following.source_end_ms != source_key[1]:
|
||||
break
|
||||
group_end += 1
|
||||
|
||||
group = payload.sentences[position:group_end]
|
||||
source = old_sentences.get(source_key)
|
||||
if source is None:
|
||||
raise ValueError("The original sentence for a queued split no longer exists.")
|
||||
if len(group) == 1:
|
||||
if "\n" in group[0].text or "\r" in group[0].text:
|
||||
raise ValueError("Unsplit batch sentences cannot contain line breaks.")
|
||||
rows.append(
|
||||
SentenceBoundary(
|
||||
index=index,
|
||||
start_ms=group[0].start_ms,
|
||||
end_ms=group[0].end_ms,
|
||||
text=group[0].text.strip(),
|
||||
language=group[0].language,
|
||||
reference_speech_duration_ms=group[0].reference_speech_duration_ms,
|
||||
)
|
||||
)
|
||||
else:
|
||||
split_text = "\n".join(item.text.strip() for item in group if item.text.strip())
|
||||
replacements = split_sentence_text(
|
||||
transcriber=transcriber,
|
||||
media_path=settings.videos_dir / video["stored_filename"],
|
||||
sentence=source,
|
||||
text=split_text,
|
||||
language=next(
|
||||
(item.language for item in group if item.language is not None),
|
||||
source.language,
|
||||
),
|
||||
)
|
||||
if len(replacements) != len(group):
|
||||
raise ValueError("Split result count does not match the queued sentences.")
|
||||
for replacement, item in zip(replacements, group):
|
||||
rows.append(
|
||||
SentenceBoundary(
|
||||
index=index,
|
||||
start_ms=replacement.start_ms,
|
||||
end_ms=replacement.end_ms,
|
||||
text=item.text.strip(),
|
||||
language=(
|
||||
item.language
|
||||
if item.language is not None
|
||||
else source.language
|
||||
),
|
||||
reference_speech_duration_ms=replacement.reference_speech_duration_ms,
|
||||
)
|
||||
)
|
||||
index += 1
|
||||
index = len(rows)
|
||||
position = group_end
|
||||
|
||||
duration_ms = video["duration_ms"]
|
||||
if duration_ms is None:
|
||||
raise ValueError("Course duration is not available.")
|
||||
previous_end = 0
|
||||
for row in rows:
|
||||
if row.start_ms < previous_end:
|
||||
raise ValueError("Batch sentences must be sorted and non-overlapping.")
|
||||
if row.end_ms > duration_ms:
|
||||
raise ValueError("Batch sentences cannot exceed the course duration.")
|
||||
previous_end = row.end_ms
|
||||
|
||||
document = old_document.model_copy(update={"sentences": rows})
|
||||
return repository.replace_sentences(document).sentences
|
||||
|
||||
|
||||
def _video_summary(row: Dict[str, Any], settings: Settings) -> VideoSummary:
|
||||
if row.get("remote_url"):
|
||||
stream_url = row["remote_url"]
|
||||
|
||||
Reference in New Issue
Block a user