import subprocess import random import re from pathlib import Path from moviepy.editor import VideoFileClip from pydub import AudioSegment class VerticalVideoGenerator: def __init__(self, source_folder, temp_folder, preview_generator, shorts_resolution=(1080, 1920), target_duration=59): """ Ініціалізація генератора вертикальних відео (Shorts та повних) Args: source_folder: Папка з фоновими відео temp_folder: Папка для тимчасових файлів preview_generator: Об'єкт PreviewGenerator для створення превью shorts_resolution: Розмір вертикальних відео (ширина, висота) target_duration: Цільова тривалість Shorts у секундах """ self.source_folder = Path(source_folder) self.temp_folder = Path(temp_folder) self.preview_generator = preview_generator self.shorts_resolution = shorts_resolution self.target_duration = target_duration def create_gradient_background(self, width, height, colors=None): """Створює градієнтний фон для заповнення порожніх областей""" if colors is None: # Красивий фіолетово-синій градієнт colors = [ (147, 112, 219), # medium slate blue (72, 61, 139), # dark slate blue (25, 25, 112), # midnight blue (0, 0, 139) # dark blue ] gradient_path = self.temp_folder / f"gradient_{width}x{height}.png" # Створюємо вертикальний градієнт cmd = [ "ffmpeg", "-y", "-f", "lavfi", "-i", f"color=c=#{colors[0][0]:02x}{colors[0][1]:02x}{colors[0][2]:02x}:size={width}x{height}:duration=1", "-vf", f"geq=r='r(X,Y)+({colors[1][0]-colors[0][0]})*Y/{height}':g='g(X,Y)+({colors[1][1]-colors[0][1]})*Y/{height}':b='b(X,Y)+({colors[1][2]-colors[0][2]})*Y/{height}'", "-frames:v", "1", str(gradient_path) ] try: subprocess.run(cmd, check=True, capture_output=True) return gradient_path except subprocess.CalledProcessError: # Fallback: створюємо простий градієнт return self.create_simple_gradient(width, height, colors[0], colors[-1]) def create_simple_gradient(self, width, height, color1, color2): """Створює простий градієнт як fallback""" gradient_path = self.temp_folder / f"simple_gradient_{width}x{height}.png" cmd = [ "ffmpeg", "-y", "-f", "lavfi", "-i", f"color=c=#{color1[0]:02x}{color1[1]:02x}{color1[2]:02x}:size={width}x{height}:duration=1", "-frames:v", "1", str(gradient_path) ] subprocess.run(cmd, check=True, capture_output=True) return gradient_path def apply_safe_fade_filters(self, use_dur, base_filters): """Застосовує fade фільтри безпечно для вертикального відео""" fade_in_duration = min(0.3, use_dur * 0.3) fade_out_duration = min(0.3, use_dur * 0.3) if fade_in_duration + fade_out_duration > use_dur: fade_in_duration = use_dur * 0.4 fade_out_duration = use_dur * 0.4 fade_out_start = max(0, use_dur - fade_out_duration) fade_filters = [] if fade_in_duration > 0.05: fade_filters.append(f"fade=t=in:st=0:d={fade_in_duration}") if fade_out_duration > 0.05 and fade_out_start > fade_in_duration: fade_filters.append(f"fade=t=out:st={fade_out_start}:d={fade_out_duration}") all_filters = [base_filters] + fade_filters return ",".join(all_filters) def create_vertical_video_with_gradient(self, video_path, output_path, duration, with_gradient=True): """Створює вертикальне відео з градієнтним фоном замість чорних сму