Spaces:
Running
Running
Upload app.py
#5
by konay233 - opened
app.py
CHANGED
|
@@ -10,39 +10,20 @@ import time
|
|
| 10 |
import numpy as np
|
| 11 |
import re
|
| 12 |
import json
|
| 13 |
-
import
|
| 14 |
from google import genai
|
| 15 |
-
from datetime import datetime, date
|
| 16 |
from PIL import Image, ImageDraw, ImageFont
|
| 17 |
|
| 18 |
# =====================================================================
|
| 19 |
-
# ⚙️
|
| 20 |
# =====================================================================
|
| 21 |
MAX_SUBTITLE_DURATION_SECONDS = 7.0
|
| 22 |
|
| 23 |
-
# 👥 LOGIN ACCOUNTS CONFIG (အကောင့်တစ်ခုတည်းကို လူအများ မျှသုံးနိုင်ပါသည်)
|
| 24 |
-
ACCOUNTS = {
|
| 25 |
-
"user1": "123123", # Free User (Session/Browser အလိုက် တစ်ရက် ၂ ပုဒ်)
|
| 26 |
-
"admin": "admin789" # Admin User (Unlimited)
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
EXPIRY_DATE_STR = "2026-8-31"
|
| 30 |
-
|
| 31 |
-
# 🛑 USER RATE LIMIT TRACKER (Session/Browser ID အလိုက် ရေတွက်မည်)
|
| 32 |
-
USER_LIMIT_TRACKER = {}
|
| 33 |
-
|
| 34 |
SYSTEM_INSTRUCTION = "You are a professional movie recap writer. Translate movie subtitle lines into natural, engaging, and thrilling Burmese movie recap style. Keep it concise."
|
| 35 |
MODEL_NAME = "gemini-2.5-flash"
|
| 36 |
|
| 37 |
def check_app_expiry():
|
| 38 |
-
|
| 39 |
-
expiry_date = datetime.strptime(EXPIRY_DATE_STR, "%Y-%m-%d").date()
|
| 40 |
-
current_date = datetime.now().date()
|
| 41 |
-
if current_date > expiry_date:
|
| 42 |
-
return False, f"❌ ဤ App သည် သက်တမ်းကုန်ဆုံးသွားပါပြီ ({EXPIRY_DATE_STR})။"
|
| 43 |
-
return True, "Active"
|
| 44 |
-
except Exception as e:
|
| 45 |
-
return False, f"လုံခြုံရေး စစ်ဆေးမှု မှားယွင်းနေပါသည်- {str(e)}"
|
| 46 |
|
| 47 |
print("Loading Multilingual Faster-Whisper Base Model...")
|
| 48 |
model = WhisperModel("base", device="cpu", compute_type="int8", cpu_threads=4)
|
|
@@ -81,12 +62,11 @@ def update_preview_image(video_path, blur_y_percent, blur_strength):
|
|
| 81 |
return None
|
| 82 |
|
| 83 |
# =====================================================================
|
| 84 |
-
# ⚡
|
| 85 |
# =====================================================================
|
| 86 |
def google_backup_translate(text, source_lang="en"):
|
| 87 |
from urllib.parse import quote
|
| 88 |
import urllib.request
|
| 89 |
-
import json
|
| 90 |
try:
|
| 91 |
url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl={source_lang}&tl=my&dt=t&q={quote(text)}"
|
| 92 |
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
|
@@ -100,7 +80,7 @@ def translate_segments_batch(segments, user_api_key, source_lang="en"):
|
|
| 100 |
if not segments: return segments
|
| 101 |
|
| 102 |
if not user_api_key or not user_api_key.strip():
|
| 103 |
-
print(f"⚠️ User က Gemini API Key မထည့်ထားပါ။ Google Translate Backup စနစ်ဖြင့် ဘာသာပြန်နေပါသည်။
|
| 104 |
for seg in segments:
|
| 105 |
seg['mm_text'] = google_backup_translate(seg['text'], source_lang=source_lang)
|
| 106 |
return segments
|
|
@@ -138,7 +118,7 @@ def translate_segments_batch(segments, user_api_key, source_lang="en"):
|
|
| 138 |
|
| 139 |
translated_map = json.loads(response_text)
|
| 140 |
except Exception as e:
|
| 141 |
-
print(f"⚠️
|
| 142 |
|
| 143 |
for idx, seg in enumerate(segments):
|
| 144 |
key_str = str(idx)
|
|
@@ -150,7 +130,7 @@ def translate_segments_batch(segments, user_api_key, source_lang="en"):
|
|
| 150 |
return segments
|
| 151 |
|
| 152 |
# =====================================================================
|
| 153 |
-
# 🎬 TEXT WRAPPING &
|
| 154 |
# =====================================================================
|
| 155 |
def segment_myanmar_syllables(text):
|
| 156 |
return re.findall(r'[a-zA-Z0-9\s\-\.,!\?]+|[\u1000-\u102a\u103f\u1040-\u1049]+[\u102b-\u103e\u1060-\u109f]*|[^\s]', text)
|
|
@@ -180,7 +160,34 @@ def draw_line_perfect_rendering(draw, position, text, font_primary, fill_color,
|
|
| 180 |
clean_text = text.replace(" ြ", "ြ").replace("ြ ", "ြ")
|
| 181 |
draw.text((x, y), clean_text, font=font_primary, fill=fill_color, stroke_width=stroke_w, stroke_fill=stroke_c)
|
| 182 |
|
| 183 |
-
def generate_voice_sync(text, voice_id, filename, desired_speed, target_duration_sec=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
rate_percentage = int((desired_speed - 1.0) * 100)
|
| 185 |
rate_str = f"{'+' if rate_percentage >= 0 else ''}{rate_percentage}%"
|
| 186 |
|
|
@@ -195,64 +202,27 @@ def generate_voice_sync(text, voice_id, filename, desired_speed, target_duration
|
|
| 195 |
communicate = edge_tts.Communicate(text, voice_id, rate=rate_str)
|
| 196 |
await communicate.save(filename)
|
| 197 |
|
| 198 |
-
try:
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
if loop.is_running():
|
| 204 |
-
import threading
|
| 205 |
-
t = threading.Thread(target=lambda: asyncio.run(_async_gen()))
|
| 206 |
t.start()
|
| 207 |
t.join()
|
| 208 |
-
|
| 209 |
-
|
| 210 |
|
| 211 |
# =====================================================================
|
| 212 |
-
# 🎬
|
| 213 |
# =====================================================================
|
| 214 |
def process_magic_recap_video(
|
| 215 |
video_path, user_api_key, ratio_select, background_fill, enable_zoom, zoom_level,
|
| 216 |
-
logo_file, mirror_flip, filter_color, voice_gender, tone_style,
|
| 217 |
text_color, stroke_color, blur_y_percent, blur_strength, sub_pos_percent, desired_speed,
|
| 218 |
-
session_id, # 👈 Browser Session State
|
| 219 |
-
request: gr.Request,
|
| 220 |
progress=gr.Progress(track_tqdm=True)
|
| 221 |
):
|
| 222 |
is_valid, msg = check_app_expiry()
|
| 223 |
if not is_valid: raise gr.Error(msg)
|
| 224 |
if not video_path: return None
|
| 225 |
-
|
| 226 |
-
# 🛑 LOGIN ACCOUNT & SESSION ID ယူခြင်း
|
| 227 |
-
login_username = request.username if (request and hasattr(request, 'username')) else "user1"
|
| 228 |
-
|
| 229 |
-
if login_username == "admin":
|
| 230 |
-
user_identifier = "admin"
|
| 231 |
-
else:
|
| 232 |
-
# Browser Session ID ဖြင့် လူတစ်ဦးစီကို သီးခြား ခွဲခြားမည်
|
| 233 |
-
user_identifier = f"session_{session_id}"
|
| 234 |
-
|
| 235 |
-
current_timestamp = time.time()
|
| 236 |
-
print(f"👤 အသုံးပြုသူ Account: {login_username} | Identifier: {user_identifier}")
|
| 237 |
-
|
| 238 |
-
# Admin မဟုတ်ပါက Session/Browser အလိုက် ၁ ရက် (၂) ပုဒ် Limit စစ်ဆေးမည်
|
| 239 |
-
if user_identifier != "admin":
|
| 240 |
-
if user_identifier in USER_LIMIT_TRACKER:
|
| 241 |
-
user_data = USER_LIMIT_TRACKER[user_identifier]
|
| 242 |
-
first_time = user_data["first_time"]
|
| 243 |
-
count = user_data["count"]
|
| 244 |
-
elapsed_time = current_timestamp - first_time
|
| 245 |
-
|
| 246 |
-
if elapsed_time < 86400: # ၂၄ နာရီ အတွင်း
|
| 247 |
-
if count >= 2: # ၂ ပုဒ် ပြည့်ပါက တားဆီးမည်
|
| 248 |
-
remaining_seconds = 86400 - elapsed_time
|
| 249 |
-
rem_hours = int(remaining_seconds // 3600)
|
| 250 |
-
rem_mins = int((remaining_seconds % 3600) // 60)
|
| 251 |
-
raise gr.Error(f"❌ ခွင့်ပြုချက်ကျော်လွန်နေပါသည်။ သင့် Browser မှ တစ်ရက်လျှင် ဗီဒီယို (၂) ပုဒ်သာ ထုတ်ယူခွင့်ရှိသည်။ ပြန်လည်စမ်းသပ်ရန် {rem_hours} နာရီ {rem_mins} မိနစ် လိုအပ်ပါသေးသည်။")
|
| 252 |
-
else:
|
| 253 |
-
USER_LIMIT_TRACKER[user_identifier] = {"count": 0, "first_time": current_timestamp}
|
| 254 |
-
else:
|
| 255 |
-
USER_LIMIT_TRACKER[user_identifier] = {"count": 0, "first_time": current_timestamp}
|
| 256 |
|
| 257 |
temp_dir = "temp_space_workspace"
|
| 258 |
if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
|
|
@@ -293,9 +263,9 @@ def process_magic_recap_video(
|
|
| 293 |
|
| 294 |
detected_lang = info.language
|
| 295 |
if user_api_key and user_api_key.strip():
|
| 296 |
-
progress(0.30, desc=f"⚡ ထည့်သွင်းထားသော Gemini API
|
| 297 |
else:
|
| 298 |
-
progress(0.30, desc=f"🌐 Google Translate Backup
|
| 299 |
|
| 300 |
segments = translate_segments_batch(segments, user_api_key, source_lang=detected_lang)
|
| 301 |
|
|
@@ -328,7 +298,7 @@ def process_magic_recap_video(
|
|
| 328 |
raw_seg_filename = os.path.join(temp_dir, f"raw_{idx}.mp3")
|
| 329 |
fixed_seg_filename = os.path.join(temp_dir, f"fixed_{idx}.mp3")
|
| 330 |
|
| 331 |
-
generate_voice_sync(mm_text, voice_id, raw_seg_filename, 1.15, orig_dur)
|
| 332 |
|
| 333 |
if os.path.exists(raw_seg_filename) and os.path.getsize(raw_seg_filename) > 0:
|
| 334 |
subprocess.run([
|
|
@@ -356,7 +326,10 @@ def process_magic_recap_video(
|
|
| 356 |
|
| 357 |
font_size = int(target_h * 0.038)
|
| 358 |
font_path = "Myanmar font.ttf"
|
| 359 |
-
|
|
|
|
|
|
|
|
|
|
| 360 |
|
| 361 |
t_color, s_color = hex_to_rgb(text_color), hex_to_rgb(stroke_color)
|
| 362 |
b_h = int(target_h * 0.12)
|
|
@@ -462,12 +435,6 @@ def process_magic_recap_video(
|
|
| 462 |
subprocess.run(['ffmpeg', '-y', '-i', final_burn_temp, '-c:v', 'libx264', '-preset', 'ultrafast', '-threads', '0', '-pix_fmt', 'yuv420p', output_video_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 463 |
|
| 464 |
if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
|
| 465 |
-
|
| 466 |
-
# 📈 ဗီဒီယို အောင်မြင်စွာ ထုတ်ယူပြီးမှသာ ထို Session ၏ Count ကို ၁ တိုးမည်
|
| 467 |
-
if user_identifier != "admin":
|
| 468 |
-
USER_LIMIT_TRACKER[user_identifier]["count"] += 1
|
| 469 |
-
print(f"✅ [{user_identifier}] ဗီဒီယိုထုတ်ယူမှု အောင်မြင်ပါသည်။ ယနေ့ ထုတ်ယူပြီးစီးမှု: {USER_LIMIT_TRACKER[user_identifier]['count']} ကြိမ်")
|
| 470 |
-
|
| 471 |
return output_video_path
|
| 472 |
|
| 473 |
except Exception as e:
|
|
@@ -478,18 +445,9 @@ def process_magic_recap_video(
|
|
| 478 |
# 🎨 GRADIO INTERFACE
|
| 479 |
# =====================================================================
|
| 480 |
def create_main_app_interface():
|
| 481 |
-
is_valid, msg = check_app_expiry()
|
| 482 |
-
|
| 483 |
with gr.Blocks() as main_app:
|
| 484 |
-
#
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
gr.Markdown("<h1 style='text-align: center; color: #5B50F3;'>✨ Video Auto Recap (Multi-Account) ✨</h1>")
|
| 488 |
-
if not is_valid:
|
| 489 |
-
gr.Markdown(f"### {msg}")
|
| 490 |
-
return main_app
|
| 491 |
-
|
| 492 |
-
gr.Markdown("<div style='background-color: #333; padding: 10px; border-radius: 8px; text-align: center; color: #fff;'>📢 <b>Free App Rules:</b> တစ်ရက်လျှင် Video (၂) ပုဒ် အခမဲ့ ထုတ်ယူခွင့် ရှိပါသည်</div>")
|
| 493 |
|
| 494 |
with gr.Row():
|
| 495 |
with gr.Column(scale=2):
|
|
@@ -497,13 +455,14 @@ def create_main_app_interface():
|
|
| 497 |
preview_image = gr.Image(label="🖼️ Interactive Blur Preview", interactive=False)
|
| 498 |
|
| 499 |
with gr.Column(scale=1):
|
| 500 |
-
user_api_key = gr.Textbox(label="🔑 သင်၏ Gemini API Key ထည့်ရန် (
|
| 501 |
ratio_select = gr.Dropdown(choices=["9:16 (Tiktok/Reels)", "16:9 (Landscape)"], value="9:16 (Tiktok/Reels)", label="ဗီဒီယိုအမျိုးအစား")
|
| 502 |
background_fill = gr.Radio(choices=["Blur Background (အနောက်ခံ ဝါးမည်)", "Black Background (အမည်းရောင်ထားမည်)"], value="Blur Background (အနောက်ခံ ဝါးမည်)", label="နောက်ခံ ဖြည့်စွက်မှု")
|
| 503 |
|
| 504 |
with gr.Column(variant="panel"):
|
| 505 |
gr.Markdown("### 🗣️ အသံနှင့် စာတန်းထိုး ဆက်တင်များ")
|
| 506 |
-
|
|
|
|
| 507 |
tone_style = gr.Dropdown(choices=["Thriller", "Comedy", "Dramatic", "Action/Epic"], value="Thriller", label="🎬 Narrative Tone")
|
| 508 |
text_color_input = gr.ColorPicker(label="စာလုံးအရောင်", value="#FFFF00")
|
| 509 |
stroke_color_input = gr.ColorPicker(label="အနားသတ်အရောင်", value="#000000")
|
|
@@ -532,21 +491,15 @@ def create_main_app_interface():
|
|
| 532 |
fn=process_magic_recap_video,
|
| 533 |
inputs=[
|
| 534 |
video_input, user_api_key, ratio_select, background_fill, enable_zoom, zoom_level,
|
| 535 |
-
logo_file, mirror_flip, filter_color, voice_select, tone_style, text_color_input,
|
| 536 |
-
stroke_color_input, blur_y_percent, blur_strength, sub_pos_percent, desired_speed
|
| 537 |
-
session_id_state # 👈 session_id ကို Function ထဲ ပို့ပေးခြင်း
|
| 538 |
],
|
| 539 |
outputs=[output_video]
|
| 540 |
)
|
| 541 |
|
| 542 |
return main_app
|
| 543 |
|
| 544 |
-
def verify_member(username, password):
|
| 545 |
-
if username in ACCOUNTS and ACCOUNTS[username] == password:
|
| 546 |
-
return True
|
| 547 |
-
return False
|
| 548 |
-
|
| 549 |
demo_app = create_main_app_interface()
|
| 550 |
|
| 551 |
if __name__ == "__main__":
|
| 552 |
-
demo_app.launch(
|
|
|
|
| 10 |
import numpy as np
|
| 11 |
import re
|
| 12 |
import json
|
| 13 |
+
import threading
|
| 14 |
from google import genai
|
|
|
|
| 15 |
from PIL import Image, ImageDraw, ImageFont
|
| 16 |
|
| 17 |
# =====================================================================
|
| 18 |
+
# ⚙️ SETTINGS & CONFIG (Login & Limits Removed)
|
| 19 |
# =====================================================================
|
| 20 |
MAX_SUBTITLE_DURATION_SECONDS = 7.0
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
SYSTEM_INSTRUCTION = "You are a professional movie recap writer. Translate movie subtitle lines into natural, engaging, and thrilling Burmese movie recap style. Keep it concise."
|
| 23 |
MODEL_NAME = "gemini-2.5-flash"
|
| 24 |
|
| 25 |
def check_app_expiry():
|
| 26 |
+
return True, "Active"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
print("Loading Multilingual Faster-Whisper Base Model...")
|
| 29 |
model = WhisperModel("base", device="cpu", compute_type="int8", cpu_threads=4)
|
|
|
|
| 62 |
return None
|
| 63 |
|
| 64 |
# =====================================================================
|
| 65 |
+
# ⚡ TRANSLATION ENGINE
|
| 66 |
# =====================================================================
|
| 67 |
def google_backup_translate(text, source_lang="en"):
|
| 68 |
from urllib.parse import quote
|
| 69 |
import urllib.request
|
|
|
|
| 70 |
try:
|
| 71 |
url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl={source_lang}&tl=my&dt=t&q={quote(text)}"
|
| 72 |
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
|
|
|
| 80 |
if not segments: return segments
|
| 81 |
|
| 82 |
if not user_api_key or not user_api_key.strip():
|
| 83 |
+
print(f"⚠️ User က Gemini API Key မထည့်ထားပါ။ Google Translate Backup စနစ်ဖြင့် ဘာသာပြန်နေပါသည်။")
|
| 84 |
for seg in segments:
|
| 85 |
seg['mm_text'] = google_backup_translate(seg['text'], source_lang=source_lang)
|
| 86 |
return segments
|
|
|
|
| 118 |
|
| 119 |
translated_map = json.loads(response_text)
|
| 120 |
except Exception as e:
|
| 121 |
+
print(f"⚠️ Gemini API Error: {e} -> Google Translate သို့ ပြောင်းလဲနေသည်။")
|
| 122 |
|
| 123 |
for idx, seg in enumerate(segments):
|
| 124 |
key_str = str(idx)
|
|
|
|
| 130 |
return segments
|
| 131 |
|
| 132 |
# =====================================================================
|
| 133 |
+
# 🎬 TEXT WRAPPING & VOICE GENERATION SYSTEMS
|
| 134 |
# =====================================================================
|
| 135 |
def segment_myanmar_syllables(text):
|
| 136 |
return re.findall(r'[a-zA-Z0-9\s\-\.,!\?]+|[\u1000-\u102a\u103f\u1040-\u1049]+[\u102b-\u103e\u1060-\u109f]*|[^\s]', text)
|
|
|
|
| 160 |
clean_text = text.replace(" ြ", "ြ").replace("ြ ", "ြ")
|
| 161 |
draw.text((x, y), clean_text, font=font_primary, fill=fill_color, stroke_width=stroke_w, stroke_fill=stroke_c)
|
| 162 |
|
| 163 |
+
def generate_voice_sync(text, voice_id, filename, desired_speed, target_duration_sec=None, user_api_key=None, voice_engine="Edge-TTS"):
|
| 164 |
+
# Google AI Studio Audio (Gemini Native Audio) အသုံးပြုလိုပါက
|
| 165 |
+
if voice_engine == "Google AI Studio Audio" and user_api_key and user_api_key.strip():
|
| 166 |
+
try:
|
| 167 |
+
client = genai.Client(api_key=user_api_key.strip())
|
| 168 |
+
prompt = f"Read the following Burmese movie recap text naturally and clearly with an engaging narrative tone. Generate audio output:\n{text}"
|
| 169 |
+
response = client.models.generate_content(
|
| 170 |
+
model=MODEL_NAME,
|
| 171 |
+
contents=prompt,
|
| 172 |
+
config={
|
| 173 |
+
"response_mime_type": "audio/mp3",
|
| 174 |
+
}
|
| 175 |
+
)
|
| 176 |
+
audio_saved = False
|
| 177 |
+
for candidate in response.candidates:
|
| 178 |
+
for part in candidate.content.parts:
|
| 179 |
+
if hasattr(part, 'inline_data') and part.inline_data:
|
| 180 |
+
with open(filename, "wb") as f:
|
| 181 |
+
f.write(part.inline_data.data)
|
| 182 |
+
audio_saved = True
|
| 183 |
+
break
|
| 184 |
+
if audio_saved: break
|
| 185 |
+
if audio_saved and os.path.exists(filename) and os.path.getsize(filename) > 0:
|
| 186 |
+
return
|
| 187 |
+
except Exception as e:
|
| 188 |
+
print(f"⚠️ Google AI Studio Audio Error: {e} -> Edge-TTS သို့ အလိုအလျောက် ပြောင်းလဲနေသည်။")
|
| 189 |
+
|
| 190 |
+
# Default: Edge-TTS
|
| 191 |
rate_percentage = int((desired_speed - 1.0) * 100)
|
| 192 |
rate_str = f"{'+' if rate_percentage >= 0 else ''}{rate_percentage}%"
|
| 193 |
|
|
|
|
| 202 |
communicate = edge_tts.Communicate(text, voice_id, rate=rate_str)
|
| 203 |
await communicate.save(filename)
|
| 204 |
|
| 205 |
+
try:
|
| 206 |
+
def run_in_thread():
|
| 207 |
+
asyncio.run(_async_gen())
|
| 208 |
+
t = threading.Thread(target=run_in_thread)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
t.start()
|
| 210 |
t.join()
|
| 211 |
+
except Exception as e:
|
| 212 |
+
print(f"Audio Generation Error: {e}")
|
| 213 |
|
| 214 |
# =====================================================================
|
| 215 |
+
# 🎬 PRODUCTION AUTOMATION ENGINE (Limits Removed)
|
| 216 |
# =====================================================================
|
| 217 |
def process_magic_recap_video(
|
| 218 |
video_path, user_api_key, ratio_select, background_fill, enable_zoom, zoom_level,
|
| 219 |
+
logo_file, mirror_flip, filter_color, voice_gender, voice_engine_select, tone_style,
|
| 220 |
text_color, stroke_color, blur_y_percent, blur_strength, sub_pos_percent, desired_speed,
|
|
|
|
|
|
|
| 221 |
progress=gr.Progress(track_tqdm=True)
|
| 222 |
):
|
| 223 |
is_valid, msg = check_app_expiry()
|
| 224 |
if not is_valid: raise gr.Error(msg)
|
| 225 |
if not video_path: return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
temp_dir = "temp_space_workspace"
|
| 228 |
if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
|
|
|
|
| 263 |
|
| 264 |
detected_lang = info.language
|
| 265 |
if user_api_key and user_api_key.strip():
|
| 266 |
+
progress(0.30, desc=f"⚡ ထည့်သွင်းထားသော Gemini API စနစ်ဖြင့် ဘာသာပြန်နေပါသည်...")
|
| 267 |
else:
|
| 268 |
+
progress(0.30, desc=f"🌐 Google Translate Backup စနစ်ဖြင့် ဘာသာပြန်နေပါသည်...")
|
| 269 |
|
| 270 |
segments = translate_segments_batch(segments, user_api_key, source_lang=detected_lang)
|
| 271 |
|
|
|
|
| 298 |
raw_seg_filename = os.path.join(temp_dir, f"raw_{idx}.mp3")
|
| 299 |
fixed_seg_filename = os.path.join(temp_dir, f"fixed_{idx}.mp3")
|
| 300 |
|
| 301 |
+
generate_voice_sync(mm_text, voice_id, raw_seg_filename, 1.15, orig_dur, user_api_key=user_api_key, voice_engine=voice_engine_select)
|
| 302 |
|
| 303 |
if os.path.exists(raw_seg_filename) and os.path.getsize(raw_seg_filename) > 0:
|
| 304 |
subprocess.run([
|
|
|
|
| 326 |
|
| 327 |
font_size = int(target_h * 0.038)
|
| 328 |
font_path = "Myanmar font.ttf"
|
| 329 |
+
if os.path.exists(font_path):
|
| 330 |
+
font_primary = ImageFont.truetype(font_path, font_size)
|
| 331 |
+
else:
|
| 332 |
+
font_primary = ImageFont.load_default()
|
| 333 |
|
| 334 |
t_color, s_color = hex_to_rgb(text_color), hex_to_rgb(stroke_color)
|
| 335 |
b_h = int(target_h * 0.12)
|
|
|
|
| 435 |
subprocess.run(['ffmpeg', '-y', '-i', final_burn_temp, '-c:v', 'libx264', '-preset', 'ultrafast', '-threads', '0', '-pix_fmt', 'yuv420p', output_video_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
| 436 |
|
| 437 |
if os.path.exists(temp_dir): shutil.rmtree(temp_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
return output_video_path
|
| 439 |
|
| 440 |
except Exception as e:
|
|
|
|
| 445 |
# 🎨 GRADIO INTERFACE
|
| 446 |
# =====================================================================
|
| 447 |
def create_main_app_interface():
|
|
|
|
|
|
|
| 448 |
with gr.Blocks() as main_app:
|
| 449 |
+
gr.Markdown("<h1 style='text-align: center; color: #5B50F3;'>✨ Video Auto Recap (Unlimited & AI Audio) ✨</h1>")
|
| 450 |
+
gr.Markdown("<div style='background-color: #222; padding: 10px; border-radius: 8px; text-align: center; color: #fff;'>📢 <b>Notice:</b> Login စနစ်နှင့် ကန့်သတ်ချက်များ (Limits) အားလုံး ဖြုတ်ထားပြီးဖြစ်ပါသည်။ Google AI Studio Audio (သို့) Edge-TTS ကို ရွေးချယ်အသုံးပြုနိုင်ပါသည်။</div>")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
|
| 452 |
with gr.Row():
|
| 453 |
with gr.Column(scale=2):
|
|
|
|
| 455 |
preview_image = gr.Image(label="🖼️ Interactive Blur Preview", interactive=False)
|
| 456 |
|
| 457 |
with gr.Column(scale=1):
|
| 458 |
+
user_api_key = gr.Textbox(label="🔑 သင်၏ Gemini API Key ထည့်ရန် (ဘာသာပြန်နှင့် Google AI Studio အသံအတွက် လိုအပ်သည်)", placeholder="AIZAcy...", type="password")
|
| 459 |
ratio_select = gr.Dropdown(choices=["9:16 (Tiktok/Reels)", "16:9 (Landscape)"], value="9:16 (Tiktok/Reels)", label="ဗီဒီယိုအမျိုးအစား")
|
| 460 |
background_fill = gr.Radio(choices=["Blur Background (အနောက်ခံ ဝါးမည်)", "Black Background (အမည်းရောင်ထားမည်)"], value="Blur Background (အနောက်ခံ ဝါးမည်)", label="နောက်ခံ ဖြည့်စွက်မှု")
|
| 461 |
|
| 462 |
with gr.Column(variant="panel"):
|
| 463 |
gr.Markdown("### 🗣️ အသံနှင့် စာတန်းထိုး ဆက်တင်များ")
|
| 464 |
+
voice_engine_select = gr.Radio(choices=["Edge-TTS", "Google AI Studio Audio"], value="Google AI Studio Audio", label="🎙️ အသံထုတ်လုပ်သည့်စနစ် (Voice Engine)")
|
| 465 |
+
voice_select = gr.Radio(choices=["🧕 မိန်းကလေး (Female)", "👨 ယောကျာ်လေး (Male)"], value="🧕 မိန်းကလေး (Female)", label="AI အသံ ပုံစံ (Edge-TTS အတွက်သာ)")
|
| 466 |
tone_style = gr.Dropdown(choices=["Thriller", "Comedy", "Dramatic", "Action/Epic"], value="Thriller", label="🎬 Narrative Tone")
|
| 467 |
text_color_input = gr.ColorPicker(label="စာလုံးအရောင်", value="#FFFF00")
|
| 468 |
stroke_color_input = gr.ColorPicker(label="အနားသတ်အရောင်", value="#000000")
|
|
|
|
| 491 |
fn=process_magic_recap_video,
|
| 492 |
inputs=[
|
| 493 |
video_input, user_api_key, ratio_select, background_fill, enable_zoom, zoom_level,
|
| 494 |
+
logo_file, mirror_flip, filter_color, voice_select, voice_engine_select, tone_style, text_color_input,
|
| 495 |
+
stroke_color_input, blur_y_percent, blur_strength, sub_pos_percent, desired_speed
|
|
|
|
| 496 |
],
|
| 497 |
outputs=[output_video]
|
| 498 |
)
|
| 499 |
|
| 500 |
return main_app
|
| 501 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
demo_app = create_main_app_interface()
|
| 503 |
|
| 504 |
if __name__ == "__main__":
|
| 505 |
+
demo_app.launch(theme=gr.themes.Default())
|