MichaelChou0806's picture
Update app.py
eee7f73 verified
raw
history blame
4.64 kB
import os
import time
import shutil
from pydub import AudioSegment
from openai import OpenAI
import gradio as gr
from fastapi import FastAPI, UploadFile, File, Form
from threading import Thread
import uvicorn
# ======================================================
# 🔐 設定區
# ======================================================
PASSWORD = os.getenv("APP_PASSWORD", "chou")
MAX_SIZE = 25 * 1024 * 1024
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
print("===== 🚀 啟動中 =====")
print(f"APP_PASSWORD: {'✅ 已載入' if PASSWORD else '❌ 未載入'}")
print(f"目前密碼內容:{PASSWORD}")
# ======================================================
# 🎧 音訊轉錄核心
# ======================================================
def split_audio_if_needed(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
parts = []
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")
parts.append(fn)
return parts
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_if_needed(path)
txts = []
for f in chunks:
with open(f, "rb") as af:
res = client.audio.transcriptions.create(model=model, file=af, response_format="text")
txts.append(res)
full_raw = "\n".join(txts)
conv_prompt = (
"請將以下內容完整轉換為「繁體中文(台灣用語)」:\n"
"規則:1) 僅做簡→繁字形轉換;2) 不要意譯或改寫;3) 不要添加任何前後綴。\n-----\n" + full_raw
)
trad = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是嚴格的繁體中文轉換器。"},
{"role": "user", "content": conv_prompt}
],
temperature=0.0,
).choices[0].message.content.strip()
sum_prompt = (
"請用台灣繁體中文撰寫摘要。若內容資訊多,可條列出重點;"
"若內容簡短,請用一句話概述即可。\n\n" + trad
)
summ = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一位精準且嚴格使用台灣繁體中文的摘要助手。"},
{"role": "user", "content": sum_prompt}
],
temperature=0.2,
).choices[0].message.content.strip()
return trad, summ
# ======================================================
# 🌐 FastAPI for 捷徑
# ======================================================
api_app = FastAPI()
@api_app.post("/api/transcribe")
async def api_transcribe(file: UploadFile = File(...), token: str = Form(...)):
if token != PASSWORD:
return {"error": "Invalid token"}
temp = file.filename
with open(temp, "wb") as f:
f.write(await file.read())
text, summary = transcribe_core(temp)
os.remove(temp)
return {"text": text, "summary": summary}
# ======================================================
# 💬 Gradio 介面
# ======================================================
def transcribe_with_password(password, file):
if password.strip() != PASSWORD:
return "❌ 密碼錯誤", "", ""
if not file:
return "⚠️ 未選擇檔案", "", ""
text, summary = transcribe_core(file.name)
return "✅ 完成", text, summary
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("## 🎧 LINE 語音轉錄與摘要(支援 .m4a / .mp4)")
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_with_password, [pw, f], [s, t, su])
# ======================================================
# 🚀 啟動
# ======================================================
def run_api():
uvicorn.run(api_app, host="0.0.0.0", port=7861)
Thread(target=run_api, daemon=True).start()
app = demo # ✅ Hugging Face 主入口使用 Gradio
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)