Athspi commited on
Commit
1dc264f
·
verified ·
1 Parent(s): b87a68d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -29
app.py CHANGED
@@ -4,19 +4,18 @@ import tempfile
4
  import google.generativeai as genai
5
  import requests
6
  from flask import Flask, request, render_template, send_from_directory, url_for, flash
7
- from moviepy.video.io.VideoFileClip import VideoFileClip
8
- from moviepy.audio.io.AudioFileClip import AudioFileClip
9
  from werkzeug.utils import secure_filename
10
 
11
- # --- 1. INITIALIZATION & CONFIGURATION ---
12
 
13
  app = Flask(__name__)
14
 
15
- # Load secrets from environment variables
16
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
17
  TTS_API_URL = os.getenv("TTS_API_URL")
18
 
19
- # Check if secrets were loaded correctly and provide clear error messages
20
  if not GEMINI_API_KEY:
21
  raise ValueError("SECURITY ERROR: GEMINI_API_KEY secret not found! Please set it as an environment variable.")
22
  if not TTS_API_URL:
@@ -25,7 +24,7 @@ if not TTS_API_URL:
25
  # Configure the Gemini API
26
  genai.configure(api_key=GEMINI_API_KEY)
27
 
28
- # Configure directories for temporary file storage
29
  UPLOAD_FOLDER = 'uploads'
30
  DOWNLOAD_FOLDER = 'downloads'
31
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
@@ -33,7 +32,7 @@ os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
33
  app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
34
  app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
35
  app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100 MB upload limit
36
- app.secret_key = 'supersecretkey' # Required for flash messages
37
 
38
  # --- 2. VOICE CHOICES & GEMINI PROMPT ---
39
 
@@ -51,7 +50,7 @@ You are an AI scriptwriter. Your task is to watch the provided video and transcr
51
  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.
52
 
53
  **EXAMPLE OUTPUT:**
54
- Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும். Shout angrily: உடனே இங்கிருந்து போ!
55
  """
56
 
57
  # --- 3. CORE LOGIC HELPER FUNCTIONS ---
@@ -59,7 +58,7 @@ Say happily: வணக்கம்! [laugh] எப்படி இருக்
59
  def generate_tamil_script(video_file_path):
60
  """Generates a single, continuous Tamil script from the video using Gemini."""
61
  print("Uploading file to Gemini for transcription...")
62
- video_file = genai.upload_file(video_file_path)
63
 
64
  print("Waiting for Gemini file processing...")
65
  while video_file.state.name == "PROCESSING":
@@ -69,7 +68,7 @@ def generate_tamil_script(video_file_path):
69
  raise Exception(f"Gemini file processing failed: {video_file.state.name}")
70
 
71
  print("Generating narrator script...")
72
- model = genai.GenerativeModel(model_name="models/gemini-2.5-flash")
73
  response = model.generate_content([GEMINI_PROMPT, video_file])
74
  genai.delete_file(video_file.name)
75
 
@@ -81,7 +80,7 @@ def generate_single_audio_track(dialogue_text, voice_name, is_cheerful, output_p
81
  """Generates one audio track for the entire script via TTS API."""
82
  print(f"Requesting audio from TTS API (Voice: {voice_name}, Cheerful: {is_cheerful})")
83
  payload = {"text": dialogue_text, "voice_name": voice_name, "cheerful": is_cheerful}
84
- response = requests.post(TTS_API_URL, json=payload, timeout=300) # 5-minute timeout
85
  if response.status_code == 200:
86
  with open(output_path, "wb") as f:
87
  f.write(response.content)
@@ -92,34 +91,33 @@ def generate_single_audio_track(dialogue_text, voice_name, is_cheerful, output_p
92
  def replace_video_audio(video_path, new_audio_path, output_path):
93
  """
94
  Replaces the audio of a video with a new audio file.
95
- This function is robustly designed to ensure file handles are always closed.
96
  """
97
  print("Replacing video audio using MoviePy...")
98
  video_clip = None
99
  audio_clip = None
100
- final_clip = None
101
  try:
102
  video_clip = VideoFileClip(video_path)
103
  audio_clip = AudioFileClip(new_audio_path)
104
 
105
- # The standard and correct way to set audio in modern moviepy
106
- final_clip = video_clip.set_audio(audio_clip)
 
 
107
 
108
- # Write the final video file with a progress bar in the console
109
- final_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", logger='bar')
110
  print(f"Successfully created final video at {output_path}")
111
 
112
  except Exception as e:
113
  print(f"FATAL ERROR in replace_video_audio: {e}")
114
- raise # Re-raise the exception to be caught by the Flask route
115
  finally:
116
- # This block ensures that all files are closed, preventing file lock issues.
117
  if video_clip:
118
  video_clip.close()
119
  if audio_clip:
120
  audio_clip.close()
121
- if final_clip:
122
- final_clip.close()
123
 
124
  # --- 4. FLASK WEB ROUTES ---
125
 
@@ -131,15 +129,12 @@ def index():
131
  @app.route('/process', methods=['POST'])
132
  def process_video():
133
  """Handles video upload, processing, and renders the result."""
134
- if 'video' not in request.files:
135
  flash("No video file selected. Please choose a file to upload.")
136
  return render_template('index.html')
137
 
138
  file = request.files['video']
139
- if file.filename == '':
140
- flash("No video file selected. Please choose a file to upload.")
141
- return render_template('index.html')
142
-
143
  try:
144
  filename = secure_filename(file.filename)
145
  upload_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
@@ -149,7 +144,6 @@ def process_video():
149
  is_cheerful = 'cheerful' in request.form
150
  voice_name = VOICE_CHOICES[voice_choice]
151
 
152
- # Core Logic Execution
153
  script = generate_tamil_script(upload_path)
154
 
155
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
@@ -162,8 +156,8 @@ def process_video():
162
 
163
  replace_video_audio(upload_path, temp_audio_path, final_video_path)
164
 
165
- # Cleanup temporary audio file
166
  os.remove(temp_audio_path)
 
167
 
168
  return render_template('index.html',
169
  result_video=url_for('serve_video', filename=final_video_name),
@@ -171,7 +165,7 @@ def process_video():
171
 
172
  except Exception as e:
173
  print(f"An error occurred during processing: {e}")
174
- flash(f"An error occurred: {e}")
175
  return render_template('index.html')
176
 
177
  @app.route('/downloads/<filename>')
 
4
  import google.generativeai as genai
5
  import requests
6
  from flask import Flask, request, render_template, send_from_directory, url_for, flash
7
+ from moviepy.editor import VideoFileClip, AudioFileClip
 
8
  from werkzeug.utils import secure_filename
9
 
10
+ # --- 1. INITIALIZE FLASK APP AND LOAD SECRETS ---
11
 
12
  app = Flask(__name__)
13
 
14
+ # Load secrets from environment variables (the secure way)
15
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
16
  TTS_API_URL = os.getenv("TTS_API_URL")
17
 
18
+ # Check if secrets were loaded correctly
19
  if not GEMINI_API_KEY:
20
  raise ValueError("SECURITY ERROR: GEMINI_API_KEY secret not found! Please set it as an environment variable.")
21
  if not TTS_API_URL:
 
24
  # Configure the Gemini API
25
  genai.configure(api_key=GEMINI_API_KEY)
26
 
27
+ # Configure directories and app settings
28
  UPLOAD_FOLDER = 'uploads'
29
  DOWNLOAD_FOLDER = 'downloads'
30
  os.makedirs(UPLOAD_FOLDER, exist_ok=True)
 
32
  app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
33
  app.config['DOWNLOAD_FOLDER'] = DOWNLOAD_FOLDER
34
  app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100 MB upload limit
35
+ app.secret_key = os.urandom(24) # Secure key for flash messages
36
 
37
  # --- 2. VOICE CHOICES & GEMINI PROMPT ---
38
 
 
50
  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.
51
 
52
  **EXAMPLE OUTPUT:**
53
+ Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
54
  """
55
 
56
  # --- 3. CORE LOGIC HELPER FUNCTIONS ---
 
58
  def generate_tamil_script(video_file_path):
59
  """Generates a single, continuous Tamil script from the video using Gemini."""
60
  print("Uploading file to Gemini for transcription...")
61
+ video_file = genai.upload_file(video_file_path, mime_type="video/mp4")
62
 
63
  print("Waiting for Gemini file processing...")
64
  while video_file.state.name == "PROCESSING":
 
68
  raise Exception(f"Gemini file processing failed: {video_file.state.name}")
69
 
70
  print("Generating narrator script...")
71
+ model = genai.GenerativeModel(model_name="models/gemini-1.5-pro-latest")
72
  response = model.generate_content([GEMINI_PROMPT, video_file])
73
  genai.delete_file(video_file.name)
74
 
 
80
  """Generates one audio track for the entire script via TTS API."""
81
  print(f"Requesting audio from TTS API (Voice: {voice_name}, Cheerful: {is_cheerful})")
82
  payload = {"text": dialogue_text, "voice_name": voice_name, "cheerful": is_cheerful}
83
+ response = requests.post(TTS_API_URL, json=payload, timeout=300)
84
  if response.status_code == 200:
85
  with open(output_path, "wb") as f:
86
  f.write(response.content)
 
91
  def replace_video_audio(video_path, new_audio_path, output_path):
92
  """
93
  Replaces the audio of a video with a new audio file.
94
+ This function now uses the modern, correct method for MoviePy.
95
  """
96
  print("Replacing video audio using MoviePy...")
97
  video_clip = None
98
  audio_clip = None
 
99
  try:
100
  video_clip = VideoFileClip(video_path)
101
  audio_clip = AudioFileClip(new_audio_path)
102
 
103
+ # --- THE CORRECTED CODE ---
104
+ # In modern MoviePy, you assign the audio clip directly to the .audio attribute.
105
+ # The .set_audio() method is deprecated and causes the error.
106
+ video_clip.audio = audio_clip
107
 
108
+ # Write the modified video_clip object to a file.
109
+ video_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", logger='bar')
110
  print(f"Successfully created final video at {output_path}")
111
 
112
  except Exception as e:
113
  print(f"FATAL ERROR in replace_video_audio: {e}")
114
+ raise
115
  finally:
116
+ # This block ensures that all file handles are closed, preventing file lock issues.
117
  if video_clip:
118
  video_clip.close()
119
  if audio_clip:
120
  audio_clip.close()
 
 
121
 
122
  # --- 4. FLASK WEB ROUTES ---
123
 
 
129
  @app.route('/process', methods=['POST'])
130
  def process_video():
131
  """Handles video upload, processing, and renders the result."""
132
+ if 'video' not in request.files or request.files['video'].filename == '':
133
  flash("No video file selected. Please choose a file to upload.")
134
  return render_template('index.html')
135
 
136
  file = request.files['video']
137
+
 
 
 
138
  try:
139
  filename = secure_filename(file.filename)
140
  upload_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
 
144
  is_cheerful = 'cheerful' in request.form
145
  voice_name = VOICE_CHOICES[voice_choice]
146
 
 
147
  script = generate_tamil_script(upload_path)
148
 
149
  with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
 
156
 
157
  replace_video_audio(upload_path, temp_audio_path, final_video_path)
158
 
 
159
  os.remove(temp_audio_path)
160
+ os.remove(upload_path) # Clean up original upload
161
 
162
  return render_template('index.html',
163
  result_video=url_for('serve_video', filename=final_video_name),
 
165
 
166
  except Exception as e:
167
  print(f"An error occurred during processing: {e}")
168
+ flash(f"An unexpected error occurred: {e}. Please check the console logs and try again.")
169
  return render_template('index.html')
170
 
171
  @app.route('/downloads/<filename>')