takumi0002's picture
Update app.py
7b3b0be verified
Raw
History Blame Contribute Delete
8.64 kB
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:
@staticmethod
def get_all_keys(prefix):
env_name = prefix.rstrip('_')
key = os.environ.get(env_name)
if key:
return [key]
return []
@staticmethod
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>"
@staticmethod
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)