gnosticdev commited on
Commit
1f72b0c
verified
1 Parent(s): 7a44339

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -0
app.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import math
3
+ import tempfile
4
+ from pydub import AudioSegment
5
+ from moviepy.editor import (
6
+ VideoFileClip, AudioFileClip, ImageClip,
7
+ concatenate_videoclips, CompositeVideoClip,
8
+ concatenate_audioclips
9
+ )
10
+ import edge_tts
11
+ import gradio as gr
12
+ import asyncio
13
+
14
+ # CONSTANTES DE ARCHIVOS
15
+ INTRO_VIDEO = "introvideo.mp4"
16
+ OUTRO_VIDEO = "outrovideo.mp4"
17
+ MUSIC_BG = "musicafondo.mp3"
18
+ FX_SOUND = "fxsound.mp3"
19
+ WATERMARK = "watermark.png"
20
+
21
+ # Validar existencia de archivos obligatorios
22
+ for file in [INTRO_VIDEO, OUTRO_VIDEO, MUSIC_BG, FX_SOUND, WATERMARK]:
23
+ if not os.path.exists(file):
24
+ raise FileNotFoundError(f"Falta archivo necesario: {file}")
25
+
26
+ def cortar_video(video_path, metodo="inteligente", duracion=10):
27
+ video = VideoFileClip(video_path)
28
+ if metodo == "manual":
29
+ return [video.subclip(i*duracion, (i+1)*duracion)
30
+ for i in range(math.ceil(video.duration/duracion))]
31
+
32
+ # Implementaci贸n b谩sica de cortes por voz (requerir铆a VAD real)
33
+ clips = []
34
+ ultimo_corte = 0
35
+ for i in range(1, math.ceil(video.duration)):
36
+ if i % 5 == 0: # Simulaci贸n de detecci贸n de pausas
37
+ clips.append(video.subclip(ultimo_corte, i))
38
+ ultimo_corte = i
39
+ return clips
40
+
41
+ def procesar_audio(texto, voz, clips_duracion):
42
+ communicate = edge_tts.Communicate(texto, voz)
43
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
44
+ asyncio.run(communicate.save(tmp.name))
45
+ tts_audio = AudioFileClip(tmp.name)
46
+
47
+ # Ajustar TTS a duraci贸n de clips
48
+ if tts_audio.duration < clips_duracion:
49
+ tts_audio = tts_audio.loop(duration=clips_duracion)
50
+ else:
51
+ tts_audio = tts_audio.subclip(0, clips_duracion)
52
+
53
+ # Mezclar con m煤sica de fondo
54
+ bg_music = AudioSegment.from_mp3(MUSIC_BG)
55
+ if len(bg_music) < clips_duracion*1000:
56
+ bg_music = bg_music * math.ceil(clips_duracion*1000 / len(bg_music))
57
+ bg_music = bg_music[:clips_duracion*1000].fade_out(3000)
58
+
59
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
60
+ bg_music.export(tmp.name, format="mp3")
61
+ bg_audio = AudioFileClip(tmp.name).volumex(0.10)
62
+
63
+ return CompositeAudioClip([bg_audio, tts_audio.volumex(0.9)])
64
+
65
+ def agregar_transiciones(clips):
66
+ fx_audio = AudioFileClip(FX_SOUND).set_duration(2.5)
67
+ transicion = ImageClip(WATERMARK).set_duration(2.5)
68
+ transicion = transicion.resize(height=clips[0].h).set_position(("center", 0.1))
69
+
70
+ clips_con_fx = []
71
+ for i, clip in enumerate(clips):
72
+ # Agregar watermark
73
+ clip_watermarked = CompositeVideoClip([clip, transicion])
74
+ clips_con_fx.append(clip_watermarked)
75
+
76
+ if i < len(clips)-1:
77
+ clips_con_fx.append(
78
+ CompositeVideoClip([transicion.set_position("center")])
79
+ .set_audio(fx_audio)
80
+ )
81
+
82
+ return concatenate_videoclips(clips_con_fx)
83
+
84
+ async def procesar_video(
85
+ video_input,
86
+ texto_tts,
87
+ voz_seleccionada,
88
+ metodo_corte,
89
+ duracion_corte
90
+ ):
91
+ # Procesar video principal
92
+ clips = cortar_video(video_input, metodo_corte, duracion_corte)
93
+ video_editado = agregar_transiciones(clips)
94
+
95
+ # Agregar intro/outro
96
+ intro = VideoFileClip(INTRO_VIDEO)
97
+ outro = VideoFileClip(OUTRO_VIDEO)
98
+ video_final = concatenate_videoclips([intro, video_editado, outro])
99
+
100
+ # Procesar audio
101
+ audio_final = procesar_audio(
102
+ texto_tts,
103
+ voz_seleccionada,
104
+ video_editado.duration
105
+ )
106
+
107
+ # Combinar y renderizar
108
+ video_final = video_final.set_audio(audio_final)
109
+
110
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as tmp:
111
+ video_final.write_videofile(tmp.name, codec="libx264", fps=24)
112
+ return tmp.name
113
+
114
+ # Interfaz Gradio
115
+ with gr.Blocks() as demo:
116
+ gr.Markdown("# Video Editor IA")
117
+
118
+ with gr.Tab("Principal"):
119
+ video_input = gr.Video(label="Subir video")
120
+ texto_tts = gr.Textbox(label="Texto para TTS", lines=3)
121
+ voz_seleccionada = gr.Dropdown(
122
+ label="Seleccionar voz",
123
+ choices=["es-ES-AlvaroNeural", "es-MX-BeatrizNeural"]
124
+ )
125
+ procesar_btn = gr.Button("Generar Video")
126
+ video_output = gr.Video(label="Resultado")
127
+
128
+ with gr.Tab("Ajustes"):
129
+ metodo_corte = gr.Radio(
130
+ ["inteligente", "manual"],
131
+ label="M茅todo de cortes",
132
+ value="inteligente"
133
+ )
134
+ duracion_corte = gr.Slider(
135
+ 1, 60, 10,
136
+ label="Segundos por corte (solo manual)"
137
+ )
138
+
139
+ procesar_btn.click(
140
+ procesar_video,
141
+ inputs=[
142
+ video_input,
143
+ texto_tts,
144
+ voz_seleccionada,
145
+ metodo_corte,
146
+ duracion_corte
147
+ ],
148
+ outputs=video_output
149
+ )
150
+
151
+ if __name__ == "__main__":
152
+ demo.queue().launch()