Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| import gradio as gr | |
| import re | |
| from datetime import datetime | |
| # === α‘. αααααα Logic αααααΆααααααΌαααΌαααΆα (Theme) ααΆααααΌαααΆα === | |
| def get_seasonal_config(): | |
| month = datetime.now().month | |
| # αααΌααααΆ (αα·α ααα·ααΆ, ααααΌ, ααααΆ) - αααααααααααΆαα | |
| if month in [11, 12, 1]: | |
| return { | |
| "bg": "#0f172a", "accent": "#38bdf8", "btn": "#0369a1", | |
| "title": "βοΈ WINTER FROST EDITION" | |
| } | |
| # αααΌααααα (ααΈααΆ, ααααΆ, α§αααΆ) - αααααΆα/ααΉαααααΌα | |
| elif month in [3, 4, 5]: | |
| return { | |
| "bg": "#1c1917", "accent": "#fbbf24", "btn": "#b45309", | |
| "title": "βοΈ SUMMER BLAZE EDITION" | |
| } | |
| # αααΌαααααα/ααααΉαααΎαααα»α (αααααα) - αααααααααααα | |
| else: | |
| return { | |
| "bg": "#064e3b", "accent": "#10b981", "btn": "#047857", | |
| "title": "πΏ RAINY FOREST EDITION" | |
| } | |
| # ααΆαααααΆαααααααααααΆααααα αα α»αααααα | |
| s_theme = get_seasonal_config() | |
| # Registry for Key Status | |
| key_status_registry = {} | |
| class AIService: | |
| def get_all_keys(prefix): | |
| env_name = prefix.rstrip('_') | |
| key = os.environ.get(env_name) | |
| if key: | |
| return [key] | |
| return [] | |
| def get_status_html(engine, target_lang=None): | |
| if "Gemini" in engine: | |
| prefix = "GEMINI_API_KEY" | |
| elif "Llama" in engine: | |
| prefix = "GROQ_API_KEY" | |
| else: | |
| prefix = "SEA_LION_API_KEY" | |
| keys = AIService.get_all_keys(prefix) | |
| label = engine.split(" ")[-1] | |
| status_label = "ααααΆαααΆα/Status" | |
| # ααααΎααα accent ααΆααααΌαααΆααααααΆαα Label | |
| html = f"<div style='display: flex; gap: 8px; align-items: center; margin-bottom: 10px;'><b style='color: {s_theme['accent']}; font-size: 12px;'>{label} {status_label}:</b>" | |
| if not keys: | |
| html += "<span style='color: #ef4444; font-size: 10px;'>αααα·αααΎα Key</span>" | |
| else: | |
| for k in keys: | |
| state = key_status_registry.get(k, "ready") | |
| color = "#22c55e" if state == "ready" else "#ef4444" | |
| html += f"<div style='width: 10px; height: 10px; background: {color}; border-radius: 50%; box-shadow: 0 0 5px {color};'></div>" | |
| return html + "</div>" | |
| def call_api(engine, prompt): | |
| temp = 0.85 | |
| if "Gemini" in engine: | |
| keys = AIService.get_all_keys("GEMINI_API_KEY") | |
| models = ["gemini-3-flash", "gemini-2.5-flash", "gemini-1.5-flash"] | |
| for key in keys: | |
| for model_name in models: | |
| try: | |
| url = f"https://generativelanguage.googleapis.com/v1beta/models/{model_name}:generateContent?key={key}" | |
| res = requests.post(url, json={ | |
| "contents": [{"parts": [{"text": prompt}]}], | |
| "generationConfig": {"temperature": temp} | |
| }, timeout=30) | |
| if res.status_code == 200: | |
| key_status_registry[key] = "ready" | |
| return res.json()['candidates'][0]['content']['parts'][0]['text'].strip() | |
| except: continue | |
| key_status_registry[key] = "dead" | |
| elif "Llama" in engine: | |
| keys = AIService.get_all_keys("GROQ_API_KEY") | |
| for key in keys: | |
| try: | |
| res = requests.post("https://api.groq.com/openai/v1/chat/completions", | |
| headers={"Authorization": f"Bearer {key}"}, | |
| json={"model": "llama-3.3-70b-versatile", "messages": [{"role": "user", "content": prompt}], "temperature": temp}, timeout=30) | |
| if res.status_code == 200: | |
| key_status_registry[key] = "ready" | |
| return res.json()['choices'][0]['message']['content'].strip() | |
| except: continue | |
| else: # SEA-LION | |
| keys = AIService.get_all_keys("SEA_LION_API_KEY") | |
| for key in keys: | |
| try: | |
| res = requests.post("https://api.sea-lion.ai/v1/chat/completions", | |
| headers={"Authorization": f"Bearer {key}"}, | |
| json={"model": "aisingapore/Gemma-SEA-LION-v4-27B-IT", "messages": [{"role": "user", "content": prompt}], "temperature": 0.7}, timeout=60) | |
| if res.status_code == 200: | |
| key_status_registry[key] = "ready" | |
| return res.json()['choices'][0]['message']['content'].strip() | |
| except: continue | |
| return None | |
| def translator_hub(text, target_lang, engine): | |
| if not text.strip(): return "", AIService.get_status_html(engine) | |
| lang_name = re.sub(r'[^\w\s]', '', target_lang).strip() | |
| instruction = f"""Translate the content into {lang_name} naturally. | |
| STRICT RULES: | |
| 1. OUTPUT ONLY the translated text. NO explanations or notes. | |
| 2. If the input is SRT format, strictly preserve the timing and numbering. | |
| 3. CONTEXT & EMOTION: Understand the scene. Use natural slang and idioms (Modern/Classic). | |
| 4. PRONOUNS: | |
| - Family: αα, ααα’αΌα, αα, α’αΌα, αααΆαα/ααα, αααΆ/αα»α, ααΌα, ααΌ, ααΈα... | |
| - Couples/Home: ααααΈ, ααααααα, αα, α’αΌα... | |
| - Conflict/Enemies: ααΎα, α’α, α’αα αα, α―α... (Allow swear words and insults if in context). | |
| 5. VARIATION: If asked again, try to find better synonyms or more natural flow.""" | |
| prompt = f"{instruction}\n\nCONTENT TO TRANSLATE:\n{text}" | |
| result = AIService.call_api(engine, prompt) | |
| if result: | |
| return re.sub(r'```[a-zA-Z]*\n?|```', '', result).strip(), AIService.get_status_html(engine) | |
| return "β Error: API Key α¬ Model αα·αααααΎαααΆα", AIService.get_status_html(engine) | |
| # === α’. ααααααα½α CSS α±ααααΎαααΆα Dynamic Theme === | |
| custom_css = f""" | |
| body, .gradio-container {{ background-color: {s_theme['bg']} !important; color: white !important; }} | |
| .btn-trans {{ background-color: {s_theme['btn']} !important; color: white !important; border: 1px solid {s_theme['accent']} !important; }} | |
| .btn-clear {{ background-color: #475569 !important; color: white !important; }} | |
| h1 {{ color: {s_theme['accent']} !important; text-shadow: 0 0 10px {s_theme['accent']}55; }} | |
| .fieldset, .input-label {{ color: {s_theme['accent']} !important; }} | |
| """ | |
| with gr.Blocks(title=f"SRT Pro - {s_theme['title']}", css=custom_css) as demo: | |
| gr.Markdown(f"<h1 style='text-align: center;'>π¬ {s_theme['title']}</h1>") | |
| with gr.Row(): | |
| with gr.Column(): | |
| lang_opt = gr.Dropdown(["π°π Khmer", "πΊπΈ English", "π¨π³ Chinese", "πΉπ Thai"], value="π°π Khmer", label="Language") | |
| engine_opt = gr.Radio(["π Gemini", "π¦ Llama", "π¦ SEA-LION"], value="π Gemini", label="Model Engine") | |
| status_ui = gr.HTML(AIService.get_status_html("π Gemini")) | |
| input_box = gr.Textbox(label="Original Content", lines=10, placeholder="αααα αΌαα’ααααα α¬ SRT αα ααΈααα...") | |
| with gr.Row(): | |
| btn_clear = gr.Button("ποΈ Clear", elem_classes="btn-clear") | |
| btn_trans = gr.Button("β‘ Translate", variant="primary", elem_classes="btn-trans") | |
| with gr.Column(): | |
| output_box = gr.Textbox(label="Result", lines=20, interactive=False) | |
| btn_copy = gr.Button("π Copy Result") | |
| # Events | |
| engine_opt.change(AIService.get_status_html, inputs=[engine_opt], outputs=[status_ui]) | |
| btn_trans.click(translator_hub, [input_box, lang_opt, engine_opt], [output_box, status_ui]) | |
| btn_copy.click(None, output_box, js="(v) => {{ navigator.clipboard.writeText(v); alert('β α αααααα½α ααΆαα!'); }}") | |
| btn_clear.click(lambda: ("", ""), None, [input_box, output_box]) | |
| if __name__ == "__main__": | |
| # Launch αααααα·ααΈ | |
| demo.launch(server_name="0.0.0.0", server_port=7860, css=custom_css, ssr_mode=False) |