MichaelChou0806 commited on
Commit
9e0d356
·
verified ·
1 Parent(s): 8ce7c18

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -15
app.py CHANGED
@@ -83,18 +83,25 @@ def split_audio_if_needed(path):
83
  return files
84
 
85
  def transcribe_core(path, model):
86
- # ✅ 處理 iPhone LINE 語音(mp4 audio-only不轉檔,只改副檔名
87
- if path.lower().endswith(".mp4"):
88
  fixed_path = path[:-4] + ".m4a"
89
- shutil.copy(path, fixed_path) # 複製一份改名,不耗資源
90
- path = fixed_path
91
- print("🔧 已自動修正 mp4 → m4a")
 
 
 
92
 
93
  chunks = split_audio_if_needed(path)
94
  txts = []
95
  for f in chunks:
96
  with open(f, "rb") as af:
97
- res = client.audio.transcriptions.create(model=model, file=af, response_format="text")
 
 
 
 
98
  txts.append(res)
99
  full = "\n".join(txts)
100
  res = client.chat.completions.create(
@@ -108,18 +115,38 @@ def transcribe_core(path, model):
108
  # ========================
109
  # 💬 主流程
110
  # ========================
111
- def transcribe_with_password(session_id, password, file, model_choice):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  password = password.strip().replace(" ", "").replace("\u200b", "")
 
113
  locked_flag, msg = check_lock(session_id)
114
  if locked_flag:
115
  return msg, "", ""
 
116
  if password != PASSWORD:
117
  cnt, msg2 = record_failed_attempt(session_id)
118
  return msg2 or f"密碼錯誤(第 {cnt} 次)", "", ""
119
- if not file:
120
- return "請上傳音訊檔。", "", ""
 
 
 
121
  clear_attempts(session_id)
122
- full, summ = transcribe_core(file, model_choice)
123
  return "✅ 轉錄完成", full, summ
124
 
125
  def ask_about_transcript(full_text, q):
@@ -129,14 +156,17 @@ def ask_about_transcript(full_text, q):
129
  return "請輸入問題"
130
  prompt = f"以下是轉錄內容:\n{full_text}\n\n問題:{q}\n請用繁體中文回答。"
131
  res = client.chat.completions.create(
132
- model="gpt-4o-mini", messages=[{"role":"user","content":prompt}], temperature=0.6)
 
 
 
133
  return res.choices[0].message.content.strip()
134
 
135
  # ========================
136
  # 🌐 Gradio 介面
137
  # ========================
138
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
139
- gr.Markdown("## 🎧 語音轉錄與摘要工具(私人API勿轉傳)")
140
 
141
  session_state = gr.State(value=None)
142
 
@@ -153,7 +183,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
153
  label="選擇模型"
154
  )
155
 
156
- audio_input = gr.Audio(type="filepath", label="上傳音訊 (.m4a, .aac, .wav, .mp4)")
 
 
 
 
 
 
157
  transcribe_btn = gr.Button("開始轉錄與摘要 🚀")
158
  status_box = gr.Textbox(label="狀態", interactive=False)
159
  transcript_box = gr.Textbox(label="完整轉錄文字", lines=10)
@@ -174,12 +210,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
174
 
175
  transcribe_btn.click(
176
  transcribe_with_password,
177
- [session_state, password_input, audio_input, model_choice],
178
  [status_box, transcript_box, summary_box],
179
  )
180
  ask_btn.click(ask_about_transcript, [transcript_box, user_q], [ai_reply])
181
 
182
- # ✅ 正確的 JS 複製寫法 (Gradio 5.x)
183
  copy_js = """
184
  async (text) => {
185
  try {
 
83
  return files
84
 
85
  def transcribe_core(path, model):
86
+ # ✅ iPhone LINE 語音(mp4 audio-only)— 不轉檔,只複製改副檔名
87
+ if path and path.lower().endswith(".mp4"):
88
  fixed_path = path[:-4] + ".m4a"
89
+ try:
90
+ shutil.copy(path, fixed_path)
91
+ path = fixed_path
92
+ print("🔧 已自動修正 mp4 → m4a")
93
+ except Exception as e:
94
+ print(f"⚠️ mp4→m4a 複製失敗:{e},改用原檔嘗試")
95
 
96
  chunks = split_audio_if_needed(path)
97
  txts = []
98
  for f in chunks:
99
  with open(f, "rb") as af:
100
+ res = client.audio.transcriptions.create(
101
+ model=model,
102
+ file=af,
103
+ response_format="text"
104
+ )
105
  txts.append(res)
106
  full = "\n".join(txts)
107
  res = client.chat.completions.create(
 
115
  # ========================
116
  # 💬 主流程
117
  # ========================
118
+ def _normalize_upload_path(file_input):
119
+ """兼容 gr.File 回傳型態:字串 / 物件 / dict / list"""
120
+ if not file_input:
121
+ return None
122
+ if isinstance(file_input, str):
123
+ return file_input
124
+ if isinstance(file_input, list) and file_input:
125
+ return _normalize_upload_path(file_input[0])
126
+ # gradio 有時傳 UploadedFile 物件或 dict
127
+ path = getattr(file_input, "name", None)
128
+ if not path and isinstance(file_input, dict):
129
+ path = file_input.get("name") or file_input.get("path")
130
+ return path
131
+
132
+ def transcribe_with_password(session_id, password, file_input, model_choice):
133
+ # 修正注音輸入造成的隱藏字元
134
  password = password.strip().replace(" ", "").replace("\u200b", "")
135
+
136
  locked_flag, msg = check_lock(session_id)
137
  if locked_flag:
138
  return msg, "", ""
139
+
140
  if password != PASSWORD:
141
  cnt, msg2 = record_failed_attempt(session_id)
142
  return msg2 or f"密碼錯誤(第 {cnt} 次)", "", ""
143
+
144
+ path = _normalize_upload_path(file_input)
145
+ if not path or not os.path.exists(path):
146
+ return "找不到上傳檔案,請重新選擇。", "", ""
147
+
148
  clear_attempts(session_id)
149
+ full, summ = transcribe_core(path, model_choice)
150
  return "✅ 轉錄完成", full, summ
151
 
152
  def ask_about_transcript(full_text, q):
 
156
  return "請輸入問題"
157
  prompt = f"以下是轉錄內容:\n{full_text}\n\n問題:{q}\n請用繁體中文回答。"
158
  res = client.chat.completions.create(
159
+ model="gpt-4o-mini",
160
+ messages=[{"role":"user","content":prompt}],
161
+ temperature=0.6,
162
+ )
163
  return res.choices[0].message.content.strip()
164
 
165
  # ========================
166
  # 🌐 Gradio 介面
167
  # ========================
168
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
169
+ gr.Markdown("## 🎧 語音轉錄與摘要工具(私人API勿轉傳|支援 iPhone LINE .mp4)")
170
 
171
  session_state = gr.State(value=None)
172
 
 
183
  label="選擇模型"
184
  )
185
 
186
+ # File 而不是 Audio,允許 video/mp4 以及常見音訊附檔名
187
+ file_input = gr.File(
188
+ label="上傳音訊 / LINE 語音檔(支援 .m4a, .aac, .wav, .mp4)",
189
+ file_count="single",
190
+ file_types=["audio", ".mp4", ".m4a", ".aac", ".wav"]
191
+ )
192
+
193
  transcribe_btn = gr.Button("開始轉錄與摘要 🚀")
194
  status_box = gr.Textbox(label="狀態", interactive=False)
195
  transcript_box = gr.Textbox(label="完整轉錄文字", lines=10)
 
210
 
211
  transcribe_btn.click(
212
  transcribe_with_password,
213
+ [session_state, password_input, file_input, model_choice],
214
  [status_box, transcript_box, summary_box],
215
  )
216
  ask_btn.click(ask_about_transcript, [transcript_box, user_q], [ai_reply])
217
 
218
+ # ✅ 複製Gradio 5.x
219
  copy_js = """
220
  async (text) => {
221
  try {