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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +27 -13
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import time
3
  import shutil
 
4
  from pydub import AudioSegment
5
  from openai import OpenAI
6
  import gradio as gr
@@ -113,38 +114,31 @@ def transcribe_core(path, model):
113
  return full, summ
114
 
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
@@ -163,13 +157,27 @@ def ask_about_transcript(full_text, q):
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
-
173
  with gr.Row():
174
  password_input = gr.Textbox(
175
  label="輸入密碼",
@@ -183,7 +191,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
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",
@@ -215,7 +222,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
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 {
@@ -230,4 +236,12 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
230
  copy_summary.click(fn=None, inputs=summary_box, outputs=None, js=copy_js)
231
  copy_reply.click(fn=None, inputs=ai_reply, outputs=None, js=copy_js)
232
 
233
- demo.launch()
 
 
 
 
 
 
 
 
 
1
  import os
2
  import time
3
  import shutil
4
+ from fastapi import FastAPI, File, UploadFile
5
  from pydub import AudioSegment
6
  from openai import OpenAI
7
  import gradio as gr
 
114
  return full, summ
115
 
116
  # ========================
117
+ # 💬 主流程(Gradio)
118
  # ========================
119
  def _normalize_upload_path(file_input):
 
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
  path = getattr(file_input, "name", None)
127
  if not path and isinstance(file_input, dict):
128
  path = file_input.get("name") or file_input.get("path")
129
  return path
130
 
131
  def transcribe_with_password(session_id, password, file_input, model_choice):
 
132
  password = password.strip().replace(" ", "").replace("\u200b", "")
 
133
  locked_flag, msg = check_lock(session_id)
134
  if locked_flag:
135
  return msg, "", ""
 
136
  if password != PASSWORD:
137
  cnt, msg2 = record_failed_attempt(session_id)
138
  return msg2 or f"密碼錯誤(第 {cnt} 次)", "", ""
 
139
  path = _normalize_upload_path(file_input)
140
  if not path or not os.path.exists(path):
141
  return "找不到上傳檔案,請重新選擇。", "", ""
 
142
  clear_attempts(session_id)
143
  full, summ = transcribe_core(path, model_choice)
144
  return "✅ 轉錄完成", full, summ
 
157
  return res.choices[0].message.content.strip()
158
 
159
  # ========================
160
+ # 🌐 FastAPI for捷徑 / API
161
+ # ========================
162
+ api = FastAPI()
163
+
164
+ @api.post("/api/transcribe")
165
+ async def api_transcribe(file: UploadFile = File(...)):
166
+ """供 iPhone 捷徑上傳音檔"""
167
+ temp_path = file.filename
168
+ with open(temp_path, "wb") as f:
169
+ f.write(await file.read())
170
+ text, summary = transcribe_core(temp_path, "whisper-1")
171
+ os.remove(temp_path)
172
+ return {"text": text, "summary": summary}
173
+
174
+ # ========================
175
+ # 🌐 Gradio介面
176
  # ========================
177
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
178
  gr.Markdown("## 🎧 語音轉錄與摘要工具(私人API勿轉傳|支援 iPhone LINE .mp4)")
179
 
180
  session_state = gr.State(value=None)
 
181
  with gr.Row():
182
  password_input = gr.Textbox(
183
  label="輸入密碼",
 
191
  label="選擇模型"
192
  )
193
 
 
194
  file_input = gr.File(
195
  label="上傳音訊 / LINE 語音檔(支援 .m4a, .aac, .wav, .mp4)",
196
  file_count="single",
 
222
  )
223
  ask_btn.click(ask_about_transcript, [transcript_box, user_q], [ai_reply])
224
 
 
225
  copy_js = """
226
  async (text) => {
227
  try {
 
236
  copy_summary.click(fn=None, inputs=summary_box, outputs=None, js=copy_js)
237
  copy_reply.click(fn=None, inputs=ai_reply, outputs=None, js=copy_js)
238
 
239
+ # ✅ 同時啟動 Gradio 與 FastAPI
240
+ import threading
241
+ import uvicorn
242
+
243
+ def run_api():
244
+ uvicorn.run(api, host="0.0.0.0", port=7861)
245
+
246
+ threading.Thread(target=run_api, daemon=True).start()
247
+ demo.launch(server_name="0.0.0.0", server_port=7860)