File size: 5,556 Bytes
f1d02d3
dbdce7b
f1d02d3
dbdce7b
 
 
 
 
f1d02d3
 
 
 
dbdce7b
 
 
 
 
 
 
 
f1d02d3
dbdce7b
 
 
 
f1d02d3
dbdce7b
 
 
 
 
 
 
 
f1d02d3
dbdce7b
 
 
 
f1d02d3
 
 
dbdce7b
f1d02d3
dbdce7b
f1d02d3
 
 
 
 
 
 
 
 
 
dbdce7b
f1d02d3
 
 
 
 
 
 
 
 
 
 
dbdce7b
f1d02d3
 
 
 
dbdce7b
f1d02d3
 
 
dbdce7b
 
f1d02d3
dbdce7b
f1d02d3
 
 
 
 
dbdce7b
f1d02d3
 
 
dbdce7b
f1d02d3
 
 
dbdce7b
 
f1d02d3
 
 
 
 
 
 
 
 
 
dbdce7b
f1d02d3
 
dbdce7b
 
f1d02d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbdce7b
 
 
f1d02d3
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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)