MichaelChou0806 commited on
Commit
2c881ff
·
verified ·
1 Parent(s): 00626bc

OK 版本 (無 Toekn 認證)

Browse files
Files changed (1) hide show
  1. app.py +106 -75
app.py CHANGED
@@ -1,80 +1,111 @@
1
  import os
2
- import gradio as gr
3
- from fastapi import FastAPI, UploadFile, Form, HTTPException
4
- from fastapi.responses import JSONResponse
5
  from openai import OpenAI
6
-
7
- # === 初始化 ===
8
- print("===== 🚀 啟動中 =====")
9
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
10
- APP_PASSWORD = os.getenv("APP_PASSWORD")
11
-
12
- print(f"OPENAI_API_KEY: {OPENAI_API_KEY[:10]}********")
13
- print(f"APP_PASSWORD: {APP_PASSWORD}")
14
-
15
- client = OpenAI(api_key=OPENAI_API_KEY)
16
-
17
- # === FastAPI 主要後端 ===
18
- app = FastAPI(title="LINE Audio Transcriber")
19
-
20
- @app.get("/ping")
21
- async def ping():
22
- return {"status": "ok", "APP_PASSWORD": bool(APP_PASSWORD)}
23
-
24
- @app.post("/api/transcribe")
25
- async def transcribe_api(file: UploadFile, token: str = Form(...)):
26
- print(f"📥 收到請求: {file.filename}, token={token}")
27
-
28
- if not APP_PASSWORD:
29
- raise HTTPException(status_code=500, detail="APP_PASSWORD not set.")
30
- if token != APP_PASSWORD:
31
- raise HTTPException(status_code=403, detail="Forbidden: invalid token")
32
-
33
- temp_path = f"/tmp/{file.filename}"
34
- with open(temp_path, "wb") as f:
35
- f.write(await file.read())
36
-
37
- # whisper 轉錄
38
- with open(temp_path, "rb") as audio_file:
39
- transcript = client.audio.transcriptions.create(model="whisper-1", file=audio_file)
40
- text = transcript.text.strip()
41
-
42
- # AI 摘要
43
- summary_prompt = f"請用繁體中文摘要以下內容:\n\n{text}"
44
- summary = client.chat.completions.create(
45
- model="gpt-4o-mini",
46
- messages=[{"role": "user", "content": summary_prompt}]
47
- ).choices[0].message.content.strip()
48
-
49
- return JSONResponse({"text": text, "summary": summary})
50
-
51
- # === Gradio 介面 ===
52
- def gradio_ui(audio):
53
- if audio is None:
54
- return "請上傳音訊檔案", ""
55
- with open(audio, "rb") as f:
56
- transcript = client.audio.transcriptions.create(model="whisper-1", file=f)
57
- text = transcript.text.strip()
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
- return text, summary
64
 
65
- demo = gr.Interface(
66
- fn=gradio_ui,
67
- inputs=gr.Audio(type="filepath", label="上傳音訊"),
68
- outputs=[gr.Textbox(label="轉錄文字"), gr.Textbox(label="AI 摘要")],
69
- title="LINE 語音轉錄與摘要 (API + UI)",
70
- description="可透過捷徑上傳音訊並取得文字摘要"
71
- )
72
-
73
- # ✅ 關鍵修正:Spaces 預期的是 "app" 為 FastAPI,而不是混合物件
74
- # 我們手動掛上 Gradio,而不是使用 mount_gradio_app
75
- from gradio.routes import mount_gradio_app
76
-
77
- gr_app = mount_gradio_app(app, demo, path="/")
78
-
79
- # Hugging Face 需要一個變數叫 application
80
- application = gr_app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)