Update app.py
Browse files
app.py
CHANGED
@@ -12,27 +12,29 @@ from indic_transliteration import sanscript
|
|
12 |
from indic_transliteration.sanscript import transliterate
|
13 |
import azure.cognitiveservices.speech as speechsdk
|
14 |
import ffmpeg
|
15 |
-
from PIL import Image
|
16 |
-
import imageio
|
17 |
|
18 |
-
#
|
19 |
-
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
#
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
|
37 |
# Tamil-specific voice configurations
|
38 |
TAMIL_VOICES = {
|
@@ -59,42 +61,39 @@ class TamilTextProcessor:
|
|
59 |
text = ' '.join(text.split())
|
60 |
return text
|
61 |
|
|
|
|
|
|
|
|
|
|
|
62 |
class TamilDubber:
|
63 |
def __init__(self):
|
64 |
-
|
65 |
-
|
66 |
-
except Exception as e:
|
67 |
-
st.error(f"Error loading Whisper model: {e}")
|
68 |
-
raise
|
69 |
-
self.temp_files = []
|
70 |
-
|
71 |
-
def __enter__(self):
|
72 |
-
return self
|
73 |
|
74 |
-
def
|
75 |
-
|
|
|
76 |
|
77 |
def cleanup(self):
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
def create_temp_file(self, suffix):
|
86 |
-
temp_file = tempfile.mktemp(suffix=suffix)
|
87 |
-
self.temp_files.append(temp_file)
|
88 |
-
return temp_file
|
89 |
|
90 |
def extract_audio(self, video_path):
|
91 |
"""Extract audio and transcribe using Whisper"""
|
92 |
try:
|
93 |
video = VideoFileClip(video_path)
|
94 |
audio_path = self.create_temp_file(".wav")
|
95 |
-
video.audio.write_audiofile(audio_path)
|
|
|
|
|
96 |
result = self.whisper_model.transcribe(audio_path)
|
97 |
return result["segments"], video.duration
|
|
|
98 |
except Exception as e:
|
99 |
st.error(f"Error in audio extraction: {e}")
|
100 |
raise
|
@@ -118,6 +117,7 @@ class TamilDubber:
|
|
118 |
})
|
119 |
except Exception as e:
|
120 |
st.warning(f"Translation warning for segment: {str(e)}")
|
|
|
121 |
translated_segments.append({
|
122 |
"text": segment["text"],
|
123 |
"start": segment["start"],
|
@@ -152,131 +152,116 @@ class TamilDubber:
|
|
152 |
st.error(f"Error creating subtitles: {e}")
|
153 |
raise
|
154 |
|
155 |
-
def create_subtitle_clip(self, txt, size, color):
|
156 |
-
"""Create subtitle clip with proper configuration"""
|
157 |
-
try:
|
158 |
-
return TextClip(
|
159 |
-
txt=txt,
|
160 |
-
font='DejaVu-Sans', # Use a system font that supports Tamil
|
161 |
-
fontsize=size,
|
162 |
-
color=color,
|
163 |
-
stroke_color='black',
|
164 |
-
stroke_width=1,
|
165 |
-
method='caption', # Use caption method instead of label
|
166 |
-
size=(720, None) # Set width, let height adjust automatically
|
167 |
-
)
|
168 |
-
except Exception as e:
|
169 |
-
st.error(f"Error creating subtitle clip: {e}")
|
170 |
-
raise
|
171 |
-
|
172 |
def main():
|
173 |
-
# Configure ImageMagick at startup
|
174 |
-
configure_imagemagick()
|
175 |
-
|
176 |
st.title("Tamil Movie Dubbing System")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
st.sidebar.header("டப்பிங் அமைப்புகள்") # Dubbing Settings in Tamil
|
178 |
|
179 |
-
# File uploader
|
|
|
180 |
video_file = st.file_uploader("Upload Video File", type=['mp4', 'mov', 'avi'])
|
|
|
181 |
if not video_file:
|
|
|
182 |
return
|
183 |
|
184 |
-
# Settings
|
185 |
-
|
186 |
-
|
187 |
-
|
|
|
|
|
188 |
generate_subtitles = st.checkbox("Generate Tamil Subtitles", value=True)
|
189 |
-
|
190 |
-
|
|
|
191 |
|
|
|
192 |
if st.button("Start Tamil Dubbing"):
|
193 |
try:
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
|
|
|
|
|
|
|
|
|
|
200 |
|
201 |
-
|
202 |
-
|
203 |
-
|
|
|
204 |
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
|
|
|
|
|
|
|
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
|
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
output_path = dubber.create_temp_file(".mp4")
|
232 |
-
|
233 |
-
# Add subtitles if enabled
|
234 |
-
if generate_subtitles:
|
235 |
-
subtitle_clips = []
|
236 |
-
for segment in translated_segments:
|
237 |
-
try:
|
238 |
-
clip = dubber.create_subtitle_clip(
|
239 |
-
segment["text"],
|
240 |
-
subtitle_size,
|
241 |
-
subtitle_color
|
242 |
-
)
|
243 |
-
clip = clip.set_position(('center', 'bottom'))
|
244 |
-
clip = clip.set_start(segment["start"])
|
245 |
-
clip = clip.set_duration(segment["duration"])
|
246 |
-
subtitle_clips.append(clip)
|
247 |
-
except Exception as e:
|
248 |
-
st.warning(f"Skipping subtitle for segment due to error: {e}")
|
249 |
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
preset='medium'
|
262 |
)
|
263 |
-
progress_bar.progress(1.0)
|
264 |
-
|
265 |
-
# Display result
|
266 |
-
st.success("டப்பிங் வெற்றிகரமாக முடிந்தது!") # Dubbing completed successfully in Tamil
|
267 |
-
st.video(output_path)
|
268 |
-
|
269 |
-
# Download button
|
270 |
-
with open(output_path, "rb") as f:
|
271 |
-
st.download_button(
|
272 |
-
"Download Dubbed Video",
|
273 |
-
f,
|
274 |
-
file_name="tamil_dubbed_video.mp4",
|
275 |
-
mime="video/mp4"
|
276 |
-
)
|
277 |
|
|
|
|
|
|
|
|
|
278 |
except Exception as e:
|
279 |
st.error(f"An error occurred: {str(e)}")
|
|
|
280 |
|
281 |
if __name__ == "__main__":
|
282 |
main()
|
|
|
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="translate",
|
19 |
+
page_icon="🎬",
|
20 |
+
layout="wide"
|
21 |
+
)
|
22 |
|
23 |
+
# Custom CSS to improve the interface
|
24 |
+
st.markdown("""
|
25 |
+
<style>
|
26 |
+
.stButton>button {
|
27 |
+
width: 100%;
|
28 |
+
border-radius: 5px;
|
29 |
+
height: 3em;
|
30 |
+
background-color: #FF4B4B;
|
31 |
+
color: white;
|
32 |
+
}
|
33 |
+
.stProgress .st-bo {
|
34 |
+
background-color: #FF4B4B;
|
35 |
+
}
|
36 |
+
</style>
|
37 |
+
""", unsafe_allow_html=True)
|
38 |
|
39 |
# Tamil-specific voice configurations
|
40 |
TAMIL_VOICES = {
|
|
|
61 |
text = ' '.join(text.split())
|
62 |
return text
|
63 |
|
64 |
+
@st.cache_resource
|
65 |
+
def load_whisper_model():
|
66 |
+
"""Load Whisper model with caching"""
|
67 |
+
return whisper.load_model("base")
|
68 |
+
|
69 |
class TamilDubber:
|
70 |
def __init__(self):
|
71 |
+
self.whisper_model = load_whisper_model()
|
72 |
+
self.temp_dir = tempfile.mkdtemp()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
def create_temp_file(self, suffix):
|
75 |
+
"""Create a temporary file in the temp directory"""
|
76 |
+
return os.path.join(self.temp_dir, f"temp_{os.urandom(8).hex()}{suffix}")
|
77 |
|
78 |
def cleanup(self):
|
79 |
+
"""Clean up temporary files"""
|
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 extract_audio(self, video_path):
|
87 |
"""Extract audio and transcribe using Whisper"""
|
88 |
try:
|
89 |
video = VideoFileClip(video_path)
|
90 |
audio_path = self.create_temp_file(".wav")
|
91 |
+
video.audio.write_audiofile(audio_path, fps=16000)
|
92 |
+
|
93 |
+
# Transcribe using Whisper
|
94 |
result = self.whisper_model.transcribe(audio_path)
|
95 |
return result["segments"], video.duration
|
96 |
+
|
97 |
except Exception as e:
|
98 |
st.error(f"Error in audio extraction: {e}")
|
99 |
raise
|
|
|
117 |
})
|
118 |
except Exception as e:
|
119 |
st.warning(f"Translation warning for segment: {str(e)}")
|
120 |
+
# Keep original text if translation fails
|
121 |
translated_segments.append({
|
122 |
"text": segment["text"],
|
123 |
"start": segment["start"],
|
|
|
152 |
st.error(f"Error creating subtitles: {e}")
|
153 |
raise
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
def main():
|
|
|
|
|
|
|
156 |
st.title("Tamil Movie Dubbing System")
|
157 |
+
st.markdown("""
|
158 |
+
👋 Welcome to the Tamil Movie Dubbing System! This tool helps you:
|
159 |
+
- 🎥 Convert English videos to Tamil
|
160 |
+
- 🗣️ Generate Tamil voiceovers
|
161 |
+
- 📝 Add Tamil subtitles
|
162 |
+
""")
|
163 |
+
|
164 |
st.sidebar.header("டப்பிங் அமைப்புகள்") # Dubbing Settings in Tamil
|
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 the dubbing process.")
|
172 |
return
|
173 |
|
174 |
+
# Settings in sidebar
|
175 |
+
with st.sidebar:
|
176 |
+
st.subheader("Voice Settings")
|
177 |
+
voice_type = st.selectbox("Select Voice", list(TAMIL_VOICES.keys()))
|
178 |
+
|
179 |
+
st.subheader("Subtitle Settings")
|
180 |
generate_subtitles = st.checkbox("Generate Tamil Subtitles", value=True)
|
181 |
+
if generate_subtitles:
|
182 |
+
subtitle_size = st.slider("Subtitle Size", 16, 32, 24)
|
183 |
+
subtitle_color = st.color_picker("Subtitle Color", "#FFFFFF")
|
184 |
|
185 |
+
# Main process
|
186 |
if st.button("Start Tamil Dubbing"):
|
187 |
try:
|
188 |
+
dubber = TamilDubber()
|
189 |
+
|
190 |
+
# Create progress containers
|
191 |
+
progress_bar = st.progress(0)
|
192 |
+
status_text = st.empty()
|
193 |
+
|
194 |
+
try:
|
195 |
+
# Save uploaded video
|
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 |
+
audio_segments = []
|
215 |
+
for idx, segment in enumerate(translated_segments):
|
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 |
+
# Add subtitles if enabled
|
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(
|
252 |
+
"⬇️ Download Dubbed Video",
|
253 |
+
f,
|
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"An error occurred: {str(e)}")
|
264 |
+
st.error("Please try again with a different video or check if the video format is supported.")
|
265 |
|
266 |
if __name__ == "__main__":
|
267 |
main()
|