Athspi commited on
Commit
352553f
·
verified ·
1 Parent(s): 2f3393b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -120
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 and load secrets
15
  load_dotenv()
16
  app = Flask(__name__)
17
 
18
  # Configuration
19
- GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
20
- TTS_API_URL = os.getenv("TTS_API_URL")
21
-
22
- if not GEMINI_API_KEY:
23
- raise ValueError("GEMINI_API_KEY not found in .env file!")
24
- if not TTS_API_URL:
25
- raise ValueError("TTS_API_URL not found in .env file!")
26
-
27
- genai.configure(api_key=GEMINI_API_KEY)
28
-
29
- # Setup directories
30
- UPLOAD_FOLDER = 'uploads'
31
- DOWNLOAD_FOLDER = 'downloads'
32
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
33
- os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
34
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
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 (Charon)": "Charon",
42
- "Female (Zephyr)": "Zephyr"
 
43
  }
44
 
45
  GEMINI_PROMPT = """
46
- 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.
47
 
48
- **CRITICAL INSTRUCTIONS:**
49
- 1. Combine all dialogue into one continuous script.
50
- 2. NO timestamps or speaker labels.
51
- 3. Add performance directions (e.g., `Say happily:`, `[laugh]`) directly in the text.
52
 
53
- **EXAMPLE OUTPUT:**
54
- Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
55
  """
56
 
57
- def generate_tamil_script(video_path):
58
- """Generate Tamil script from video using Gemini AI"""
59
- print("Uploading video to Gemini...")
60
- video_file = genai.upload_file(video_path, mime_type="video/mp4")
61
-
62
- while video_file.state.name == "PROCESSING":
63
- time.sleep(5)
64
- video_file = genai.get_file(video_file.name)
65
-
66
- if video_file.state.name != "ACTIVE":
67
- raise Exception(f"Gemini processing failed: {video_file.state.name}")
68
-
69
- model = genai.GenerativeModel(model_name="models/gemini-2.5-flash")
70
- response = model.generate_content([GEMINI_PROMPT, video_file])
71
- genai.delete_file(video_file.name)
72
-
73
- if hasattr(response, 'text') and response.text:
74
- return " ".join(response.text.strip().splitlines())
75
- raise Exception("No valid script generated")
76
-
77
- def generate_audio(script, voice, is_cheerful, output_path):
78
- """Generate audio from script using TTS API"""
79
- print(f"Generating audio (Voice: {voice}, Cheerful: {is_cheerful})")
80
- payload = {
81
- "text": script,
82
- "voice_name": voice,
83
- "cheerful": is_cheerful
84
- }
85
-
86
- response = requests.post(TTS_API_URL, json=payload, timeout=300)
87
- if response.status_code == 200:
88
- with open(output_path, "wb") as f:
89
- f.write(response.content)
90
- return True
91
- raise Exception(f"TTS API error: {response.status_code} - {response.text}")
92
-
93
- def dub_video(video_path, audio_path, output_path):
94
- """Replace video audio with generated audio"""
95
- print("Dubbing video...")
96
- video_clip = AudioFileClip = None
97
-
 
 
 
 
 
 
98
  try:
99
- video_clip = VideoFileClip(video_path)
100
- audio_clip = AudioFileClip(audio_path)
101
- video_clip.audio = audio_clip
102
- video_clip.write_videofile(
 
 
 
 
 
 
 
 
 
 
103
  output_path,
104
- codec="libx264",
105
- audio_codec="aac",
106
- logger='bar'
 
107
  )
 
 
 
 
 
108
  finally:
109
- if audio_clip: audio_clip.close()
110
- if video_clip: video_clip.close()
 
111
 
112
  @app.route('/', methods=['GET'])
113
- def index():
114
- return render_template('index.html')
115
 
116
  @app.route('/process', methods=['POST'])
117
- def process_video():
118
- input_path = audio_path = None
 
 
 
 
 
 
 
 
 
 
 
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
- # Process options
137
- voice = VOICE_CHOICES[request.form['voice_choice']]
138
- cheerful = 'cheerful' in request.form
139
 
140
  # Generate script and audio
141
- script = generate_tamil_script(input_path)
142
-
143
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f:
144
- audio_path = f.name
145
 
146
- generate_audio(script, voice, cheerful, audio_path)
147
-
148
- # Create dubbed video
149
  output_filename = f"dubbed_{filename}"
150
- output_path = os.path.join(app.config['DOWNLOAD_FOLDER'], output_filename)
151
- dub_video(input_path, audio_path, output_path)
152
 
153
- flash("Video processing complete!", "success")
154
- return render_template('index.html',
155
- result_video=url_for('serve_video', filename=output_filename),
156
- script=script)
157
 
158
  except Exception as e:
159
- print(f"Error: {e}")
160
- flash(f"Processing failed: {str(e)}", "error")
161
- return render_template('index.html')
162
-
163
  finally:
164
- # Cleanup temp files
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 serve_video(filename):
172
  return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
173
 
174
  if __name__ == '__main__':
175
- app.run(host="0.0.0.0", port=7860)
 
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)