from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from gradio_client import Client import base64 import os app = FastAPI() @app.get("/") async def root(): return {"message": "16 族語 AI 大師雲端大腦正在運行中!請透過 Chrome 套件呼叫 API。"} app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # ========================================== # 🔗 連結原語會 AI 實驗室 (16 族雙大腦) # ========================================== trans_client = Client("https://ai-labs.ilrdf.org.tw/kari-seejiq-tnpusu-ai-hmjil/") tts_client = Client("https://ai-labs.ilrdf.org.tw/hnang-kari-ai-asi-sluhay/") # 🛠️ 解析字典檔的小工具 def parse_dialect(dialect_result): if isinstance(dialect_result, dict) and 'value' in dialect_result: return dialect_result['value'] elif isinstance(dialect_result, list): return dialect_result[0] return dialect_result # ========================================== # 📚 功能 A:16 族雙向文字翻譯 (✨ 已升級支援 16 族) # ========================================== @app.post("/translate") async def translate(data: dict): source_text = data.get("text") direction = data.get("direction", "zh2indigenous") # 改為更通用的命名 ethnicity = data.get("ethnicity", "太魯閣") # ✨ 關鍵升級:動態接收族別 try: # 相容舊的 zh2trv 參數,確保原本的右鍵選單不會壞掉 if direction in ["zh2trv", "zh2indigenous", "中翻族"]: # 【中翻族】 dialect_result = trans_client.predict(ethnicity=ethnicity, api_name="/lambda_1") dialect_code = parse_dialect(dialect_result) result = trans_client.predict( text=source_text, src_lang="zho_Hant", tgt_lang=dialect_code, api_name="/translate_1" ) else: # 【族翻中】 dialect_result = trans_client.predict(ethnicity=ethnicity, api_name="/lambda") dialect_code = parse_dialect(dialect_result) result = trans_client.predict( text=source_text, src_lang=dialect_code, tgt_lang="zho_Hant", api_name="/translate" ) return {"result": result} except Exception as e: print(f"❌ {ethnicity} 翻譯發生錯誤: {e}") return {"result": f"API 呼叫失敗: {str(e)}"} # ========================================== # 📋 功能 B:獲取 16 族配音員名單 # ========================================== @app.post("/get_speakers") async def get_speakers(data: dict): ethnicity = data.get("ethnicity", "太魯閣") try: result = tts_client.predict(ethnicity=ethnicity, api_name="/lambda") if isinstance(result, dict) and 'choices' in result: speakers = [c[0] if isinstance(c, list) else c for c in result['choices']] else: speakers = result return {"speakers": speakers} except Exception as e: print(f"❌ 獲取名單失敗: {e}") return {"error": str(e)} # ========================================== # 🎵 功能 C:16 族核心語音合成 # ========================================== @app.post("/synthesize") async def synthesize(data: dict): text = data.get("text", "") ethnicity = data.get("ethnicity", "太魯閣") requested_speaker = data.get("speaker", "太魯閣_男聲") if not text: raise HTTPException(status_code=400, detail="請提供文字") sanitized_text = text.replace("!", "!").replace("?", "?").replace(",", ",").replace("。", ".") sanitized_text = sanitized_text.replace(":", ":").replace("(", "(").replace(")", ")") try: print(f"🌍 處理族別:{ethnicity},選定:{requested_speaker}") speaker_choices = tts_client.predict(ethnicity=ethnicity, api_name="/lambda") full_list = [c[0] if isinstance(c, list) else c for c in speaker_choices['choices']] if requested_speaker in full_list: target_speaker = requested_speaker else: gender_keyword = "男聲" if "男聲" in requested_speaker else "女聲" matches = [s for s in full_list if gender_keyword in s] target_speaker = matches[0] if matches else full_list[0] audio_filepath = tts_client.predict( ref=target_speaker, gen_text_input=sanitized_text[:300], api_name="/default_speaker_tts" ) if not os.path.exists(audio_filepath): raise Exception("音檔生成失敗") with open(audio_filepath, "rb") as audio_file: encoded_audio = base64.b64encode(audio_file.read()).decode('utf-8') try: os.remove(audio_filepath) except: pass return { "audio_base64": encoded_audio, "mime_type": "audio/wav", "speaker_used": target_speaker } except Exception as e: print(f"❌ 合成錯誤: {e}") raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": import uvicorn port = int(os.environ.get("PORT", 8000)) print(f"🎬 正在啟動 16 族全能超級大腦 (Port {port})...") uvicorn.run(app, host="0.0.0.0", port=port)