Update app.py
Browse files
app.py
CHANGED
|
@@ -20,6 +20,41 @@ def format_timestamp(seconds: float) -> str:
|
|
| 20 |
millisecs = int((seconds - int(seconds)) * 1000)
|
| 21 |
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millisecs:03d}"
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
def generate_srt(segments) -> str:
|
| 24 |
srt_content = []
|
| 25 |
for i, segment in enumerate(segments, 1):
|
|
@@ -54,7 +89,8 @@ async def health_check():
|
|
| 54 |
async def generate_subtitle(
|
| 55 |
file: UploadFile = File(...),
|
| 56 |
output_format: str = Form("srt"),
|
| 57 |
-
language: Optional[str] = Form(None)
|
|
|
|
| 58 |
):
|
| 59 |
valid_formats = ["srt", "vtt", "txt", "json"]
|
| 60 |
if output_format not in valid_formats:
|
|
@@ -67,18 +103,20 @@ async def generate_subtitle(
|
|
| 67 |
tmp_path = tmp_file.name
|
| 68 |
|
| 69 |
try:
|
| 70 |
-
transcribe_options = {}
|
| 71 |
if language:
|
| 72 |
transcribe_options["language"] = language
|
| 73 |
|
| 74 |
result = model.transcribe(tmp_path, **transcribe_options)
|
| 75 |
|
|
|
|
|
|
|
| 76 |
if output_format == "srt":
|
| 77 |
-
subtitle_text = generate_srt(
|
| 78 |
media_type = "text/plain"
|
| 79 |
filename = "subtitle.srt"
|
| 80 |
elif output_format == "vtt":
|
| 81 |
-
subtitle_text = generate_vtt(
|
| 82 |
media_type = "text/plain"
|
| 83 |
filename = "subtitle.vtt"
|
| 84 |
elif output_format == "txt":
|
|
@@ -88,7 +126,7 @@ async def generate_subtitle(
|
|
| 88 |
elif output_format == "json":
|
| 89 |
subtitle_text = json.dumps({
|
| 90 |
"text": result["text"],
|
| 91 |
-
"segments":
|
| 92 |
"language": result["language"]
|
| 93 |
}, ensure_ascii=False, indent=2)
|
| 94 |
media_type = "application/json"
|
|
|
|
| 20 |
millisecs = int((seconds - int(seconds)) * 1000)
|
| 21 |
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millisecs:03d}"
|
| 22 |
|
| 23 |
+
def group_by_chars(result, max_chars: int):
|
| 24 |
+
"""用词级时间戳按字数重新组合字幕段,时间戳精确来自 Whisper"""
|
| 25 |
+
words = []
|
| 26 |
+
for seg in result["segments"]:
|
| 27 |
+
for w in seg.get("words", []):
|
| 28 |
+
word = w["word"].strip()
|
| 29 |
+
if word:
|
| 30 |
+
words.append({"word": word, "start": w["start"], "end": w["end"]})
|
| 31 |
+
|
| 32 |
+
if not words:
|
| 33 |
+
# 没有词级时间戳,回退原始分段
|
| 34 |
+
return result["segments"]
|
| 35 |
+
|
| 36 |
+
segments = []
|
| 37 |
+
buf_text = ""
|
| 38 |
+
buf_start = None
|
| 39 |
+
buf_end = None
|
| 40 |
+
|
| 41 |
+
for w in words:
|
| 42 |
+
if buf_start is None:
|
| 43 |
+
buf_start = w["start"]
|
| 44 |
+
buf_text += w["word"]
|
| 45 |
+
buf_end = w["end"]
|
| 46 |
+
|
| 47 |
+
# 达到字数上限,或遇到句末标点就切断
|
| 48 |
+
if len(buf_text) >= max_chars or buf_text[-1] in ",。!?、,.!?":
|
| 49 |
+
segments.append({"start": buf_start, "end": buf_end, "text": buf_text})
|
| 50 |
+
buf_text = ""
|
| 51 |
+
buf_start = None
|
| 52 |
+
|
| 53 |
+
if buf_text:
|
| 54 |
+
segments.append({"start": buf_start, "end": buf_end, "text": buf_text})
|
| 55 |
+
|
| 56 |
+
return segments
|
| 57 |
+
|
| 58 |
def generate_srt(segments) -> str:
|
| 59 |
srt_content = []
|
| 60 |
for i, segment in enumerate(segments, 1):
|
|
|
|
| 89 |
async def generate_subtitle(
|
| 90 |
file: UploadFile = File(...),
|
| 91 |
output_format: str = Form("srt"),
|
| 92 |
+
language: Optional[str] = Form(None),
|
| 93 |
+
max_chars: int = Form(0) # 0 = 不拆分,用 Whisper 原始分段
|
| 94 |
):
|
| 95 |
valid_formats = ["srt", "vtt", "txt", "json"]
|
| 96 |
if output_format not in valid_formats:
|
|
|
|
| 103 |
tmp_path = tmp_file.name
|
| 104 |
|
| 105 |
try:
|
| 106 |
+
transcribe_options = {"word_timestamps": True}
|
| 107 |
if language:
|
| 108 |
transcribe_options["language"] = language
|
| 109 |
|
| 110 |
result = model.transcribe(tmp_path, **transcribe_options)
|
| 111 |
|
| 112 |
+
segments = group_by_chars(result, max_chars) if max_chars > 0 else result["segments"]
|
| 113 |
+
|
| 114 |
if output_format == "srt":
|
| 115 |
+
subtitle_text = generate_srt(segments)
|
| 116 |
media_type = "text/plain"
|
| 117 |
filename = "subtitle.srt"
|
| 118 |
elif output_format == "vtt":
|
| 119 |
+
subtitle_text = generate_vtt(segments)
|
| 120 |
media_type = "text/plain"
|
| 121 |
filename = "subtitle.vtt"
|
| 122 |
elif output_format == "txt":
|
|
|
|
| 126 |
elif output_format == "json":
|
| 127 |
subtitle_text = json.dumps({
|
| 128 |
"text": result["text"],
|
| 129 |
+
"segments": segments,
|
| 130 |
"language": result["language"]
|
| 131 |
}, ensure_ascii=False, indent=2)
|
| 132 |
media_type = "application/json"
|