Update app.py
Browse files
app.py
CHANGED
@@ -2,7 +2,6 @@
|
|
2 |
import os
|
3 |
import time
|
4 |
import tempfile
|
5 |
-
import uuid
|
6 |
import google.generativeai as genai
|
7 |
import requests
|
8 |
from flask import Flask, request, render_template, send_from_directory, url_for, flash
|
@@ -11,165 +10,177 @@ from moviepy.audio.io.AudioFileClip import AudioFileClip
|
|
11 |
from werkzeug.utils import secure_filename
|
12 |
from dotenv import load_dotenv
|
13 |
|
14 |
-
# Initialize Flask app
|
15 |
load_dotenv()
|
16 |
app = Flask(__name__)
|
17 |
|
18 |
# Configuration
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
#
|
30 |
-
UPLOAD_FOLDER =
|
31 |
-
DOWNLOAD_FOLDER =
|
32 |
-
|
33 |
-
|
34 |
-
app.config['
|
35 |
-
app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
|
36 |
-
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024
|
37 |
-
app.secret_key = os.urandom(24)
|
38 |
|
39 |
# Constants
|
40 |
VOICE_CHOICES = {
|
41 |
-
"Male (
|
42 |
-
"Female (
|
|
|
43 |
}
|
44 |
|
45 |
GEMINI_PROMPT = """
|
46 |
-
You are an expert AI scriptwriter.
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
|
53 |
-
|
54 |
-
|
55 |
"""
|
56 |
|
57 |
-
def
|
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 |
try:
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
output_path,
|
104 |
-
codec=
|
105 |
-
audio_codec=
|
106 |
-
|
|
|
107 |
)
|
|
|
|
|
|
|
|
|
|
|
108 |
finally:
|
109 |
-
if
|
110 |
-
if
|
|
|
111 |
|
112 |
@app.route('/', methods=['GET'])
|
113 |
-
def
|
114 |
-
return render_template('index.html')
|
115 |
|
116 |
@app.route('/process', methods=['POST'])
|
117 |
-
def
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
try:
|
121 |
-
# Validate upload
|
122 |
-
if 'video' not in request.files:
|
123 |
-
flash("No file selected", "error")
|
124 |
-
return render_template('index.html')
|
125 |
-
|
126 |
-
file = request.files['video']
|
127 |
-
if file.filename == '':
|
128 |
-
flash("No file selected", "error")
|
129 |
-
return render_template('index.html')
|
130 |
-
|
131 |
# Save uploaded file
|
132 |
filename = secure_filename(file.filename)
|
133 |
input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
134 |
file.save(input_path)
|
135 |
|
136 |
-
#
|
137 |
-
voice =
|
138 |
-
|
139 |
|
140 |
# Generate script and audio
|
141 |
-
script =
|
142 |
-
|
143 |
-
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
|
144 |
-
audio_path = f.name
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
# Create dubbed video
|
149 |
output_filename = f"dubbed_{filename}"
|
150 |
-
output_path =
|
151 |
-
dub_video(input_path, audio_path, output_path)
|
152 |
|
153 |
-
flash(
|
154 |
-
return render_template('
|
155 |
-
|
156 |
-
|
157 |
|
158 |
except Exception as e:
|
159 |
-
|
160 |
-
|
161 |
-
return render_template('index.html')
|
162 |
-
|
163 |
finally:
|
164 |
-
|
165 |
-
if input_path and os.path.exists(input_path):
|
166 |
os.remove(input_path)
|
167 |
-
if audio_path and os.path.exists(audio_path):
|
168 |
-
os.remove(audio_path)
|
169 |
|
170 |
@app.route('/downloads/<filename>')
|
171 |
-
def
|
172 |
return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
|
173 |
|
174 |
if __name__ == '__main__':
|
175 |
-
app.run(host=
|
|
|
2 |
import os
|
3 |
import time
|
4 |
import tempfile
|
|
|
5 |
import google.generativeai as genai
|
6 |
import requests
|
7 |
from flask import Flask, request, render_template, send_from_directory, url_for, flash
|
|
|
10 |
from werkzeug.utils import secure_filename
|
11 |
from dotenv import load_dotenv
|
12 |
|
13 |
+
# Initialize Flask app
|
14 |
load_dotenv()
|
15 |
app = Flask(__name__)
|
16 |
|
17 |
# Configuration
|
18 |
+
app.config.update({
|
19 |
+
'GEMINI_API_KEY': os.getenv('GEMINI_API_KEY'),
|
20 |
+
'TTS_API_URL': os.getenv('TTS_API_URL'),
|
21 |
+
'UPLOAD_FOLDER': 'uploads',
|
22 |
+
'DOWNLOAD_FOLDER': 'downloads',
|
23 |
+
'MAX_CONTENT_LENGTH': 100 * 1024 * 1024, # 100MB
|
24 |
+
'SECRET_KEY': os.urandom(24),
|
25 |
+
'ALLOWED_EXTENSIONS': {'mp4', 'mov', 'webm', 'avi'}
|
26 |
+
})
|
27 |
+
|
28 |
+
# Create directories if they don't exist
|
29 |
+
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
|
30 |
+
os.makedirs(app.config['DOWNLOAD_FOLDER'], exist_ok=True)
|
31 |
+
|
32 |
+
# Initialize Gemini AI
|
33 |
+
genai.configure(api_key=app.config['GEMINI_API_KEY'])
|
|
|
|
|
|
|
34 |
|
35 |
# Constants
|
36 |
VOICE_CHOICES = {
|
37 |
+
"Male (Deep Voice)": "deep_male",
|
38 |
+
"Female (Soft Tone)": "soft_female",
|
39 |
+
"Neutral (Professional)": "neutral"
|
40 |
}
|
41 |
|
42 |
GEMINI_PROMPT = """
|
43 |
+
You are an expert AI scriptwriter. Analyze this video and:
|
44 |
|
45 |
+
1. Transcribe ALL dialogue into continuous Tamil
|
46 |
+
2. Remove timestamps/speaker labels
|
47 |
+
3. Add expressive directions like [laugh] or [pause]
|
48 |
+
4. Keep natural flow and cultural context
|
49 |
|
50 |
+
Example Output:
|
51 |
+
[cheerful] வணக்கம்! [laugh] இன்று நிலைமை எப்படி இருக்கிறது? [serious] இதை கவனமாக கேளுங்கள்...
|
52 |
"""
|
53 |
|
54 |
+
def allowed_file(filename):
|
55 |
+
return '.' in filename and \
|
56 |
+
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
|
57 |
+
|
58 |
+
def generate_script(video_path):
|
59 |
+
"""Generate Tamil script using Gemini AI"""
|
60 |
+
try:
|
61 |
+
print("Uploading video to Gemini...")
|
62 |
+
video_file = genai.upload_file(video_path, mime_type="video/mp4")
|
63 |
+
|
64 |
+
# Wait for processing
|
65 |
+
while video_file.state.name == "PROCESSING":
|
66 |
+
time.sleep(5)
|
67 |
+
video_file = genai.get_file(video_file.name)
|
68 |
+
|
69 |
+
if video_file.state.name != "ACTIVE":
|
70 |
+
raise Exception("Gemini processing failed")
|
71 |
+
|
72 |
+
model = genai.GenerativeModel("models/gemini-pro-vision")
|
73 |
+
response = model.generate_content([GEMINI_PROMPT, video_file])
|
74 |
+
genai.delete_file(video_file.name)
|
75 |
+
|
76 |
+
return response.text.strip() if hasattr(response, 'text') else ""
|
77 |
+
except Exception as e:
|
78 |
+
print(f"Gemini Error: {str(e)}")
|
79 |
+
raise
|
80 |
+
|
81 |
+
def generate_audio(script, voice, tone):
|
82 |
+
"""Generate audio using TTS API"""
|
83 |
+
try:
|
84 |
+
response = requests.post(
|
85 |
+
app.config['TTS_API_URL'],
|
86 |
+
json={
|
87 |
+
"text": script,
|
88 |
+
"voice": voice,
|
89 |
+
"tone": tone
|
90 |
+
},
|
91 |
+
timeout=300
|
92 |
+
)
|
93 |
+
response.raise_for_status()
|
94 |
+
return response.content
|
95 |
+
except requests.exceptions.RequestException as e:
|
96 |
+
print(f"TTS API Error: {str(e)}")
|
97 |
+
raise
|
98 |
+
|
99 |
+
def process_video(input_path, audio_data, output_filename):
|
100 |
+
"""Combine video with new audio track"""
|
101 |
try:
|
102 |
+
with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as audio_temp:
|
103 |
+
audio_temp.write(audio_data)
|
104 |
+
audio_temp_path = audio_temp.name
|
105 |
+
|
106 |
+
video = VideoFileClip(input_path)
|
107 |
+
audio = AudioFileClip(audio_temp_path)
|
108 |
+
|
109 |
+
# Ensure audio matches video duration
|
110 |
+
if audio.duration > video.duration:
|
111 |
+
audio = audio.subclip(0, video.duration)
|
112 |
+
|
113 |
+
video.audio = audio
|
114 |
+
output_path = os.path.join(app.config['DOWNLOAD_FOLDER'], output_filename)
|
115 |
+
video.write_videofile(
|
116 |
output_path,
|
117 |
+
codec='libx264',
|
118 |
+
audio_codec='aac',
|
119 |
+
threads=4,
|
120 |
+
logger=None
|
121 |
)
|
122 |
+
|
123 |
+
return output_path
|
124 |
+
except Exception as e:
|
125 |
+
print(f"Video Processing Error: {str(e)}")
|
126 |
+
raise
|
127 |
finally:
|
128 |
+
if 'video' in locals(): video.close()
|
129 |
+
if 'audio' in locals(): audio.close()
|
130 |
+
if os.path.exists(audio_temp_path): os.remove(audio_temp_path)
|
131 |
|
132 |
@app.route('/', methods=['GET'])
|
133 |
+
def home():
|
134 |
+
return render_template('index.html', voices=VOICE_CHOICES)
|
135 |
|
136 |
@app.route('/process', methods=['POST'])
|
137 |
+
def process():
|
138 |
+
if 'video' not in request.files:
|
139 |
+
flash('No file selected', 'error')
|
140 |
+
return render_template('index.html', voices=VOICE_CHOICES)
|
141 |
+
|
142 |
+
file = request.files['video']
|
143 |
+
if file.filename == '':
|
144 |
+
flash('No file selected', 'error')
|
145 |
+
return render_template('index.html', voices=VOICE_CHOICES)
|
146 |
+
|
147 |
+
if not allowed_file(file.filename):
|
148 |
+
flash('Invalid file type. Allowed: MP4, MOV, WEBM, AVI', 'error')
|
149 |
+
return render_template('index.html', voices=VOICE_CHOICES)
|
150 |
|
151 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
# Save uploaded file
|
153 |
filename = secure_filename(file.filename)
|
154 |
input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
155 |
file.save(input_path)
|
156 |
|
157 |
+
# Get processing options
|
158 |
+
voice = request.form.get('voice', 'neutral')
|
159 |
+
tone = 'cheerful' if request.form.get('tone') == 'on' else 'neutral'
|
160 |
|
161 |
# Generate script and audio
|
162 |
+
script = generate_script(input_path)
|
163 |
+
audio_data = generate_audio(script, voice, tone)
|
|
|
|
|
164 |
|
165 |
+
# Process video
|
|
|
|
|
166 |
output_filename = f"dubbed_{filename}"
|
167 |
+
output_path = process_video(input_path, audio_data, output_filename)
|
|
|
168 |
|
169 |
+
flash('Processing completed successfully!', 'success')
|
170 |
+
return render_template('result.html',
|
171 |
+
video_url=url_for('download', filename=output_filename),
|
172 |
+
script=script)
|
173 |
|
174 |
except Exception as e:
|
175 |
+
flash(f'Processing failed: {str(e)}', 'error')
|
176 |
+
return render_template('index.html', voices=VOICE_CHOICES)
|
|
|
|
|
177 |
finally:
|
178 |
+
if 'input_path' in locals() and os.path.exists(input_path):
|
|
|
179 |
os.remove(input_path)
|
|
|
|
|
180 |
|
181 |
@app.route('/downloads/<filename>')
|
182 |
+
def download(filename):
|
183 |
return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
|
184 |
|
185 |
if __name__ == '__main__':
|
186 |
+
app.run(host='0.0.0.0', port=5000)
|