File size: 11,006 Bytes
5140a89
e575ced
5140a89
6ecc602
 
5140a89
e575ced
 
e4b3a75
e575ced
6ecc602
e575ced
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee4b9c0
 
 
e575ced
ee4b9c0
 
 
 
 
 
 
 
 
 
 
 
 
e575ced
 
 
 
 
ee4b9c0
 
 
 
 
 
 
 
 
e575ced
 
 
 
 
 
ee4b9c0
 
 
 
e575ced
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee4b9c0
 
 
 
 
 
e575ced
5140a89
ee4b9c0
 
6ecc602
 
 
ee4b9c0
6ecc602
ee4b9c0
 
 
 
 
 
 
6ecc602
ee4b9c0
6ecc602
 
ee4b9c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e4b3a75
ee4b9c0
 
 
 
6ecc602
 
ee4b9c0
 
 
 
 
 
 
 
8e46c45
ee4b9c0
 
 
 
 
 
5140a89
ee4b9c0
 
 
 
 
 
 
 
 
 
 
6ecc602
ee4b9c0
8e46c45
6ecc602
ee4b9c0
 
 
 
 
 
6ecc602
ee4b9c0
 
 
 
 
 
 
6ecc602
ee4b9c0
 
 
 
6ecc602
ee4b9c0
 
 
 
 
 
 
 
 
 
 
8e46c45
6ecc602
ee4b9c0
 
5140a89
ee4b9c0
5140a89
 
ee4b9c0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
import streamlit as st
from pathlib import Path
import torch
from transformers import pipeline
from PIL import Image, ImageDraw, ImageFont
import tempfile
import os
from moviepy.editor import *
import numpy as np
from gtts import gTTS
import textwrap
from concurrent.futures import ThreadPoolExecutor
import io
import unicodedata
import re

class FastVideoGenerator:
    def __init__(self):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        
        # Initialize text generation with efficient model
        self.text_generator = pipeline(
            'text-generation',
            model='distilgpt2',
            device=0 if self.device == "cuda" else -1
        )
        
        # Create temp directory
        self.temp_dir = Path(tempfile.mkdtemp())
        
        # Theme colors with opacity for better text visibility
        self.themes = {
            'Professional': {
                'bg': (245, 245, 245),
                'text': (33, 33, 33),
                'accent': (0, 102, 204),
                'overlay': (255, 255, 255, 180)
            },
            'Creative': {
                'bg': (255, 240, 245),
                'text': (51, 51, 51),
                'accent': (255, 64, 129),
                'overlay': (255, 255, 255, 180)
            },
            'Educational': {
                'bg': (240, 249, 255),
                'text': (25, 25, 25),
                'accent': (0, 151, 167),
                'overlay': (255, 255, 255, 180)
            }
        }

        # Pre-load font
        try:
            self.font = ImageFont.truetype("arial.ttf", 40)
        except:
            self.font = ImageFont.load_default()
            
        # Add text cleaner
        self.text_cleaner = re.compile(r'[^\x00-\x7F]+')

    @staticmethod
    def clean_text(text):
        """Clean text to handle encoding issues"""
        # Normalize unicode characters
        text = unicodedata.normalize('NFKD', text)
        # Replace special characters with standard ASCII
        text = text.encode('ascii', 'ignore').decode('ascii')
        # Replace common special characters
        replacements = {
            'โ€“': '-',  # en dash
            'โ€”': '-',  # em dash
            ''': "'",  # curly quote
            ''': "'",  # curly quote
            '"': '"',  # curly quote
            '"': '"',  # curly quote
            'โ€ฆ': '...' # ellipsis
        }
        for old, new in replacements.items():
            text = text.replace(old, new)
        return text

    @staticmethod
    @st.cache_data
    def generate_script_cached(prompt, style, length, temperature=0.7):
        """Cached script generation with proper text cleaning"""
        style_prompts = {
            'Professional': "Write a clear, professional video script about:",
            'Creative': "Write an engaging, creative video script about:",
            'Educational': "Write an informative educational video script about:"
        }
        
        prompt = FastVideoGenerator.clean_text(prompt)
        
        with pipeline('text-generation', model='distilgpt2') as generator:
            output = generator(
                f"{style_prompts[style]} {prompt}. Make it {length} seconds long.",
                max_length=min(length * 3, 1000),
                num_return_sequences=1,
                temperature=temperature
            )
        
        script = output[0]['generated_text']
        script = script.replace(style_prompts[style], '').strip()
        return FastVideoGenerator.clean_text(script)

    def create_frame_fast(self, text, theme, frame_number, total_frames, size=(1280, 720)):
        """Create frame with cleaned text"""
        # Clean text before rendering
        text = self.clean_text(text)
        
        # Create frame
        frame = np.full((size[1], size[0], 3), theme['bg'], dtype=np.uint8)
        img = Image.fromarray(frame)
        draw = ImageDraw.Draw(img)
        
        # Wrap text for better presentation
        wrapped_text = textwrap.fill(text, width=50)
        
        # Calculate text position
        text_bbox = draw.textbbox((0, 0), wrapped_text, font=self.font)
        text_x = (size[0] - (text_bbox[2] - text_bbox[0])) // 2
        text_y = (size[1] - (text_bbox[3] - text_bbox[1])) // 2
        
        # Draw text with background for better readability
        text_bg = Image.new('RGBA', size, (0, 0, 0, 0))
        text_draw = ImageDraw.Draw(text_bg)
        text_draw.text((text_x, text_y), wrapped_text, fill=theme['text'], font=self.font)
        
        # Add progress bar
        progress = frame_number / total_frames
        bar_width = int(1000 * progress)
        draw.rectangle([140, 650, 1140, 660], fill=(200,200,200))
        draw.rectangle([140, 650, 140+bar_width, 660], fill=theme['accent'])
        
        return np.array(img)

    def generate_audio_chunks(self, script, chunk_size=1000):
        """Generate audio with cleaned text"""
        # Clean text before TTS
        script = self.clean_text(script)
        chunks = textwrap.wrap(script, chunk_size)
        audio_paths = []
        
        for i, chunk in enumerate(chunks):
            chunk_path = self.temp_dir / f"audio_chunk_{i}.mp3"
            try:
                tts = gTTS(text=chunk, lang='en', slow=False)
                tts.save(str(chunk_path))
                audio_paths.append(chunk_path)
            except Exception as e:
                # If TTS fails, try with further cleaning
                cleaned_chunk = re.sub(r'[^a-zA-Z0-9\s.,!?-]', '', chunk)
                tts = gTTS(text=cleaned_chunk, lang='en', slow=False)
                tts.save(str(chunk_path))
                audio_paths.append(chunk_path)
            
        return audio_paths

    def create_optimized_video(self, script, theme, duration=30):
        """Create video with optimized processing"""
        fps = 24
        total_frames = duration * fps
        
        # Create frames efficiently
        def make_frame(t):
            frame_number = int(t * fps)
            return self.create_frame_fast(
                script,
                self.themes[theme],
                frame_number,
                total_frames
            )
        
        # Generate video with reduced memory usage
        clip = VideoClip(make_frame, duration=duration)
        
        # Generate audio in background while processing video
        with ThreadPoolExecutor() as executor:
            future_audio = executor.submit(self.generate_audio_chunks, script)
            
            # Process video
            output_path = self.temp_dir / "output_video.mp4"
            temp_video = self.temp_dir / "temp_video.mp4"
            
            # Write video without audio first
            clip.write_videofile(
                str(temp_video),
                fps=fps,
                codec='libx264',
                audio=False,
                preset='ultrafast'
            )
            
            # Get audio paths and combine audio
            audio_paths = future_audio.result()
            audio_clips = [AudioFileClip(str(path)) for path in audio_paths]
            final_audio = concatenate_audioclips(audio_clips)
            
            # Combine video and audio
            video = VideoFileClip(str(temp_video))
            final_clip = video.set_audio(final_audio)
            final_clip.write_videofile(str(output_path), fps=fps, codec='libx264')
            
            # Cleanup
            video.close()
            final_clip.close()
            for clip in audio_clips:
                clip.close()
            
            return output_path

def main():
    st.set_page_config(
        page_title="โšก Fast Video Generator",
        layout="wide",
        initial_sidebar_state="expanded"
    )

    # Custom CSS
    st.markdown("""
        <style>
        .stButton>button {
            width: 100%;
            height: 3em;
            background-color: #FF4B4B;
            color: white;
        }
        .stProgress > div > div > div > div {
            background-color: #FF4B4B;
        }
        </style>
    """, unsafe_allow_html=True)

    if 'video_generator' not in st.session_state:
        st.session_state.video_generator = FastVideoGenerator()

    with st.sidebar:
        st.title("๐ŸŽฎ Video Settings")
        
        theme = st.selectbox(
            "Theme Style",
            ["Professional", "Creative", "Educational"],
            help="Choose the visual style of your video"
        )
        
        duration = st.slider(
            "Duration (seconds)",
            min_value=30,
            max_value=300,
            value=60,
            step=30,
            help="Videos up to 5 minutes supported"
        )
        
        quality = st.select_slider(
            "Generation Speed",
            options=["High Quality", "Balanced", "Fast"],
            value="Balanced",
            help="Faster generation may reduce video quality"
        )

    st.title("โšก Fast Video Generator")
    st.markdown("Create longer videos with optimized performance!")

    text_input = st.text_area(
        "Video Topic",
        height=100,
        placeholder="Enter your topic here..."
    )

    if st.button("๐ŸŽฌ Generate Video", use_container_width=True):
        if text_input:
            try:
                progress_bar = st.progress(0)
                status = st.empty()
                
                # Script generation
                status.text("โœ๏ธ Creating script...")
                script = FastVideoGenerator.generate_script_cached(
                    text_input, theme, duration
                )
                progress_bar.progress(30)
                
                # Video creation
                status.text("๐ŸŽจ Generating video...")
                video_path = st.session_state.video_generator.create_optimized_video(
                    script, theme, duration
                )
                progress_bar.progress(100)
                status.text("โœจ Video ready!")
                
                # Display results
                tab1, tab2 = st.tabs(["๐Ÿ“ฝ๏ธ Video", "๐Ÿ“ Script"])
                
                with tab1:
                    st.video(str(video_path))
                    with open(str(video_path), 'rb') as f:
                        st.download_button(
                            "โฌ‡๏ธ Download Video",
                            f,
                            file_name="generated_video.mp4",
                            mime="video/mp4"
                        )
                
                with tab2:
                    st.markdown("### Generated Script")
                    st.write(script)
                
            except Exception as e:
                st.error(f"๐Ÿ’ฅ Error: {str(e)}")
                st.error("Please try again with different settings")
        else:
            st.warning("โš ๏ธ Please enter a topic first!")

if __name__ == "__main__":
    main()