Artificial-superintelligence
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import streamlit as st
|
2 |
-
from moviepy.editor import VideoFileClip, AudioFileClip, TextClip, CompositeVideoClip
|
3 |
import whisper
|
4 |
from translate import Translator
|
5 |
from gtts import gTTS
|
@@ -7,20 +7,17 @@ import tempfile
|
|
7 |
import os
|
8 |
import numpy as np
|
9 |
from datetime import timedelta
|
10 |
-
import
|
11 |
-
from
|
12 |
-
from indic_transliteration.sanscript import transliterate
|
13 |
-
import azure.cognitiveservices.speech as speechsdk
|
14 |
-
import ffmpeg
|
15 |
|
16 |
# Set page configuration
|
17 |
st.set_page_config(
|
18 |
-
page_title="
|
19 |
page_icon="🎬",
|
20 |
layout="wide"
|
21 |
)
|
22 |
|
23 |
-
# Custom CSS
|
24 |
st.markdown("""
|
25 |
<style>
|
26 |
.stButton>button {
|
@@ -36,7 +33,7 @@ st.markdown("""
|
|
36 |
</style>
|
37 |
""", unsafe_allow_html=True)
|
38 |
|
39 |
-
# Tamil
|
40 |
TAMIL_VOICES = {
|
41 |
'Female 1': {'name': 'ta-IN-PallaviNeural', 'style': 'normal'},
|
42 |
'Female 2': {'name': 'ta-IN-PallaviNeural', 'style': 'formal'},
|
@@ -66,37 +63,36 @@ def load_whisper_model():
|
|
66 |
"""Load Whisper model with caching"""
|
67 |
return whisper.load_model("base")
|
68 |
|
69 |
-
class
|
70 |
def __init__(self):
|
|
|
71 |
self.whisper_model = load_whisper_model()
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
return os.path.join(self.temp_dir, f"temp_{os.urandom(8).hex()}{suffix}")
|
77 |
|
78 |
def cleanup(self):
|
79 |
-
"""Clean up temporary
|
80 |
-
import shutil
|
81 |
try:
|
82 |
shutil.rmtree(self.temp_dir)
|
83 |
except Exception as e:
|
84 |
st.warning(f"Cleanup warning: {e}")
|
85 |
|
86 |
-
def
|
87 |
-
"""
|
88 |
try:
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
97 |
except Exception as e:
|
98 |
-
|
99 |
-
raise
|
100 |
|
101 |
def translate_segments(self, segments):
|
102 |
"""Translate segments to Tamil"""
|
@@ -124,128 +120,173 @@ class TamilDubber:
|
|
124 |
"end": segment["end"],
|
125 |
"duration": segment["end"] - segment["start"]
|
126 |
})
|
127 |
-
|
128 |
return translated_segments
|
129 |
|
130 |
-
def
|
131 |
"""Generate Tamil audio using gTTS"""
|
132 |
try:
|
133 |
-
|
134 |
tts = gTTS(text=text, lang='ta', slow=False)
|
135 |
-
tts.save(
|
136 |
-
return
|
137 |
except Exception as e:
|
138 |
-
|
139 |
-
raise
|
140 |
|
141 |
-
def
|
142 |
-
"""
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
|
155 |
def main():
|
156 |
st.title("Tamil Movie Dubbing System")
|
157 |
st.markdown("""
|
158 |
-
👋 Welcome
|
159 |
- 🎥 Convert English videos to Tamil
|
160 |
- 🗣️ Generate Tamil voiceovers
|
161 |
- 📝 Add Tamil subtitles
|
162 |
""")
|
163 |
|
164 |
-
|
165 |
-
|
166 |
-
# File uploader with clear instructions
|
167 |
-
st.info("Please upload a video file (MP4, MOV, or AVI format)")
|
168 |
video_file = st.file_uploader("Upload Video File", type=['mp4', 'mov', 'avi'])
|
169 |
|
170 |
if not video_file:
|
171 |
-
st.warning("Please upload a video to begin
|
172 |
return
|
173 |
-
|
174 |
-
# Settings
|
175 |
-
|
176 |
-
|
|
|
177 |
voice_type = st.selectbox("Select Voice", list(TAMIL_VOICES.keys()))
|
|
|
|
|
|
|
178 |
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
subtitle_size = st.slider("Subtitle Size", 16, 32, 24)
|
|
|
183 |
subtitle_color = st.color_picker("Subtitle Color", "#FFFFFF")
|
184 |
-
|
185 |
-
#
|
186 |
-
if st.button("Start
|
187 |
try:
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
temp_video_path = dubber.create_temp_file(".mp4")
|
197 |
-
with open(temp_video_path, "wb") as f:
|
198 |
-
f.write(video_file.read())
|
199 |
-
|
200 |
-
# Extract audio and transcribe
|
201 |
-
status_text.text("📥 Extracting audio and transcribing...")
|
202 |
-
segments, video_duration = dubber.extract_audio(temp_video_path)
|
203 |
-
progress_bar.progress(0.25)
|
204 |
-
|
205 |
-
# Translate segments
|
206 |
-
status_text.text("🔄 Translating to Tamil...")
|
207 |
-
translated_segments = dubber.translate_segments(segments)
|
208 |
-
progress_bar.progress(0.50)
|
209 |
-
|
210 |
-
# Generate Tamil audio
|
211 |
-
status_text.text("🔊 Generating Tamil audio...")
|
212 |
-
video = VideoFileClip(temp_video_path)
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
audio_path = dubber.generate_audio(segment["text"])
|
217 |
-
audio_segments.append({
|
218 |
-
"audio": AudioFileClip(audio_path),
|
219 |
-
"start": segment["start"]
|
220 |
-
})
|
221 |
-
progress_bar.progress(0.50 + (0.25 * (idx + 1) / len(translated_segments)))
|
222 |
-
|
223 |
-
# Create final video
|
224 |
-
status_text.text("🎬 Creating final video...")
|
225 |
-
output_path = dubber.create_temp_file(".mp4")
|
226 |
|
227 |
-
#
|
228 |
-
if generate_subtitles:
|
229 |
-
srt_path = dubber.create_temp_file(".srt")
|
230 |
-
dubber.create_subtitles(translated_segments, srt_path)
|
231 |
-
|
232 |
-
# Use ffmpeg to add subtitles
|
233 |
-
stream = ffmpeg.input(temp_video_path)
|
234 |
-
stream = ffmpeg.output(stream, output_path,
|
235 |
-
vf=f'subtitles={srt_path}:force_style=\'FontSize={subtitle_size},PrimaryColour={subtitle_color}\'',
|
236 |
-
acodec='aac')
|
237 |
-
ffmpeg.run(stream, overwrite_output=True)
|
238 |
-
else:
|
239 |
-
# Just copy the video if no subtitles
|
240 |
-
video.write_videofile(output_path)
|
241 |
-
|
242 |
-
progress_bar.progress(1.0)
|
243 |
-
status_text.text("✅ Dubbing completed!")
|
244 |
-
|
245 |
-
# Display result
|
246 |
-
st.success("டப்பிங் வெற்றிகரமாக முடிந்தது!") # Dubbing completed successfully in Tamil
|
247 |
st.video(output_path)
|
248 |
-
|
249 |
# Download button
|
250 |
with open(output_path, "rb") as f:
|
251 |
st.download_button(
|
@@ -254,14 +295,10 @@ def main():
|
|
254 |
file_name="tamil_dubbed_video.mp4",
|
255 |
mime="video/mp4"
|
256 |
)
|
257 |
-
|
258 |
-
finally:
|
259 |
-
# Cleanup
|
260 |
-
dubber.cleanup()
|
261 |
|
262 |
except Exception as e:
|
263 |
-
st.error(f"
|
264 |
-
st.error("Please try
|
265 |
|
266 |
if __name__ == "__main__":
|
267 |
main()
|
|
|
1 |
import streamlit as st
|
2 |
+
from moviepy.editor import VideoFileClip, AudioFileClip, TextClip, CompositeVideoClip, concatenate_audioclips
|
3 |
import whisper
|
4 |
from translate import Translator
|
5 |
from gtts import gTTS
|
|
|
7 |
import os
|
8 |
import numpy as np
|
9 |
from datetime import timedelta
|
10 |
+
import shutil
|
11 |
+
from pathlib import Path
|
|
|
|
|
|
|
12 |
|
13 |
# Set page configuration
|
14 |
st.set_page_config(
|
15 |
+
page_title="Tamil Movie Dubber",
|
16 |
page_icon="🎬",
|
17 |
layout="wide"
|
18 |
)
|
19 |
|
20 |
+
# Custom CSS
|
21 |
st.markdown("""
|
22 |
<style>
|
23 |
.stButton>button {
|
|
|
33 |
</style>
|
34 |
""", unsafe_allow_html=True)
|
35 |
|
36 |
+
# Tamil voice configurations
|
37 |
TAMIL_VOICES = {
|
38 |
'Female 1': {'name': 'ta-IN-PallaviNeural', 'style': 'normal'},
|
39 |
'Female 2': {'name': 'ta-IN-PallaviNeural', 'style': 'formal'},
|
|
|
63 |
"""Load Whisper model with caching"""
|
64 |
return whisper.load_model("base")
|
65 |
|
66 |
+
class VideoProcessor:
|
67 |
def __init__(self):
|
68 |
+
self.temp_dir = Path(tempfile.mkdtemp())
|
69 |
self.whisper_model = load_whisper_model()
|
70 |
+
|
71 |
+
def create_temp_path(self, suffix):
|
72 |
+
"""Create a temporary file path"""
|
73 |
+
return str(self.temp_dir / f"temp_{os.urandom(4).hex()}{suffix}")
|
|
|
74 |
|
75 |
def cleanup(self):
|
76 |
+
"""Clean up temporary directory"""
|
|
|
77 |
try:
|
78 |
shutil.rmtree(self.temp_dir)
|
79 |
except Exception as e:
|
80 |
st.warning(f"Cleanup warning: {e}")
|
81 |
|
82 |
+
def transcribe_video(self, video_path):
|
83 |
+
"""Transcribe video audio using Whisper"""
|
84 |
try:
|
85 |
+
with VideoFileClip(video_path) as video:
|
86 |
+
# Extract audio to temporary file
|
87 |
+
audio_path = self.create_temp_path(".wav")
|
88 |
+
video.audio.write_audiofile(audio_path, fps=16000, verbose=False, logger=None)
|
89 |
+
|
90 |
+
# Transcribe using Whisper
|
91 |
+
result = self.whisper_model.transcribe(audio_path)
|
92 |
+
return result["segments"], video.duration
|
93 |
+
|
94 |
except Exception as e:
|
95 |
+
raise Exception(f"Transcription error: {str(e)}")
|
|
|
96 |
|
97 |
def translate_segments(self, segments):
|
98 |
"""Translate segments to Tamil"""
|
|
|
120 |
"end": segment["end"],
|
121 |
"duration": segment["end"] - segment["start"]
|
122 |
})
|
123 |
+
|
124 |
return translated_segments
|
125 |
|
126 |
+
def generate_tamil_audio(self, text):
|
127 |
"""Generate Tamil audio using gTTS"""
|
128 |
try:
|
129 |
+
audio_path = self.create_temp_path(".mp3")
|
130 |
tts = gTTS(text=text, lang='ta', slow=False)
|
131 |
+
tts.save(audio_path)
|
132 |
+
return audio_path
|
133 |
except Exception as e:
|
134 |
+
raise Exception(f"Audio generation error: {str(e)}")
|
|
|
135 |
|
136 |
+
def create_subtitle_clip(self, txt, fontsize, color, size):
|
137 |
+
"""Create a subtitle clip"""
|
138 |
+
return TextClip(
|
139 |
+
txt=txt,
|
140 |
+
fontsize=fontsize,
|
141 |
+
color=color,
|
142 |
+
bg_color='rgba(0,0,0,0.5)',
|
143 |
+
size=size,
|
144 |
+
method='caption'
|
145 |
+
)
|
146 |
+
|
147 |
+
def process_video(video_data, voice_type, generate_subtitles=True, subtitle_size=24, subtitle_color='white'):
|
148 |
+
"""Main video processing function"""
|
149 |
+
processor = VideoProcessor()
|
150 |
+
|
151 |
+
try:
|
152 |
+
# Save uploaded video to temporary file
|
153 |
+
input_path = processor.create_temp_path(".mp4")
|
154 |
+
with open(input_path, "wb") as f:
|
155 |
+
f.write(video_data)
|
156 |
+
|
157 |
+
# Load video
|
158 |
+
video = VideoFileClip(input_path)
|
159 |
+
|
160 |
+
# Create progress tracking
|
161 |
+
progress_text = st.empty()
|
162 |
+
progress_bar = st.progress(0)
|
163 |
+
|
164 |
+
# Step 1: Transcribe
|
165 |
+
progress_text.text("Transcribing video...")
|
166 |
+
segments, duration = processor.transcribe_video(input_path)
|
167 |
+
progress_bar.progress(0.25)
|
168 |
+
|
169 |
+
# Step 2: Translate
|
170 |
+
progress_text.text("Translating to Tamil...")
|
171 |
+
translated_segments = processor.translate_segments(segments)
|
172 |
+
progress_bar.progress(0.50)
|
173 |
+
|
174 |
+
# Step 3: Generate audio
|
175 |
+
progress_text.text("Generating Tamil audio...")
|
176 |
+
subtitle_clips = []
|
177 |
+
audio_clips = []
|
178 |
+
|
179 |
+
for i, segment in enumerate(translated_segments):
|
180 |
+
# Generate audio
|
181 |
+
audio_path = processor.generate_tamil_audio(segment["text"])
|
182 |
+
audio_clip = AudioFileClip(audio_path)
|
183 |
+
audio_clips.append(audio_clip.set_start(segment["start"]))
|
184 |
+
|
185 |
+
# Create subtitle if enabled
|
186 |
+
if generate_subtitles:
|
187 |
+
subtitle_clip = processor.create_subtitle_clip(
|
188 |
+
segment["text"],
|
189 |
+
subtitle_size,
|
190 |
+
subtitle_color,
|
191 |
+
(video.w, None)
|
192 |
+
)
|
193 |
+
subtitle_clip = (subtitle_clip
|
194 |
+
.set_position(('center', 'bottom'))
|
195 |
+
.set_start(segment["start"])
|
196 |
+
.set_duration(segment["duration"]))
|
197 |
+
subtitle_clips.append(subtitle_clip)
|
198 |
+
|
199 |
+
progress_bar.progress(0.50 + (0.4 * (i + 1) / len(translated_segments)))
|
200 |
+
|
201 |
+
# Step 4: Combine everything
|
202 |
+
progress_text.text("Creating final video...")
|
203 |
+
|
204 |
+
# Combine audio clips
|
205 |
+
final_audio = CompositeVideoClip([*audio_clips])
|
206 |
+
|
207 |
+
# Create final video
|
208 |
+
if generate_subtitles:
|
209 |
+
final_video = CompositeVideoClip([video, *subtitle_clips])
|
210 |
+
else:
|
211 |
+
final_video = video
|
212 |
+
|
213 |
+
# Set audio
|
214 |
+
final_video = final_video.set_audio(final_audio)
|
215 |
+
|
216 |
+
# Write final video
|
217 |
+
output_path = processor.create_temp_path(".mp4")
|
218 |
+
final_video.write_videofile(
|
219 |
+
output_path,
|
220 |
+
codec='libx264',
|
221 |
+
audio_codec='aac',
|
222 |
+
temp_audiofile=processor.create_temp_path(".m4a"),
|
223 |
+
remove_temp=True,
|
224 |
+
verbose=False,
|
225 |
+
logger=None
|
226 |
+
)
|
227 |
+
|
228 |
+
progress_bar.progress(1.0)
|
229 |
+
progress_text.text("Processing complete!")
|
230 |
+
|
231 |
+
return output_path
|
232 |
+
|
233 |
+
except Exception as e:
|
234 |
+
raise Exception(f"Video processing error: {str(e)}")
|
235 |
+
|
236 |
+
finally:
|
237 |
+
# Cleanup
|
238 |
+
processor.cleanup()
|
239 |
|
240 |
def main():
|
241 |
st.title("Tamil Movie Dubbing System")
|
242 |
st.markdown("""
|
243 |
+
👋 Welcome! This tool helps you:
|
244 |
- 🎥 Convert English videos to Tamil
|
245 |
- 🗣️ Generate Tamil voiceovers
|
246 |
- 📝 Add Tamil subtitles
|
247 |
""")
|
248 |
|
249 |
+
# File uploader
|
|
|
|
|
|
|
250 |
video_file = st.file_uploader("Upload Video File", type=['mp4', 'mov', 'avi'])
|
251 |
|
252 |
if not video_file:
|
253 |
+
st.warning("Please upload a video to begin.")
|
254 |
return
|
255 |
+
|
256 |
+
# Settings
|
257 |
+
col1, col2 = st.columns(2)
|
258 |
+
|
259 |
+
with col1:
|
260 |
voice_type = st.selectbox("Select Voice", list(TAMIL_VOICES.keys()))
|
261 |
+
|
262 |
+
with col2:
|
263 |
+
generate_subtitles = st.checkbox("Generate Subtitles", value=True)
|
264 |
|
265 |
+
if generate_subtitles:
|
266 |
+
col3, col4 = st.columns(2)
|
267 |
+
with col3:
|
268 |
subtitle_size = st.slider("Subtitle Size", 16, 32, 24)
|
269 |
+
with col4:
|
270 |
subtitle_color = st.color_picker("Subtitle Color", "#FFFFFF")
|
271 |
+
|
272 |
+
# Process video
|
273 |
+
if st.button("Start Dubbing"):
|
274 |
try:
|
275 |
+
with st.spinner("Processing video..."):
|
276 |
+
output_path = process_video(
|
277 |
+
video_file.read(),
|
278 |
+
voice_type,
|
279 |
+
generate_subtitles,
|
280 |
+
subtitle_size if generate_subtitles else 24,
|
281 |
+
subtitle_color if generate_subtitles else 'white'
|
282 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
283 |
|
284 |
+
# Show success message
|
285 |
+
st.success("டப்பிங் வெற்றிகரமாக முடிந்தது!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
286 |
|
287 |
+
# Display video
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
st.video(output_path)
|
289 |
+
|
290 |
# Download button
|
291 |
with open(output_path, "rb") as f:
|
292 |
st.download_button(
|
|
|
295 |
file_name="tamil_dubbed_video.mp4",
|
296 |
mime="video/mp4"
|
297 |
)
|
|
|
|
|
|
|
|
|
298 |
|
299 |
except Exception as e:
|
300 |
+
st.error(f"Processing failed: {str(e)}")
|
301 |
+
st.error("Please try uploading a different video or check if the format is supported.")
|
302 |
|
303 |
if __name__ == "__main__":
|
304 |
main()
|