File size: 6,912 Bytes
d24a2f3 74d8a08 8a409a5 21e6f34 d24a2f3 d6208ae 202112a 8a409a5 08c3547 d24a2f3 21e6f34 08c3547 8a409a5 21e6f34 d24a2f3 8a814dc ab46005 8a814dc ab46005 8a814dc d24a2f3 21e6f34 352553f 21e6f34 74d8a08 21e6f34 8a409a5 21e6f34 8a409a5 21e6f34 d6208ae 21e6f34 9814147 21e6f34 a30e87b 21e6f34 d6208ae 21e6f34 d6208ae 21e6f34 d6208ae 21e6f34 08c3547 21e6f34 8a409a5 21e6f34 8a409a5 21e6f34 8a409a5 c599c00 |
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 149 150 151 152 153 |
import os
import time
import tempfile
import uuid
import google.generativeai as genai
import requests
from flask import Flask, request, render_template, send_from_directory, url_for, flash
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy.audio.io.AudioFileClip import AudioFileClip
from werkzeug.utils import secure_filename
from dotenv import load_dotenv
# --- 1. INITIALIZE FLASK APP AND LOAD SECRETS ---
load_dotenv()
app = Flask(__name__)
# Load secrets from the loaded environment variables
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
TTS_API_URL = os.getenv("TTS_API_URL")
# Check if secrets were loaded correctly
if not GEMINI_API_KEY:
raise ValueError("SECURITY ERROR: GEMINI_API_KEY not found in .env file!")
if not TTS_API_URL:
raise ValueError("CONFIGURATION ERROR: TTS_API_URL not found in .env file!")
# Configure the Gemini API
genai.configure(api_key=GEMINI_API_KEY)
# Configure directories and app settings
UPLOAD_FOLDER = 'uploads'
DOWNLOAD_FOLDER = 'downloads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100 MB upload limit
app.secret_key = os.urandom(24) # Secure key for flash messages
# --- 2. VOICE CHOICES & GEMINI PROMPT ---
VOICE_CHOICES = {"Male (Charon)": "Charon", "Female (Zephyr)": "Zephyr"}
GEMINI_PROMPT = """
You are an expert AI scriptwriter. Your task is to watch the provided video and transcribe ALL spoken dialogue into a SINGLE, CONTINUOUS block of modern, colloquial Tamil.
**CRITICAL INSTRUCTIONS:**
1. **Single Script:** Combine all dialogue from all speakers into one continuous script.
2. **NO Timestamps or Speaker Labels:** Do NOT include any timestamps or speaker identifiers.
3. **Incorporate Performance:** Add English style prompts (e.g., `Say happily:`, `Whisper mysteriously:`) and performance tags (e.g., `[laugh]`, `[sigh]`) directly into the text for an expressive narration.
**EXAMPLE OUTPUT:**
Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
"""
# --- 3. CORE LOGIC HELPER FUNCTIONS ---
def generate_tamil_script(video_file_path):
"""Generates a single, continuous Tamil script from the video using Gemini."""
print("Uploading file to Gemini for transcription...")
video_file = genai.upload_file(video_file_path, mime_type="video/mp4")
print("Waiting for Gemini file processing...")
while video_file.state.name == "PROCESSING":
time.sleep(5)
video_file = genai.get_file(video_file.name)
if video_file.state.name != "ACTIVE":
raise Exception(f"Gemini file processing failed: {video_file.state.name}")
print("Generating narrator script...")
model = genai.GenerativeModel(model_name="models/gemini-2.5-flash")
response = model.generate_content([GEMINI_PROMPT, video_file])
genai.delete_file(video_file.name)
if hasattr(response, 'text') and response.text:
return " ".join(response.text.strip().splitlines())
raise Exception("No valid script was generated by Gemini.")
def generate_single_audio_track(dialogue_text, voice_name, is_cheerful, output_path):
"""Generates one audio track for the entire script via TTS API."""
print(f"Requesting audio from TTS API (Voice: {voice_name}, Cheerful: {is_cheerful})")
payload = {"text": dialogue_text, "voice_name": voice_name, "cheerful": is_cheerful}
response = requests.post(TTS_API_URL, json=payload, timeout=300)
if response.status_code == 200:
with open(output_path, "wb") as f:
f.write(response.content)
return True
raise Exception(f"Error from TTS API: {response.status_code} - {response.text}")
def replace_video_audio(video_path, new_audio_path, output_path):
"""Replaces the audio of a video using the modern, correct MoviePy method."""
print("Replacing video audio using MoviePy...")
video_clip, audio_clip = None, None
try:
video_clip = VideoFileClip(video_path)
audio_clip = AudioFileClip(new_audio_path)
video_clip.audio = audio_clip
video_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", logger='bar')
finally:
if audio_clip: audio_clip.close()
if video_clip: video_clip.close()
# --- 4. FLASK WEB ROUTES ---
@app.route('/', methods=['GET'])
def index():
"""Renders the main upload page."""
return render_template('index.html')
@app.route('/process', methods=['POST'])
def process_video():
"""Handles video upload, processing, and renders the result."""
input_video_path, temp_audio_path = None, None
try:
if 'video' not in request.files or request.files['video'].filename == '':
flash("Please upload a video file.")
return render_template('index.html')
file = request.files['video']
filename = secure_filename(file.filename)
input_video_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(input_video_path)
voice_choice = request.form['voice_choice']
is_cheerful = 'cheerful' in request.form
voice_name = VOICE_CHOICES[voice_choice]
script = generate_tamil_script(input_video_path)
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
temp_audio_path = temp_audio.name
generate_single_audio_track(script, voice_name, is_cheerful, temp_audio_path)
final_video_name = f"dubbed_{filename}"
final_video_path = os.path.join(app.config['DOWNLOAD_FOLDER'], final_video_name)
replace_video_audio(input_video_path, temp_audio_path, final_video_path)
flash("Video processing complete!", "success")
return render_template('index.html',
result_video=url_for('serve_video', filename=final_video_name),
script=script)
except Exception as e:
print(f"An error occurred during processing: {e}")
flash(f"An unexpected error occurred: {e}. Please check the console and try again.", "error")
return render_template('index.html')
finally:
if input_video_path and os.path.exists(input_video_path):
os.remove(input_video_path)
if temp_audio_path and os.path.exists(temp_audio_path):
os.remove(temp_audio_path)
@app.route('/downloads/<filename>')
def serve_video(filename):
"""Serves the final dubbed video."""
return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
# --- 5. APPLICATION ENTRY POINT ---
if __name__ == '__main__':
app.run(host="0.0.0.0", port=7860) |