Athspi commited on
Commit
a30e87b
·
verified ·
1 Parent(s): 58a23cd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -68
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import os
2
  import time
3
  import tempfile
@@ -10,144 +11,165 @@ 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 the loaded environment variables
18
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
19
  TTS_API_URL = os.getenv("TTS_API_URL")
20
 
21
- # Check if secrets were loaded correctly
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 the Gemini API
28
  genai.configure(api_key=GEMINI_API_KEY)
29
 
30
- # Configure directories and app settings
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. VOICE CHOICES & GEMINI PROMPT ---
41
- VOICE_CHOICES = {"Male (Charon)": "Charon", "Female (Zephyr)": "Zephyr"}
42
  GEMINI_PROMPT = """
43
  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.
44
 
45
  **CRITICAL INSTRUCTIONS:**
46
- 1. **Single Script:** Combine all dialogue from all speakers into one continuous script.
47
- 2. **NO Timestamps or Speaker Labels:** Do NOT include any timestamps or speaker identifiers.
48
- 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.
49
 
50
  **EXAMPLE OUTPUT:**
51
  Say happily: வணக்கம்! [laugh] எப்படி இருக்கீங்க? Whisper mysteriously: அந்த ரகசியம் எனக்கு மட்டும் தான் தெரியும்.
52
  """
53
 
54
- # --- 3. CORE LOGIC HELPER FUNCTIONS ---
55
-
56
- def generate_tamil_script(video_file_path):
57
- """Generates a single, continuous Tamil script from the video using Gemini."""
58
- print("Uploading file to Gemini for transcription...")
59
- video_file = genai.upload_file(video_file_path, mime_type="video/mp4")
60
- print("Waiting for Gemini file processing...")
61
  while video_file.state.name == "PROCESSING":
62
  time.sleep(5)
63
  video_file = genai.get_file(video_file.name)
 
64
  if video_file.state.name != "ACTIVE":
65
- raise Exception(f"Gemini file processing failed: {video_file.state.name}")
66
- print("Generating narrator script...")
67
  model = genai.GenerativeModel(model_name="models/gemini-2.5-flash")
68
  response = model.generate_content([GEMINI_PROMPT, video_file])
69
  genai.delete_file(video_file.name)
 
70
  if hasattr(response, 'text') and response.text:
71
  return " ".join(response.text.strip().splitlines())
72
- raise Exception("No valid script was generated by Gemini.")
73
-
74
- def generate_single_audio_track(dialogue_text, voice_name, is_cheerful, output_path):
75
- """Generates one audio track for the entire script via TTS API."""
76
- print(f"Requesting audio from TTS API (Voice: {voice_name}, Cheerful: {is_cheerful})")
77
- payload = {"text": dialogue_text, "voice_name": voice_name, "cheerful": is_cheerful}
 
 
 
 
 
78
  response = requests.post(TTS_API_URL, json=payload, timeout=300)
79
  if response.status_code == 200:
80
  with open(output_path, "wb") as f:
81
  f.write(response.content)
82
  return True
83
- raise Exception(f"Error from TTS API: {response.status_code} - {response.text}")
84
 
85
- def replace_video_audio(video_path, new_audio_path, output_path):
86
- """Replaces the audio of a video using the modern, correct MoviePy method."""
87
- print("Replacing video audio using MoviePy...")
88
- video_clip, audio_clip = None, None
 
89
  try:
90
  video_clip = VideoFileClip(video_path)
91
- audio_clip = AudioFileClip(new_audio_path)
92
  video_clip.audio = audio_clip
93
- video_clip.write_videofile(output_path, codec="libx264", audio_codec="aac", logger='bar')
 
 
 
 
 
94
  finally:
95
  if audio_clip: audio_clip.close()
96
  if video_clip: video_clip.close()
97
 
98
- # --- 4. FLASK WEB ROUTES ---
99
-
100
  @app.route('/', methods=['GET'])
101
  def index():
102
- """Renders the main upload page."""
103
  return render_template('index.html')
104
 
105
  @app.route('/process', methods=['POST'])
106
  def process_video():
107
- """Handles video upload, processing, and renders the result."""
108
- input_video_path, temp_audio_path = None, None
109
  try:
110
- if 'video' not in request.files or request.files['video'].filename == '':
111
- flash("Please upload a video file.")
 
112
  return render_template('index.html')
113
-
114
  file = request.files['video']
 
 
 
 
 
115
  filename = secure_filename(file.filename)
116
- input_video_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
117
- file.save(input_video_path)
118
-
119
- voice_choice = request.form['voice_choice']
120
- is_cheerful = 'cheerful' in request.form
121
- voice_name = VOICE_CHOICES[voice_choice]
122
- script = generate_tamil_script(input_video_path)
 
 
 
 
 
123
 
124
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio:
125
- temp_audio_path = temp_audio.name
126
 
127
- generate_single_audio_track(script, voice_name, is_cheerful, temp_audio_path)
128
- final_video_name = f"dubbed_{filename}"
129
- final_video_path = os.path.join(app.config['DOWNLOAD_FOLDER'], final_video_name)
130
- replace_video_audio(input_video_path, temp_audio_path, final_video_path)
131
 
132
  flash("Video processing complete!", "success")
133
- return render_template('index.html',
134
- result_video=url_for('serve_video', filename=final_video_name),
135
- script=script)
 
136
  except Exception as e:
137
- print(f"An error occurred during processing: {e}")
138
- flash(f"An unexpected error occurred: {e}. Please check the console and try again.", "error")
139
  return render_template('index.html')
 
140
  finally:
141
- if input_video_path and os.path.exists(input_video_path):
142
- os.remove(input_video_path)
143
- if temp_audio_path and os.path.exists(temp_audio_path):
144
- os.remove(temp_audio_path)
 
145
 
146
  @app.route('/downloads/<filename>')
147
  def serve_video(filename):
148
- """Serves the final dubbed video."""
149
  return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename)
150
 
151
- # --- 5. APPLICATION ENTRY POINT ---
152
  if __name__ == '__main__':
153
  app.run(host="0.0.0.0", port=7860)
 
1
+ # app.py
2
  import os
3
  import time
4
  import tempfile
 
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)