MichaelChou0806 commited on
Commit
e7d0f5a
·
verified ·
1 Parent(s): 51f6ab7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -0
app.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, UploadFile, File, Form
8
+ from threading import Thread
9
+ import uvicorn
10
+
11
+ # ======================================================
12
+ # 🔐 設定區
13
+ # ======================================================
14
+ PASSWORD = os.getenv("APP_PASSWORD", "chou")
15
+ MAX_SIZE = 25 * 1024 * 1024
16
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
17
+
18
+ print("===== 🚀 啟動中 =====")
19
+ print(f"APP_PASSWORD: {'✅ 已載入' if PASSWORD else '❌ 未載入'}")
20
+ print(f"目前密碼內容:{PASSWORD}")
21
+
22
+ # ======================================================
23
+ # 🎧 音訊轉錄核心
24
+ # ======================================================
25
+ def split_audio_if_needed(path):
26
+ size = os.path.getsize(path)
27
+ if size <= MAX_SIZE:
28
+ return [path]
29
+ audio = AudioSegment.from_file(path)
30
+ n = int(size / MAX_SIZE) + 1
31
+ chunk_ms = len(audio) / n
32
+ parts = []
33
+ for i in range(n):
34
+ fn = f"chunk_{i+1}.wav"
35
+ audio[int(i * chunk_ms):int((i + 1) * chunk_ms)].export(fn, format="wav")
36
+ parts.append(fn)
37
+ return parts
38
+
39
+ def transcribe_core(path, model="whisper-1"):
40
+ if path.lower().endswith(".mp4"):
41
+ fixed = path[:-4] + ".m4a"
42
+ try:
43
+ shutil.copy(path, fixed)
44
+ path = fixed
45
+ except Exception as e:
46
+ print(f"⚠️ mp4→m4a 轉檔失敗:{e}")
47
+
48
+ chunks = split_audio_if_needed(path)
49
+ txts = []
50
+ for f in chunks:
51
+ with open(f, "rb") as af:
52
+ res = client.audio.transcriptions.create(model=model, file=af, response_format="text")
53
+ txts.append(res)
54
+ full_raw = "\n".join(txts)
55
+
56
+ conv_prompt = (
57
+ "請將以下內容完整轉換為「繁體中文(台灣用語)」:\n"
58
+ "規則:1) 僅做簡→繁字形轉換;2) 不要意譯或改寫;3) 不要添加任何前後綴。\n-----\n" + full_raw
59
+ )
60
+ trad = client.chat.completions.create(
61
+ model="gpt-4o-mini",
62
+ messages=[
63
+ {"role": "system", "content": "你是嚴格的繁體中文轉換器。"},
64
+ {"role": "user", "content": conv_prompt}
65
+ ],
66
+ temperature=0.0,
67
+ ).choices[0].message.content.strip()
68
+
69
+ sum_prompt = (
70
+ "請用台灣繁體中文撰寫摘要。若內容資訊多,可條列出重點;"
71
+ "若內容簡短,請用一句話概述即可。\n\n" + trad
72
+ )
73
+ summ = client.chat.completions.create(
74
+ model="gpt-4o-mini",
75
+ messages=[
76
+ {"role": "system", "content": "你是一位精準且嚴格使用台灣繁體中文的摘要助手。"},
77
+ {"role": "user", "content": sum_prompt}
78
+ ],
79
+ temperature=0.2,
80
+ ).choices[0].message.content.strip()
81
+
82
+ return trad, summ
83
+
84
+ # ======================================================
85
+ # 🌐 FastAPI for 捷徑
86
+ # ======================================================
87
+ api_app = FastAPI()
88
+
89
+ @api_app.post("/api/transcribe")
90
+ async def api_transcribe(file: UploadFile = File(...), token: str = Form(...)):
91
+ if token != PASSWORD:
92
+ return {"error": "Invalid token"}
93
+ temp = file.filename
94
+ with open(temp, "wb") as f:
95
+ f.write(await file.read())
96
+ text, summary = transcribe_core(temp)
97
+ os.remove(temp)
98
+ return {"text": text, "summary": summary}
99
+
100
+ # ======================================================
101
+ # 💬 Gradio 介面
102
+ # ======================================================
103
+ def transcribe_with_password(password, file):
104
+ if password.strip() != PASSWORD:
105
+ return "❌ 密碼錯誤", "", ""
106
+ if not file:
107
+ return "⚠️ 未選擇檔案", "", ""
108
+ text, summary = transcribe_core(file.name)
109
+ return "✅ 完成", text, summary
110
+
111
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
112
+ gr.Markdown("## 🎧 LINE 語音轉錄與摘要(支援 .m4a / .mp4)")
113
+ pw = gr.Textbox(label="輸入密碼", type="password")
114
+ f = gr.File(label="上傳音訊檔")
115
+ run = gr.Button("開始轉錄 🚀")
116
+ s = gr.Textbox(label="狀態", interactive=False)
117
+ t = gr.Textbox(label="轉錄結果", lines=10)
118
+ su = gr.Textbox(label="AI 摘要", lines=8)
119
+ run.click(transcribe_with_password, [pw, f], [s, t, su])
120
+
121
+ # ======================================================
122
+ # 🚀 啟動
123
+ # ======================================================
124
+ def run_api():
125
+ uvicorn.run(api_app, host="0.0.0.0", port=7861)
126
+
127
+ Thread(target=run_api, daemon=True).start()
128
+ app = demo # ✅ Hugging Face 主入口使用 Gradio
129
+
130
+ if __name__ == "__main__":
131
+ demo.launch(server_name="0.0.0.0", server_port=7860)