admin can split a sentence into 2 sentences

This commit is contained in:
2026-08-30 09:25:34 +08:00
parent abc052e42d
commit a99c0cfd17
8 changed files with 577 additions and 13 deletions

View File

@@ -331,6 +331,166 @@ class VideoRepository:
reference_speech_duration_ms=row["reference_speech_duration_ms"],
)
def get_sentence(
self,
video_hash: str,
sentence_index: int,
) -> Optional[SentenceBoundary]:
normalized_hash = normalize_video_hash(video_hash)
with self._connect() as connection:
row = connection.execute(
"""
SELECT * FROM sentences
WHERE video_hash = ? AND sentence_index = ?
""",
(normalized_hash, sentence_index),
).fetchone()
if row is None:
return None
return SentenceBoundary(
index=row["sentence_index"],
start_ms=row["start_ms"],
end_ms=row["end_ms"],
text=row["text"],
language=row["language"],
reference_speech_duration_ms=row["reference_speech_duration_ms"],
)
def replace_sentence(
self,
video_hash: str,
sentence_index: int,
replacements: List[SentenceBoundary],
) -> Optional[List[SentenceBoundary]]:
"""Replace one sentence row with one or more contiguous sentence rows."""
normalized_hash = normalize_video_hash(video_hash)
if not replacements:
raise ValueError("At least one replacement sentence is required.")
with self._connect() as connection:
source = connection.execute(
"""
SELECT * FROM sentences
WHERE video_hash = ? AND sentence_index = ?
""",
(normalized_hash, sentence_index),
).fetchone()
if source is None:
return None
rows = [
SentenceBoundary(
index=index,
start_ms=replacement.start_ms,
end_ms=replacement.end_ms,
text=replacement.text.strip(),
language=replacement.language,
reference_speech_duration_ms=max(
1, replacement.reference_speech_duration_ms or 1
),
)
for index, replacement in enumerate(replacements)
]
previous_end = source["start_ms"]
for replacement in rows:
if (
replacement.start_ms < previous_end
or replacement.end_ms <= replacement.start_ms
):
raise ValueError("Replacement sentence times must be contiguous.")
if replacement.end_ms > source["end_ms"]:
raise ValueError(
"Replacement sentences cannot exceed the source sentence."
)
previous_end = replacement.end_ms
if rows[-1].end_ms != source["end_ms"]:
raise ValueError("Replacement sentences must cover the source sentence.")
# Shift following rows down first. Descending indexes avoid violating
# the (video_hash, sentence_index) primary key while making room.
added_count = len(rows) - 1
if added_count:
following = [
row["sentence_index"]
for row in connection.execute(
"""
SELECT sentence_index FROM sentences
WHERE video_hash = ? AND sentence_index > ?
ORDER BY sentence_index DESC
""",
(normalized_hash, sentence_index),
).fetchall()
]
for old_index in following:
connection.execute(
"""
UPDATE sentences SET sentence_index = ?
WHERE video_hash = ? AND sentence_index = ?
""",
(old_index + added_count, normalized_hash, old_index),
)
first = rows[0]
connection.execute(
"""
UPDATE sentences
SET start_ms = ?, end_ms = ?, text = ?, language = ?,
reference_speech_duration_ms = ?
WHERE video_hash = ? AND sentence_index = ?
""",
(
first.start_ms,
first.end_ms,
first.text,
first.language,
first.reference_speech_duration_ms,
normalized_hash,
sentence_index,
),
)
for replacement in rows[1:]:
connection.execute(
"""
INSERT INTO sentences (
video_hash, sentence_index, start_ms, end_ms, text,
language, reference_speech_duration_ms
) VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
normalized_hash,
replacement.index,
replacement.start_ms,
replacement.end_ms,
replacement.text,
replacement.language,
replacement.reference_speech_duration_ms,
),
)
connection.execute(
"UPDATE videos SET updated_at = ? WHERE video_hash = ?",
(utc_now(), normalized_hash),
)
result_rows = connection.execute(
"""
SELECT * FROM sentences
WHERE video_hash = ? AND sentence_index >= ? AND sentence_index < ?
ORDER BY sentence_index
""",
(normalized_hash, sentence_index, sentence_index + len(rows)),
).fetchall()
return [
SentenceBoundary(
index=row["sentence_index"],
start_ms=row["start_ms"],
end_ms=row["end_ms"],
text=row["text"],
language=row["language"],
reference_speech_duration_ms=row["reference_speech_duration_ms"],
)
for row in result_rows
]
def create_dub_share(
self,
*,