LPX55's picture
Upload folder using huggingface_hub
d3a2f33 verified
Raw
History Blame Contribute Delete
17.9 kB
import os
import tempfile
import re
import httpx
from datetime import datetime
import gradio as gr
from dotenv import load_dotenv
# Load local environment variables
load_dotenv()
# Import core logic from main.py
from main import cleanup_srt_punctuation, translate_srt_content
from deepgram import DeepgramClient, PrerecordedOptions
from deepgram_captions import DeepgramConverter, srt
from moviepy.video.io.VideoFileClip import VideoFileClip
# CSS styling for a premium glassmorphism dark-mode look
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;600;800&family=Inter:wght@400;500;600&display=swap');
body, .gradio-container {
font-family: 'Inter', sans-serif !important;
background: #0b0f19 !important;
}
/* Main card styling */
.glass-container {
background: rgba(17, 24, 39, 0.7) !important;
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.08) !important;
border-radius: 20px !important;
padding: 30px !important;
box-shadow: 0 10px 40px 0 rgba(0, 0, 0, 0.5) !important;
}
/* Glowing text title */
.glow-title {
background: linear-gradient(135deg, #a5b4fc 0%, #c084fc 50%, #818cf8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
text-align: center;
font-size: 2.8rem;
margin-bottom: 8px;
font-family: 'Outfit', sans-serif;
letter-spacing: -0.5px;
}
.sub-title {
color: #9ca3af;
text-align: center;
font-size: 1.15rem;
margin-bottom: 30px;
font-family: 'Inter', sans-serif;
}
/* Styled primary action button */
.action-btn {
background: linear-gradient(90deg, #6366f1 0%, #8b5cf6 100%) !important;
color: white !important;
border: none !important;
font-weight: 600 !important;
border-radius: 12px !important;
padding: 12px 24px !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.3) !important;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 25px rgba(99, 102, 241, 0.5) !important;
opacity: 0.95;
}
.action-btn:active {
transform: translateY(1px);
}
/* Inputs styling */
input, textarea, select {
background: #1f2937 !important;
border: 1px solid #374151 !important;
border-radius: 8px !important;
color: #f3f4f6 !important;
}
input:focus, textarea:focus, select:focus {
border-color: #818cf8 !important;
}
/* Tab styling */
.tabs {
border-bottom: 2px solid #1f2937 !important;
margin-bottom: 20px;
}
.tab-nav button {
font-family: 'Outfit', sans-serif;
font-size: 1.05rem !important;
color: #9ca3af !important;
padding: 10px 20px !important;
}
.tab-nav button.selected {
color: #818cf8 !important;
border-bottom: 2px solid #818cf8 !important;
}
/* Footer styling */
.footer-text {
text-align: center;
color: #4b5563;
font-size: 0.85rem;
margin-top: 40px;
}
"""
def extract_audio(video_path):
"""Extract audio track from video file using MoviePy."""
temp_dir = tempfile.gettempdir()
audio_path = os.path.join(temp_dir, f"extracted_{os.path.basename(video_path)}.mp3")
try:
with VideoFileClip(video_path) as video_clip:
audio_clip = video_clip.audio
# Write audio without verbose logging
audio_clip.write_audiofile(audio_path, logger=None)
return audio_path
except Exception as e:
raise gr.Error(f"Failed to extract audio from video: {str(e)}")
def process_transcribe(
file_path,
model,
language,
diarize,
translate_to,
dg_key_override,
dl_key_override
):
"""Core transcription and translation pipeline for audio/video input."""
if not file_path:
raise gr.Error("Please upload a file first.")
# Resolve Deepgram API Key
dg_key = dg_key_override.strip() if dg_key_override else os.getenv("DEEPGRAM_API_KEY")
if not dg_key:
raise gr.Error("Deepgram API Key is required. Please provide it in the UI or environment.")
# Resolve DeepL API Key (if translation requested)
dl_key = None
if translate_to:
dl_key = dl_key_override.strip() if dl_key_override else (os.getenv("DEEPL_API_KEY") or os.getenv("DEEPL_AUTH_KEY"))
if not dl_key:
raise gr.Error("DeepL API Key is required for translation. Please provide it in the UI or environment.")
# Check extension to determine if audio extraction is needed
_, ext = os.path.splitext(file_path.lower())
is_audio = ext in {'.mp3', '.wav', '.m4a', '.flac', '.ogg', '.aac', '.wma', '.opus', '.webm', '.m4b', '.mp4a', '.aiff', '.aif', '.mp2'}
audio_filepath = file_path
temp_audio_to_cleanup = None
if not is_audio:
gr.Info("Video file detected. Extracting audio track...")
audio_filepath = extract_audio(file_path)
temp_audio_to_cleanup = audio_filepath
try:
# Read the file data
with open(audio_filepath, "rb") as file:
buffer_data = file.read()
payload = {"buffer": buffer_data}
# Configure Deepgram options
options_dict = {
"model": model,
"smart_format": True,
"utterances": True,
"punctuate": True,
"diarize": diarize,
}
if language:
if language.lower() in {"auto", "detect"}:
options_dict["detect_language"] = True
else:
options_dict["language"] = language
options = PrerecordedOptions(**options_dict)
deepgram = DeepgramClient(dg_key)
gr.Info("Transcribing audio via Deepgram...")
response = deepgram.listen.rest.v("1").transcribe_file(
payload, options, timeout=httpx.Timeout(30000.0, connect=10.0)
)
# Process words check
has_words = False
try:
if hasattr(response, 'results') and response.results:
if response.results.channels and response.results.channels[0].alternatives:
if response.results.channels[0].alternatives[0].words:
has_words = True
except Exception:
pass
if not has_words:
original_srt = ""
gr.Warning("No speech detected in the audio file.")
else:
transcription = DeepgramConverter(response)
original_srt = srt(transcription)
original_srt = cleanup_srt_punctuation(original_srt)
# Write original SRT to temp file
temp_dir = tempfile.gettempdir()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
orig_file_path = os.path.join(temp_dir, f"transcription_{timestamp}.srt")
with open(orig_file_path, "w", encoding="utf-8") as f:
f.write(original_srt)
translated_srt = ""
trans_file_path = None
# Handle translation if requested
if translate_to and original_srt:
gr.Info(f"Translating subtitles to {translate_to} using DeepL...")
target_lang = translate_to.upper()
if target_lang == "EN":
target_lang = "EN-US"
elif target_lang == "PT":
target_lang = "PT-BR"
translated_srt = translate_srt_content(original_srt, dl_key, target_lang)
translated_srt = cleanup_srt_punctuation(translated_srt)
trans_file_path = os.path.join(temp_dir, f"transcription_{timestamp}.{translate_to.lower()}.srt")
with open(trans_file_path, "w", encoding="utf-8") as f:
f.write(translated_srt)
return original_srt, orig_file_path, translated_srt, trans_file_path
except Exception as e:
raise gr.Error(f"An error occurred: {str(e)}")
finally:
# Cleanup temporary extracted audio
if temp_audio_to_cleanup and os.path.exists(temp_audio_to_cleanup):
try:
os.remove(temp_audio_to_cleanup)
except Exception:
pass
def process_translate_srt(srt_file, translate_to, dl_key_override):
"""Translate an existing SRT file."""
if not srt_file:
raise gr.Error("Please upload an SRT file.")
dl_key = dl_key_override.strip() if dl_key_override else (os.getenv("DEEPL_API_KEY") or os.getenv("DEEPL_AUTH_KEY"))
if not dl_key:
raise gr.Error("DeepL API Key is required. Please provide it in the UI or environment.")
try:
with open(srt_file.name, "r", encoding="utf-8") as f:
original_content = f.read()
target_lang = translate_to.upper()
if target_lang == "EN":
target_lang = "EN-US"
elif target_lang == "PT":
target_lang = "PT-BR"
gr.Info(f"Translating SRT file to {translate_to} using DeepL...")
translated_content = translate_srt_content(original_content, dl_key, target_lang)
cleaned_content = cleanup_srt_punctuation(translated_content)
# Write to temp file
temp_dir = tempfile.gettempdir()
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
translated_path = os.path.join(temp_dir, f"translated_{timestamp}.srt")
with open(translated_path, "w", encoding="utf-8") as f:
f.write(cleaned_content)
return cleaned_content, translated_path
except Exception as e:
raise gr.Error(f"Translation error: {str(e)}")
# ------------------ Build Interface ------------------
# Supported languages list
language_choices = [
("Auto Detect", "auto"),
("English", "en"),
("Korean", "ko"),
("Spanish", "es"),
("French", "fr"),
("German", "de"),
("Italian", "it"),
("Japanese", "ja"),
("Chinese", "zh"),
("Portuguese", "pt"),
]
translation_choices = [
("None", ""),
("Korean", "ko"),
("English", "en"),
("Japanese", "ja"),
("Spanish", "es"),
("French", "fr"),
("German", "de"),
("Italian", "it"),
("Chinese", "zh"),
("Portuguese", "pt"),
]
model_choices = [
("Nova-3 (Latest / Recommended)", "nova-3"),
("Nova-2 (Fast & Accurate)", "nova-2"),
("Enhanced", "enhanced"),
("Base", "base"),
]
with gr.Blocks(title="Deepgram SRT Generator & Translator") as demo:
with gr.Column(elem_classes="glass-container"):
gr.HTML("<h1 class='glow-title'>Deepgram SRT Subtitles</h1>")
gr.HTML("<p class='sub-title'>Generate and translate SRT subtitles with state-of-the-art accuracy</p>")
# API Keys Accordion (Collapsible for cleaner layout)
with gr.Accordion("🔑 API Credentials (Optional Override)", open=False):
with gr.Row():
dg_key_input = gr.Textbox(
label="Deepgram API Key",
placeholder="Enter key to override DEEPGRAM_API_KEY environment variable",
type="password"
)
dl_key_input = gr.Textbox(
label="DeepL API Key",
placeholder="Enter key to override DEEPL_API_KEY environment variable",
type="password"
)
with gr.Tabs(elem_classes="tabs"):
# --- Tab 1: Video Transcription ---
with gr.TabItem("🎥 Transcribe Video"):
with gr.Row():
with gr.Column(scale=1):
video_input = gr.Video(label="Upload Video", sources=["upload"])
with gr.Row():
video_model = gr.Dropdown(
choices=model_choices, value="nova-3", label="Deepgram Model"
)
video_lang = gr.Dropdown(
choices=language_choices, value="auto", label="Audio Language", allow_custom_value=True
)
with gr.Row():
video_diarize = gr.Checkbox(label="Speaker Diarization", value=True)
video_trans = gr.Dropdown(
choices=translation_choices, value="", label="Translate Subtitles to (DeepL)"
)
video_btn = gr.Button("Generate Subtitles", elem_classes="action-btn")
with gr.Column(scale=1):
with gr.Tabs():
with gr.TabItem("Original Subtitles"):
video_original_srt = gr.Textbox(label="SRT Output", buttons=["copy"], lines=15)
video_original_file = gr.File(label="Download original SRT")
with gr.TabItem("Translated Subtitles"):
video_translated_srt = gr.Textbox(label="Translated SRT Output", buttons=["copy"], lines=15)
video_translated_file = gr.File(label="Download translated SRT")
video_btn.click(
fn=process_transcribe,
inputs=[
video_input,
video_model,
video_lang,
video_diarize,
video_trans,
dg_key_input,
dl_key_input
],
outputs=[
video_original_srt,
video_original_file,
video_translated_srt,
video_translated_file
],
api_name="transcribe_video"
)
# --- Tab 2: Audio Transcription ---
with gr.TabItem("🎵 Transcribe Audio"):
with gr.Row():
with gr.Column(scale=1):
audio_input = gr.Audio(label="Upload Audio", type="filepath", sources=["upload"])
with gr.Row():
audio_model = gr.Dropdown(
choices=model_choices, value="nova-3", label="Deepgram Model"
)
audio_lang = gr.Dropdown(
choices=language_choices, value="auto", label="Audio Language", allow_custom_value=True
)
with gr.Row():
audio_diarize = gr.Checkbox(label="Speaker Diarization", value=True)
audio_trans = gr.Dropdown(
choices=translation_choices, value="", label="Translate Subtitles to (DeepL)"
)
audio_btn = gr.Button("Generate Subtitles", elem_classes="action-btn")
with gr.Column(scale=1):
with gr.Tabs():
with gr.TabItem("Original Subtitles"):
audio_original_srt = gr.Textbox(label="SRT Output", buttons=["copy"], lines=15)
audio_original_file = gr.File(label="Download original SRT")
with gr.TabItem("Translated Subtitles"):
audio_translated_srt = gr.Textbox(label="Translated SRT Output", buttons=["copy"], lines=15)
audio_translated_file = gr.File(label="Download translated SRT")
audio_btn.click(
fn=process_transcribe,
inputs=[
audio_input,
audio_model,
audio_lang,
audio_diarize,
audio_trans,
dg_key_input,
dl_key_input
],
outputs=[
audio_original_srt,
audio_original_file,
audio_translated_srt,
audio_translated_file
],
api_name="transcribe_audio"
)
# --- Tab 3: SRT Translation ---
with gr.TabItem("📄 Translate SRT File"):
with gr.Row():
with gr.Column(scale=1):
srt_input = gr.File(label="Upload SRT File", file_types=[".srt"])
srt_trans_lang = gr.Dropdown(
choices=translation_choices[1:], value="ko", label="Translate Subtitles to (DeepL)"
)
srt_btn = gr.Button("Translate File", elem_classes="action-btn")
with gr.Column(scale=1):
srt_output_text = gr.Textbox(label="Translated SRT Output", buttons=["copy"], lines=15)
srt_output_file = gr.File(label="Download translated SRT")
srt_btn.click(
fn=process_translate_srt,
inputs=[
srt_input,
srt_trans_lang,
dl_key_input
],
outputs=[
srt_output_text,
srt_output_file
],
api_name="translate_srt"
)
gr.HTML("<div class='footer-text'>Deepgram SRT Subtitle Tool • Powered by Deepgram & DeepL</div>")
if __name__ == "__main__":
demo.queue()
demo.launch(server_name="0.0.0.0", server_port=7860, css=custom_css)