Athspi commited on
Commit
dd03a74
·
verified ·
1 Parent(s): ab41f5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -118
app.py CHANGED
@@ -4,40 +4,36 @@ import tempfile
4
  import uuid
5
  import google.generativeai as genai
6
  import requests
7
- from flask import Flask, request, render_template, send_from_directory, url_for, flash
8
  from moviepy.video.io.VideoFileClip import VideoFileClip
9
  from moviepy.audio.io.AudioFileClip import AudioFileClip
10
  from werkzeug.utils import secure_filename
11
  from dotenv import load_dotenv
12
 
13
- # --- 1. INITIALIZE FLASK APP AND LOAD SECRETS ---
14
  load_dotenv()
15
- app = Flask(__name__)
16
 
17
- # Load secrets from environment variables
18
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
19
  TTS_API_URL = os.getenv("TTS_API_URL")
20
 
21
- # Validate required configurations
22
- if not GEMINI_API_KEY:
23
- raise ValueError("SECURITY ERROR: GEMINI_API_KEY not found in .env file!")
24
- if not TTS_API_URL:
25
- raise ValueError("CONFIGURATION ERROR: TTS_API_URL not found in .env file!")
26
 
27
- # Configure Gemini AI
28
  genai.configure(api_key=GEMINI_API_KEY)
29
 
30
- # Configure directories
31
  UPLOAD_FOLDER = 'uploads'
32
  DOWNLOAD_FOLDER = 'downloads'
33
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
34
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
35
  app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
36
  app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
37
- app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100 MB upload limit
38
- app.secret_key = os.urandom(24) # Secure key for flash messages
39
 
40
- # --- 2. APPLICATION CONFIGURATION ---
41
  VOICE_CHOICES = {
42
  "Male (Charon)": "Charon",
43
  "Female (Zephyr)": "Zephyr"
@@ -55,136 +51,143 @@ You are an expert AI scriptwriter. Your task is to watch the provided video and
55
  Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
56
  """
57
 
58
- # --- 3. CORE APPLICATION FUNCTIONS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- def generate_tamil_script(video_file_path):
61
- """Generates a Tamil script from the video using Gemini AI."""
62
- print("Uploading video to Gemini for transcription...")
63
- video_file = genai.upload_file(video_file_path, mime_type="video/mp4")
64
-
65
- # Wait for file processing
66
- while video_file.state.name == "PROCESSING":
67
- time.sleep(5)
68
- video_file = genai.get_file(video_file.name)
69
-
70
- if video_file.state.name != "ACTIVE":
71
- raise Exception(f"Gemini file processing failed: {video_file.state.name}")
72
-
73
- print("Generating script...")
74
- model = genai.GenerativeModel(model_name="models/gemini-2.5-flash")
75
- response = model.generate_content([GEMINI_PROMPT, video_file])
76
- genai.delete_file(video_file.name)
77
-
78
- if hasattr(response, 'text') and response.text:
79
- return " ".join(response.text.strip().splitlines())
80
- raise Exception("No valid script was generated by Gemini.")
81
-
82
- def generate_audio_track(script_text, voice_name, is_cheerful, output_path):
83
- """Generates audio from script using TTS API."""
84
- print(f"Generating audio (Voice: {voice_name}, Cheerful: {is_cheerful})")
85
- payload = {
86
- "text": script_text,
87
- "voice_name": voice_name,
88
- "cheerful": is_cheerful
89
- }
90
-
91
- response = requests.post(TTS_API_URL, json=payload, timeout=300)
92
- if response.status_code == 200:
93
  with open(output_path, "wb") as f:
94
  f.write(response.content)
95
- return True
96
- raise Exception(f"TTS API Error: {response.status_code} - {response.text}")
97
-
98
- def replace_video_audio(video_path, new_audio_path, output_path):
99
- """Replaces the audio track of a video file."""
100
- print("Replacing video audio...")
101
- video_clip = None
102
- audio_clip = None
103
-
104
  try:
105
- video_clip = VideoFileClip(video_path)
106
- audio_clip = AudioFileClip(new_audio_path)
107
- video_clip.audio = audio_clip
108
- video_clip.write_videofile(
 
109
  output_path,
110
  codec="libx264",
111
  audio_codec="aac",
112
- logger='bar'
 
113
  )
 
 
 
 
114
  finally:
115
- if audio_clip:
116
- audio_clip.close()
117
- if video_clip:
118
- video_clip.close()
119
 
120
- # --- 4. FLASK ROUTES ---
121
-
122
- @app.route('/', methods=['GET'])
123
  def index():
124
- """Render the main upload page."""
125
  return render_template('index.html', voices=VOICE_CHOICES)
126
 
127
  @app.route('/process', methods=['POST'])
128
  def process_video():
129
- """Handle video upload and processing."""
130
- input_video_path = None
131
- temp_audio_path = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
  try:
134
- # Validate file upload
135
- if 'video' not in request.files or request.files['video'].filename == '':
136
- flash("Please upload a video file.", "error")
137
- return render_template('index.html', voices=VOICE_CHOICES)
138
 
139
- # Save uploaded file
140
- file = request.files['video']
141
- filename = secure_filename(file.filename)
142
- input_video_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
143
- file.save(input_video_path)
144
-
145
- # Get processing options
146
- voice_choice = request.form.get('voice', 'Charon')
147
- is_cheerful = request.form.get('tone') == 'on'
148
-
149
- # Generate script and audio
150
- script = generate_tamil_script(input_video_path)
151
-
152
- # Create temporary audio file
153
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
154
- temp_audio_path = temp_audio.name
155
 
156
- generate_audio_track(script, voice_choice, is_cheerful, temp_audio_path)
157
 
158
  # Create dubbed video
159
- final_video_name = f"dubbed_{filename}"
160
- final_video_path = os.path.join(app.config['DOWNLOAD_FOLDER'], final_video_name)
161
- replace_video_audio(input_video_path, temp_audio_path, final_video_path)
162
 
163
- flash("Video processing complete!", "success")
164
- return render_template(
165
- 'index.html',
166
- voices=VOICE_CHOICES,
167
- result_video=url_for('serve_video', filename=final_video_name),
168
- script=script
169
- )
170
 
171
- except Exception as e:
172
- print(f"Processing error: {str(e)}")
173
- flash(f"An error occurred: {str(e)}", "error")
174
- return render_template('index.html', voices=VOICE_CHOICES)
 
175
 
176
- finally:
177
- # Clean up temporary files
178
- if input_video_path and os.path.exists(input_video_path):
179
- os.remove(input_video_path)
180
- if temp_audio_path and os.path.exists(temp_audio_path):
181
- os.remove(temp_audio_path)
182
-
183
- @app.route('/downloads/<filename>')
184
- def serve_video(filename):
185
- """Serve the processed video file."""
 
 
186
  return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
187
 
188
- # --- 5. APPLICATION ENTRY POINT ---
189
  if __name__ == '__main__':
190
  app.run(host="0.0.0.0", port=7860)
 
4
  import uuid
5
  import google.generativeai as genai
6
  import requests
7
+ from flask import Flask, request, render_template, send_from_directory, url_for, flash, jsonify
8
  from moviepy.video.io.VideoFileClip import VideoFileClip
9
  from moviepy.audio.io.AudioFileClip import AudioFileClip
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__, template_folder='templates', static_folder='static')
16
 
17
+ # Configuration
18
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
19
  TTS_API_URL = os.getenv("TTS_API_URL")
20
 
21
+ if not GEMINI_API_KEY or not TTS_API_URL:
22
+ raise ValueError("Missing required environment variables")
 
 
 
23
 
 
24
  genai.configure(api_key=GEMINI_API_KEY)
25
 
26
+ # File storage setup
27
  UPLOAD_FOLDER = 'uploads'
28
  DOWNLOAD_FOLDER = 'downloads'
29
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
30
  os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
31
  app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
32
  app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
33
+ app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB
34
+ app.secret_key = os.urandom(24)
35
 
36
+ # Voice options
37
  VOICE_CHOICES = {
38
  "Male (Charon)": "Charon",
39
  "Female (Zephyr)": "Zephyr"
 
51
  Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
52
  """
53
 
54
+ def generate_tamil_script(video_path):
55
+ """Generate Tamil script using Gemini"""
56
+ try:
57
+ print("Uploading video to Gemini...")
58
+ video_file = genai.upload_file(video_path, mime_type="video/mp4")
59
+
60
+ # Wait for processing
61
+ while video_file.state.name == "PROCESSING":
62
+ time.sleep(5)
63
+ video_file = genai.get_file(video_file.name)
64
+
65
+ if video_file.state.name != "ACTIVE":
66
+ raise Exception(f"Gemini processing failed: {video_file.state.name}")
67
+
68
+ print("Generating script...")
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
+ except Exception as e:
77
+ print(f"Script generation failed: {str(e)}")
78
+ raise
79
 
80
+ def generate_audio_track(text, voice, cheerful, output_path):
81
+ """Generate audio using TTS API"""
82
+ try:
83
+ print("Generating audio track...")
84
+ payload = {
85
+ "text": text,
86
+ "voice_name": voice,
87
+ "cheerful": cheerful
88
+ }
89
+
90
+ response = requests.post(TTS_API_URL, json=payload, timeout=300)
91
+ if response.status_code != 200:
92
+ raise Exception(f"TTS API error: {response.status_code}")
93
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  with open(output_path, "wb") as f:
95
  f.write(response.content)
96
+ print("Audio track generated successfully")
97
+ except Exception as e:
98
+ print(f"Audio generation failed: {str(e)}")
99
+ raise
100
+
101
+ def replace_video_audio(video_path, audio_path, output_path):
102
+ """Replace video audio track"""
103
+ video = None
104
+ audio = None
105
  try:
106
+ print("Replacing audio track...")
107
+ video = VideoFileClip(video_path)
108
+ audio = AudioFileClip(audio_path)
109
+ video.audio = audio
110
+ video.write_videofile(
111
  output_path,
112
  codec="libx264",
113
  audio_codec="aac",
114
+ threads=4,
115
+ verbose=False
116
  )
117
+ print("Video processing complete!")
118
+ except Exception as e:
119
+ print(f"Video processing failed: {str(e)}")
120
+ raise
121
  finally:
122
+ if video:
123
+ video.close()
124
+ if audio:
125
+ audio.close()
126
 
127
+ @app.route('/')
 
 
128
  def index():
129
+ """Main page"""
130
  return render_template('index.html', voices=VOICE_CHOICES)
131
 
132
  @app.route('/process', methods=['POST'])
133
  def process_video():
134
+ """Handle video processing"""
135
+ if 'video' not in request.files:
136
+ flash("No video file uploaded", "error")
137
+ return render_template('index.html', voices=VOICE_CHOICES)
138
+
139
+ file = request.files['video']
140
+ if file.filename == '':
141
+ flash("No file selected", "error")
142
+ return render_template('index.html', voices=VOICE_CHOICES)
143
+
144
+ # Save uploaded file
145
+ filename = secure_filename(f"{uuid.uuid4()}_{file.filename}")
146
+ video_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
147
+ file.save(video_path)
148
+
149
+ # Get processing options
150
+ voice = request.form.get('voice', 'Charon')
151
+ cheerful = request.form.get('cheerful') == 'on'
152
 
153
  try:
154
+ # Generate script
155
+ script = generate_tamil_script(video_path)
 
 
156
 
157
+ # Generate audio
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
159
+ audio_path = temp_audio.name
160
 
161
+ generate_audio_track(script, voice, cheerful, audio_path)
162
 
163
  # Create dubbed video
164
+ final_filename = f"dubbed_{filename}"
165
+ final_path = os.path.join(app.config['DOWNLOAD_FOLDER'], final_filename)
166
+ replace_video_audio(video_path, audio_path, final_path)
167
 
168
+ # Cleanup
169
+ os.unlink(audio_path)
170
+ os.unlink(video_path)
 
 
 
 
171
 
172
+ return jsonify({
173
+ 'status': 'success',
174
+ 'video_url': url_for('download', filename=final_filename),
175
+ 'script': script
176
+ })
177
 
178
+ except Exception as e:
179
+ # Cleanup on error
180
+ if os.path.exists(video_path):
181
+ os.unlink(video_path)
182
+ return jsonify({
183
+ 'status': 'error',
184
+ 'message': str(e)
185
+ }), 500
186
+
187
+ @app.route('/download/<filename>')
188
+ def download(filename):
189
+ """Serve processed video"""
190
  return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
191
 
 
192
  if __name__ == '__main__':
193
  app.run(host="0.0.0.0", port=7860)