MichaelChou0806 commited on
Commit
a7f0ba7
·
verified ·
1 Parent(s): 83d1da9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -99
app.py CHANGED
@@ -1,111 +1,82 @@
1
  import os
2
- import time
3
- import shutil
4
- from pydub import AudioSegment
5
- from openai import OpenAI
6
  import gradio as gr
7
- from fastapi import FastAPI, File, UploadFile
 
8
 
9
- # ========================
10
- # 🔐 基本設定
11
- # ========================
12
- PASSWORD = os.getenv("APP_PASSWORD", "defaultpass")
13
- MAX_SIZE = 25 * 1024 * 1024
14
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
15
 
16
- # FastAPI App for捷徑 API
17
- app = FastAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- # ========================
20
- # 🎧 音訊轉錄核心
21
- # ========================
22
- def split_audio_if_needed(path: str):
23
- size = os.path.getsize(path)
24
- if size <= MAX_SIZE:
25
- return [path]
26
- audio = AudioSegment.from_file(path)
27
- n = int(size / MAX_SIZE) + 1
28
- chunk_ms = len(audio) / n
29
- parts = []
30
- for i in range(n):
31
- fn = f"chunk_{i+1}.wav"
32
- audio[int(i * chunk_ms):int((i + 1) * chunk_ms)].export(fn, format="wav")
33
- parts.append(fn)
34
- return parts
35
-
36
- def transcribe_core(path: str, model: str = "whisper-1"):
37
- if path.lower().endswith(".mp4"):
38
- fixed = path[:-4] + ".m4a"
39
- try:
40
- shutil.copy(path, fixed)
41
- path = fixed
42
- print("🔧 已自動修正 mp4 → m4a")
43
- except Exception as e:
44
- print(f"⚠️ mp4→m4a 轉檔失敗:{e}")
45
-
46
- chunks = split_audio_if_needed(path)
47
- txts = []
48
- for f in chunks:
49
- with open(f, "rb") as af:
50
- t = client.audio.transcriptions.create(
51
- model=model, file=af, response_format="text"
52
- )
53
- txts.append(t)
54
- full = "\n".join(txts)
55
- summ = client.chat.completions.create(
56
  model="gpt-4o-mini",
57
- messages=[{"role": "user", "content": f"請用繁體中文摘要以下內容:\n{full}"}],
58
- temperature=0.4,
59
  ).choices[0].message.content.strip()
60
- return full, summ
61
 
62
- # ========================
63
- # 🌐 FastAPI 端點(捷徑用)
64
- # ========================
65
- @app.post("/api/transcribe")
66
- async def api_transcribe(file: UploadFile = File(...)):
67
- """供 iPhone 捷徑上傳音訊並取得 JSON"""
68
- temp = file.filename
69
- with open(temp, "wb") as f:
70
- f.write(await file.read())
71
- text, summary = transcribe_core(temp)
72
- os.remove(temp)
73
  return {"text": text, "summary": summary}
74
 
75
- @app.get("/health")
76
- def health():
77
- """捷徑可先 ping 這個確認服務運作中"""
78
- return {"status": "ok", "time": int(time.time())}
79
-
80
- # ========================
81
- # 💬 Gradio 介面
82
- # ========================
83
- def transcribe_with_pw(password, file):
84
- if password.strip() != PASSWORD:
85
- return "❌ 密碼錯誤", "", ""
86
- if not file:
87
- return "⚠️ 未選擇檔案", "", ""
88
- text, summary = transcribe_core(file.name)
89
- return "✅ 完成", text, summary
90
-
91
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
92
- gr.Markdown("## 🎧 LINE 語音轉錄與摘要工具(支援 .m4a / .mp4)")
93
- pw = gr.Textbox(label="輸入密碼", type="password")
94
- f = gr.File(label="上傳音訊檔 (.m4a/.mp3/.wav/.mp4)")
95
- run = gr.Button("開始轉錄 🚀")
96
- s = gr.Textbox(label="狀態", interactive=False)
97
- t = gr.Textbox(label="逐字稿", lines=10)
98
- su = gr.Textbox(label="摘要", lines=8)
99
-
100
- run.click(transcribe_with_pw, [pw, f], [s, t, su])
101
-
102
- # ========================
103
- # 🚀 啟動(單一埠)
104
- # ========================
105
- # 讓 Gradio 介面掛載到 FastAPI
106
- gr.mount_gradio_app(app, demo, path="/")
107
-
108
- # Hugging Face 自動綁定 port=7860,不用手動設定
 
109
  if __name__ == "__main__":
110
- import uvicorn
111
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
  import os
 
 
 
 
2
  import gradio as gr
3
+ from fastapi import FastAPI, UploadFile, Form, HTTPException
4
+ from openai import OpenAI
5
 
6
+ # 初始化 FastAPI + Gradio
7
+ app = FastAPI()
 
 
 
8
  client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9
 
10
+ # 載入安全金鑰(請在 Hugging Face → Settings → Secrets 裡設 APP_PASSWORD)
11
+ APP_PASSWORD = os.getenv("APP_PASSWORD", None)
12
+
13
+ # === API endpoint ===
14
+ @app.post("/api/transcribe")
15
+ async def transcribe_api(file: UploadFile, token: str = Form(...)):
16
+ # 驗證 token
17
+ if not APP_PASSWORD:
18
+ raise HTTPException(status_code=500, detail="Server misconfiguration: APP_PASSWORD not set.")
19
+ if token != APP_PASSWORD:
20
+ raise HTTPException(status_code=403, detail="Forbidden: invalid token.")
21
+
22
+ # 儲存臨時音訊檔
23
+ contents = await file.read()
24
+ temp_path = f"/tmp/{file.filename}"
25
+ with open(temp_path, "wb") as f:
26
+ f.write(contents)
27
+
28
+ # 語音轉文字
29
+ with open(temp_path, "rb") as audio_file:
30
+ transcript = client.audio.transcriptions.create(
31
+ model="whisper-1",
32
+ file=audio_file
33
+ )
34
 
35
+ text = transcript.text.strip()
36
+
37
+ # 簡短摘要
38
+ summary_prompt = f"請幫我用中文摘要以下內容:\n\n{text}"
39
+ summary = client.chat.completions.create(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  model="gpt-4o-mini",
41
+ messages=[{"role": "user", "content": summary_prompt}]
 
42
  ).choices[0].message.content.strip()
 
43
 
 
 
 
 
 
 
 
 
 
 
 
44
  return {"text": text, "summary": summary}
45
 
46
+
47
+ # === Gradio 前端 ===
48
+ def transcribe_ui(audio):
49
+ if audio is None:
50
+ return "請上傳音訊檔案", ""
51
+ with open(audio, "rb") as f:
52
+ transcript = client.audio.transcriptions.create(
53
+ model="whisper-1",
54
+ file=f
55
+ )
56
+ text = transcript.text.strip()
57
+
58
+ summary_prompt = f"請幫我用中文摘要以下內容:\n\n{text}"
59
+ summary = client.chat.completions.create(
60
+ model="gpt-4o-mini",
61
+ messages=[{"role": "user", "content": summary_prompt}]
62
+ ).choices[0].message.content.strip()
63
+
64
+ return text, summary
65
+
66
+
67
+ demo = gr.Interface(
68
+ fn=transcribe_ui,
69
+ inputs=gr.Audio(type="filepath", label="上傳音訊"),
70
+ outputs=[
71
+ gr.Textbox(label="轉錄結果"),
72
+ gr.Textbox(label="AI 摘要")
73
+ ],
74
+ title="LINE 語音轉錄與摘要 (安全版)",
75
+ description="上傳 LINE 語音或其他音訊,進行自動轉錄與摘要"
76
+ )
77
+
78
+ # 掛載 Gradio 到 FastAPI
79
+ app = gr.mount_gradio_app(app, demo, path="/")
80
+
81
  if __name__ == "__main__":
82
+ demo.launch(server_name="0.0.0.0", server_port=7860)