MichaelChou0806's picture
Update app.py
34eab1f verified
raw
history blame
3.79 kB
import os, shutil, base64
from pydub import AudioSegment
from openai import OpenAI
import gradio as gr
# === 解鎖 Gradio 檔案限制 ===
import gradio.processing_utils as pu
def _dummy_check_allowed(*a, **kw): return True
pu._check_allowed = _dummy_check_allowed
print("🔓 已解除 Gradio 上傳路徑限制")
# === 設定 ===
PASSWORD = os.getenv("APP_PASSWORD", "chou")
MAX_SIZE = 25 * 1024 * 1024
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
print("===== 🚀 啟動中 =====")
# === 分段 ===
def split_audio(path):
size = os.path.getsize(path)
if size <= MAX_SIZE: return [path]
audio = AudioSegment.from_file(path)
n = int(size / MAX_SIZE) + 1
chunk_ms = len(audio) / n
files = []
for i in range(n):
fn = f"chunk_{i+1}.wav"
audio[int(i*chunk_ms):int((i+1)*chunk_ms)].export(fn, format="wav")
files.append(fn)
return files
# === 核心轉錄 ===
def transcribe_core(path, model="whisper-1"):
if path.lower().endswith(".mp4"):
fixed = path[:-4] + ".m4a"
try: shutil.copy(path, fixed); path = fixed
except Exception as e: print(f"⚠️ mp4→m4a 失敗: {e}")
chunks = split_audio(path)
text_list = []
for f in chunks:
with open(f, "rb") as af:
txt = client.audio.transcriptions.create(model=model, file=af, response_format="text")
text_list.append(txt)
full_txt = "\n".join(text_list)
trad = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role":"system","content":"你是嚴格的繁體中文轉換器"},
{"role":"user","content":f"將以下內容轉為台灣繁體,不意譯:\n{full_txt}"}
], temperature=0.0).choices[0].message.content.strip()
summ = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role":"system","content":"你是繁體摘要助手"},
{"role":"user","content":f"用條列或一句話摘要:\n{trad}"}
], temperature=0.2).choices[0].message.content.strip()
return trad, summ
# === 外層驗證 ===
def transcribe(password, file):
if password.strip() != PASSWORD:
return "❌ 密碼錯誤", "", ""
if not file:
return "⚠️ 未選擇檔案", "", ""
# 🔒 防呆處理 base64 與錯誤 path
temp_path = "uploaded_audio.m4a"
try:
if hasattr(file, "data") and isinstance(file.data, str) and file.data.startswith("data:audio"):
base64_str = file.data.split(",")[1]
with open(temp_path, "wb") as f:
f.write(base64.b64decode(base64_str))
file.name = temp_path
elif os.path.isdir(getattr(file, "name", "")):
print("⚠️ path 是資料夾,改用 base64")
base64_str = getattr(file, "data", "").split(",")[1]
with open(temp_path, "wb") as f:
f.write(base64.b64decode(base64_str))
file.name = temp_path
except Exception as e:
print(f"⚠️ base64 寫入失敗: {e}")
return f"❌ 上傳格式錯誤: {e}", "", ""
text, summary = transcribe_core(file.name)
return "✅ 完成", text, summary
# === Gradio UI ===
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("## 🎧 LINE 語音轉錄與摘要(Base64 安全版)")
pw = gr.Textbox(label="密碼", type="password")
f = gr.File(label="上傳音訊檔")
run = gr.Button("開始轉錄 🚀")
s = gr.Textbox(label="狀態", interactive=False)
t = gr.Textbox(label="轉錄結果", lines=10)
su = gr.Textbox(label="AI 摘要", lines=8)
run.click(transcribe, [pw, f], [s, t, su])
app = demo
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)