DAI_Project / app.py
younes21000's picture
Update app.py
476a03d verified
import gradio as gr
import moviepy.editor as mp
import librosa
from transformers import pipeline
from concurrent.futures import ThreadPoolExecutor
import tempfile
import docx # To create Word documents
from moviepy.video.tools.subtitles import SubtitlesClip
from moviepy.editor import TextClip
# Load Whisper model for speech-to-text (using smaller 'tiny' model for faster performance)
asr = pipeline("automatic-speech-recognition", model="openai/whisper-tiny")
# MarianMT or M2M100 for translation (multi-language)
translator = pipeline("translation", model="facebook/m2m100_418M")
# Store generated subtitles and translations
subtitle_storage = {}
# Supported languages with their codes
languages = {
"Persian": "fa",
"French": "fr",
"Spanish": "es",
"German": "de",
"Chinese": "zh",
"Arabic": "ar",
"Hindi": "hi",
"Russian": "ru"
}
def transcribe_audio(chunk):
"""Transcribe a single audio chunk."""
return asr(chunk)["text"]
def add_subtitle(video):
try:
# The video is passed as a file path string, so we use it directly
video_path = video if isinstance(video, str) else None
if not video_path:
return "No video provided!"
video_clip = mp.VideoFileClip(video_path)
audio = video_clip.audio
# Use a temporary file for audio extraction
with tempfile.NamedTemporaryFile(delete=True, suffix='.wav') as tmp_audio_file:
audio.write_audiofile(tmp_audio_file.name, codec='pcm_s16le')
waveform, sr = librosa.load(tmp_audio_file.name, sr=16000)
# Transcribe in chunks (parallel)
chunk_duration = 15 # seconds
chunk_size = sr * chunk_duration
chunks = [
waveform[i:i + chunk_size]
for i in range(0, len(waveform), chunk_size)
if len(waveform[i:i + chunk_size]) > 0
]
with ThreadPoolExecutor() as executor:
transcriptions = list(executor.map(transcribe_audio, chunks))
full_transcription = " ".join(transcriptions)
subtitle_storage["original"] = full_transcription # Store the original subtitle (English or other)
subtitle_storage["video_path"] = video_path # Store the video path
return f"Subtitle added: {full_transcription[:100]}..." # Display first 100 characters
except Exception as e:
return f"Error in adding subtitle: {e}"
def translate_subtitle(video, language):
try:
# Translate the stored subtitle
original_subtitle = subtitle_storage.get("original")
if not original_subtitle:
return "No subtitle to translate!"
# Translate using the selected language
translated_subtitle = translator(
original_subtitle,
src_lang="en", # Source language (assuming the subtitle is in English)
tgt_lang=languages[language] # Get the language code from the dropdown selection
)[0]["translation_text"]
subtitle_storage["translated"] = translated_subtitle # Store the translated subtitle
return f"Subtitle translated to {language} successfully!"
except Exception as e:
return f"Error in translating subtitle: {e}"
def download_word():
try:
# Save translated subtitles to a Word document
translated_subtitle = subtitle_storage.get("translated")
if not translated_subtitle:
return "No translated subtitle to save!"
# Prepare the document
doc = docx.Document()
doc.add_heading('Translated Subtitles', 0)
# Create timestamps and subtitles
for i in range(0, len(translated_subtitle), 40): # Adjusted chunk size
start_time = (i // 40) * 2.5 # Each subtitle lasts for 2.5 seconds
subtitle_text = translated_subtitle[i:i + 40].strip() # Get the next 40 characters
# Add a formatted string with timestamp and subtitle to the document
if subtitle_text:
doc.add_paragraph(f"{start_time:.3f}s - {subtitle_text}")
file_path = "translated_subtitles.docx"
doc.save(file_path)
# Return the file for download
return file_path # Gradio will handle this as a downloadable file
except Exception as e:
return f"Error in saving subtitles as Word: {e}"
def download_original_subtitled_video():
"""Download video with original subtitles (no translation)."""
try:
# Add original subtitles to the video
original_subtitle = subtitle_storage.get("original")
if not original_subtitle:
return "No original subtitles available!"
video_path = subtitle_storage.get("video_path")
video = mp.VideoFileClip(video_path)
# Function to generate subtitle text
generator = lambda txt: TextClip(txt, font='Arial', fontsize=24, color='white')
# Generate original subtitles with start_time, end_time, and text
subs = []
subtitle_length = 2.5 # seconds each subtitle will be displayed
for i in range(0, len(original_subtitle), 40): # Adjusted chunk size
start_time = (i // 40) * subtitle_length
end_time = start_time + subtitle_length
subtitle_text = original_subtitle[i:i + 40].strip() # Get the next 40 characters
if subtitle_text:
subs.append((start_time, end_time, subtitle_text)) # Create a tuple for start time, end time, and text
# Debugging: Print the generated subtitles
print("Generated subtitles:", subs)
# Create subtitle clips
subtitles = SubtitlesClip(subs, generator)
# Overlay subtitles on video
subtitled_video = mp.CompositeVideoClip([video, subtitles.set_position(('center', 'bottom'))])
output_video_path = "original_subtitled_video.mp4"
subtitled_video.write_videofile(output_video_path, codec='libx264') # Use libx264 for better compatibility
# Return the file path for download
return output_video_path
except Exception as e:
return f"Error in generating original subtitled video: {e}"
# Gradio UI Interface
with gr.Blocks() as demo:
# Title
gr.Markdown("<h1 style='text-align: center;'>Video Subtitle Translator</h1>")
# Video Upload
with gr.Row():
video_input = gr.Video(label="Upload Video")
upload_button = gr.Button("Upload Video")
upload_status = gr.Textbox(label="Upload Status")
upload_button.click(add_subtitle, inputs=video_input, outputs=upload_status)
# Add Subtitle
with gr.Row():
add_subtitle_button = gr.Button("Add Subtitle")
subtitle_status = gr.Textbox(label="Subtitle Status")
add_subtitle_button.click(add_subtitle, inputs=video_input, outputs=subtitle_status)
# Translate Subtitle
with gr.Row():
language_dropdown = gr.Dropdown(choices=list(languages.keys()), label="Choose Target Language", value="Persian")
translate_button = gr.Button("Translate Subtitle")
translate_status = gr.Textbox(label="Translation Status")
translate_button.click(translate_subtitle, inputs=[video_input, language_dropdown], outputs=translate_status)
# Download as Word
with gr.Row():
download_button = gr.Button("Download as Word")
download_status = gr.File(label="Download Translated Word File") # File output for Word download
download_button.click(download_word, inputs=None, outputs=download_status)
# Download Original Subtitled Video (no translation)
with gr.Row():
download_original_video_button = gr.Button("Download Original Subtitled Video")
download_original_video_status = gr.File(label="Download Original Subtitled Video") # File output for original subtitled video
download_original_video_button.click(download_original_subtitled_video, inputs=None, outputs=download_original_video_status)
# Launch the Gradio app
demo.launch()