jacky3102 commited on
Commit
c58597d
·
verified ·
1 Parent(s): 241bdfc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -13
app.py CHANGED
@@ -20,6 +20,35 @@ model = whisper.load_model(model_size)
20
  print(f"[Startup] 模型加载完成 ✅", flush=True)
21
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  def format_timestamp(seconds: float) -> str:
24
  hours = int(seconds // 3600)
25
  minutes = int((seconds % 3600) // 60)
@@ -60,6 +89,10 @@ def group_by_chars(result, max_chars: int):
60
  if buf_text:
61
  segments.append({"start": buf_start, "end": buf_end, "text": buf_text})
62
 
 
 
 
 
63
  return segments
64
 
65
 
@@ -102,7 +135,7 @@ async def generate_subtitle(
102
  file: UploadFile = File(...),
103
  output_format: str = Form("srt"),
104
  language: Optional[str] = Form(None),
105
- max_chars: int = Form(0) # 0 = 不拆分,用 Whisper 原始分段
106
  ):
107
  valid_formats = ["srt", "vtt", "txt", "json"]
108
  if output_format not in valid_formats:
@@ -116,12 +149,11 @@ async def generate_subtitle(
116
 
117
  file_mb = len(content) / 1024 / 1024
118
  print(f"[Transcribe] 文件: {file.filename} ({file_mb:.1f} MB)", flush=True)
119
- print(f"[Transcribe] 格式: {output_format} | 语言: {language or '自动检测'} | max_chars: {max_chars or '不拆分'}", flush=True)
120
 
121
  try:
122
- transcribe_options = {"verbose": True}
123
- if max_chars > 0:
124
- transcribe_options["word_timestamps"] = True
125
  if language:
126
  transcribe_options["language"] = language
127
 
@@ -134,11 +166,12 @@ async def generate_subtitle(
134
  detected_lang = result.get("language", "unknown")
135
  print(f"[Transcribe] 完成 ✅ 耗时 {elapsed:.1f}s | 语言: {detected_lang} | 原始段数: {seg_count}", flush=True)
136
 
137
- if max_chars > 0:
138
- segments = group_by_chars(result, max_chars)
139
- print(f"[Transcribe] 按字数拆分完成,共 {len(segments)} 段(max_chars={max_chars})", flush=True)
140
- else:
141
- segments = result["segments"]
 
142
 
143
  if output_format == "srt":
144
  subtitle_text = generate_srt(segments)
@@ -149,14 +182,15 @@ async def generate_subtitle(
149
  media_type = "text/plain"
150
  filename = "subtitle.vtt"
151
  elif output_format == "txt":
152
- subtitle_text = result["text"]
153
  media_type = "text/plain"
154
  filename = "subtitle.txt"
155
  elif output_format == "json":
156
  subtitle_text = json.dumps({
157
- "text": result["text"],
158
  "segments": segments,
159
- "language": result["language"]
 
160
  }, ensure_ascii=False, indent=2)
161
  media_type = "application/json"
162
  filename = "subtitle.json"
 
20
  print(f"[Startup] 模型加载完成 ✅", flush=True)
21
 
22
 
23
+ import re
24
+
25
+ # 每种语言的默认分段字数(用户未显式传 max_chars 时生效)
26
+ DEFAULT_MAX_CHARS = {"zh": 12, "en": 55}
27
+ FALLBACK_MAX_CHARS = 40 # 其他语言兜底值
28
+
29
+ # 需要从最终字幕文本中清除的标点符号(中英文标点、引号、括号等)
30
+ # 特意不包含直引号撇号 ' ,避免破坏英文缩写(don't / it's)
31
+ _PUNCT_CHARS = ",。!?、;:“”‘’「」『』()()《》【】[]<>—–…‥·,.!?;:\""
32
+ _PUNCT_PATTERN = re.compile("[" + re.escape(_PUNCT_CHARS) + "]")
33
+
34
+
35
+ def strip_punctuation(text: str) -> str:
36
+ """去除字幕文本中的标点符号,仅做视觉清洗,不影响时间戳/断句逻辑"""
37
+ if not text:
38
+ return text
39
+ cleaned = _PUNCT_PATTERN.sub("", text)
40
+ cleaned = re.sub(r"[ \t]+", " ", cleaned).strip()
41
+ return cleaned
42
+
43
+
44
+ def resolve_max_chars(user_max_chars: int, lang_code: str) -> int:
45
+ """决定实际使用的 max_chars:用户显式指定则用用户的,否则按语言给默认值"""
46
+ if user_max_chars and user_max_chars > 0:
47
+ return user_max_chars
48
+ base_lang = (lang_code or "").split("-")[0].lower()
49
+ return DEFAULT_MAX_CHARS.get(base_lang, FALLBACK_MAX_CHARS)
50
+
51
+
52
  def format_timestamp(seconds: float) -> str:
53
  hours = int(seconds // 3600)
54
  minutes = int((seconds % 3600) // 60)
 
89
  if buf_text:
90
  segments.append({"start": buf_start, "end": buf_end, "text": buf_text})
91
 
92
+ # 断句依赖标点判断已经用完了,这里统一清洗掉标点,不影响时间戳
93
+ for seg in segments:
94
+ seg["text"] = strip_punctuation(seg["text"])
95
+
96
  return segments
97
 
98
 
 
135
  file: UploadFile = File(...),
136
  output_format: str = Form("srt"),
137
  language: Optional[str] = Form(None),
138
+ max_chars: int = Form(0) # 0/未传 = 按语言自动使用默认值(zh=12, en=55, 其他=40)
139
  ):
140
  valid_formats = ["srt", "vtt", "txt", "json"]
141
  if output_format not in valid_formats:
 
149
 
150
  file_mb = len(content) / 1024 / 1024
151
  print(f"[Transcribe] 文件: {file.filename} ({file_mb:.1f} MB)", flush=True)
152
+ print(f"[Transcribe] 格式: {output_format} | 语言: {language or '自动检测'} | max_chars: {max_chars if max_chars > 0 else '未指定(将按语言自动选择)'}", flush=True)
153
 
154
  try:
155
+ # word_timestamps 永远开启:分组逻辑现在始终执行,不再有"完全不分组"的原始模式
156
+ transcribe_options = {"verbose": True, "word_timestamps": True}
 
157
  if language:
158
  transcribe_options["language"] = language
159
 
 
166
  detected_lang = result.get("language", "unknown")
167
  print(f"[Transcribe] 完成 ✅ 耗时 {elapsed:.1f}s | 语言: {detected_lang} | 原始段数: {seg_count}", flush=True)
168
 
169
+ effective_max_chars = resolve_max_chars(max_chars, language or detected_lang)
170
+ source = "用户指定" if (max_chars and max_chars > 0) else f"按语言({language or detected_lang})自动默认"
171
+ segments = group_by_chars(result, effective_max_chars)
172
+ print(f"[Transcribe] 按字数拆分完成,共 {len(segments)} 段(max_chars={effective_max_chars},{source})", flush=True)
173
+
174
+ full_text = strip_punctuation(result["text"])
175
 
176
  if output_format == "srt":
177
  subtitle_text = generate_srt(segments)
 
182
  media_type = "text/plain"
183
  filename = "subtitle.vtt"
184
  elif output_format == "txt":
185
+ subtitle_text = full_text
186
  media_type = "text/plain"
187
  filename = "subtitle.txt"
188
  elif output_format == "json":
189
  subtitle_text = json.dumps({
190
+ "text": full_text,
191
  "segments": segments,
192
+ "language": result["language"],
193
+ "max_chars_used": effective_max_chars
194
  }, ensure_ascii=False, indent=2)
195
  media_type = "application/json"
196
  filename = "subtitle.json"