Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import whisper | |
| import os | |
| import tempfile | |
| from pydub import AudioSegment | |
| import subprocess | |
| # Cargar modelo - "small" funciona mucho mejor que "base" para español | |
| print("Cargando modelo Whisper...") | |
| model = whisper.load_model("small") | |
| print("Modelo cargado.") | |
| def extract_audio_from_video(video_path): | |
| """Extrae audio de video usando ffmpeg""" | |
| audio_path = tempfile.mktemp(suffix='.wav') | |
| command = [ | |
| 'ffmpeg', | |
| '-i', video_path, | |
| '-vn', | |
| '-acodec', 'pcm_s16le', | |
| '-ar', '16000', | |
| '-ac', '1', | |
| '-y', | |
| audio_path | |
| ] | |
| result = subprocess.run(command, capture_output=True, text=True) | |
| if result.returncode != 0: | |
| raise Exception(f"Error extrayendo audio: {result.stderr}") | |
| return audio_path | |
| def convert_to_wav(input_path): | |
| """Convierte cualquier audio a WAV 16kHz mono""" | |
| audio = AudioSegment.from_file(input_path) | |
| audio_path = tempfile.mktemp(suffix='.wav') | |
| audio = audio.set_frame_rate(16000).set_channels(1) | |
| audio.export(audio_path, format="wav") | |
| return audio_path | |
| def transcribir_archivo(archivo): | |
| """Función principal de transcripción""" | |
| if archivo is None: | |
| yield "Por favor sube un archivo.", "" | |
| return | |
| archivos_temp = [] | |
| try: | |
| extension = os.path.splitext(archivo)[1].lower() | |
| es_video = extension in ['.mp4', '.avi', '.mov', '.mkv', '.webm', '.mpg', '.mpeg'] | |
| yield "Procesando archivo...", "" | |
| # Paso 1: obtener WAV limpio | |
| if es_video: | |
| yield "Extrayendo audio del video...", "" | |
| audio_path = extract_audio_from_video(archivo) | |
| else: | |
| yield "Convirtiendo audio a WAV...", "" | |
| audio_path = convert_to_wav(archivo) | |
| archivos_temp.append(audio_path) | |
| # Paso 2: verificar duración | |
| audio = AudioSegment.from_wav(audio_path) | |
| duracion_total = len(audio) / 1000 | |
| yield f"Audio listo. Duración: {duracion_total:.1f}s. Iniciando transcripción...", "" | |
| # Paso 3: transcribir directamente con Whisper | |
| # Whisper maneja internamente audios largos (sliding window de 30s) | |
| # sin necesidad de dividir manualmente, lo que mejora la coherencia | |
| resultado = model.transcribe( | |
| audio_path, | |
| language="es", | |
| task="transcribe", | |
| fp16=False, # Necesario en CPU (Hugging Face free tier) | |
| temperature=0, # Más determinista, menos alucinaciones | |
| best_of=1, | |
| beam_size=5, | |
| verbose=False | |
| ) | |
| texto_final = resultado["text"].strip() | |
| if not texto_final: | |
| yield "La transcripción quedó vacía. Verifica que el audio tenga voz clara.", "" | |
| return | |
| yield "¡Transcripción completada!", texto_final | |
| except Exception as e: | |
| yield f"Error: {str(e)}", "" | |
| finally: | |
| for temp_file in archivos_temp: | |
| try: | |
| if os.path.exists(temp_file): | |
| os.remove(temp_file) | |
| except: | |
| pass | |
| # Interfaz Gradio | |
| with gr.Blocks(title="Transcriptor de Video/Audio") as demo: | |
| gr.Markdown(""" | |
| # 🎙️ Transcriptor de Video y Audio | |
| Sube un video o archivo de audio y obtén la transcripción en español. | |
| **Formatos soportados:** MP4, AVI, MOV, MKV, MP3, WAV, M4A, OGG, WEBM | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| archivo_input = gr.File( | |
| label="Sube tu video o audio", | |
| file_types=["video", "audio"] | |
| ) | |
| btn_transcribir = gr.Button("🚀 Transcribir", variant="primary") | |
| with gr.Column(): | |
| estado = gr.Textbox(label="Estado", interactive=False) | |
| resultado = gr.Textbox( | |
| label="Transcripción", | |
| lines=15, | |
| interactive=True, | |
| placeholder="La transcripción aparecerá aquí..." | |
| ) | |
| btn_transcribir.click( | |
| fn=transcribir_archivo, | |
| inputs=archivo_input, | |
| outputs=[estado, resultado] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |