Spaces:
Sleeping
Sleeping
| 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() | |