Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from gradio_client import Client | |
| import base64 | |
| import os | |
| app = FastAPI() | |
| 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 族) | |
| # ========================================== | |
| 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 族配音員名單 | |
| # ========================================== | |
| 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 族核心語音合成 | |
| # ========================================== | |
| 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) |