# @title core/video_editor.py import os import json import asyncio from typing import List, Dict, Any, Optional from pathlib import Path import subprocess from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_videoclips from utils.logger import SpotMakerLogger class VideoEditor: """ Montażysta video który łączy wygenerowane klipy z audio w finalny spot. """ def __init__(self, output_dir: str, logger: SpotMakerLogger): self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) self.logger = logger async def create_spot(self, video_paths: List[str], audio_path: str, audio_duration: float) -> str: """ Tworzy finalny spot łącząc klipy video ze ścieżką audio. Args: video_paths: Lista ścieżek do klipów video audio_path: Ścieżka do pliku audio audio_duration: Długość ścieżki audio w sekundach Returns: Ścieżka do finalnego spotu """ try: self.logger.log("video_editor", "Rozpoczynam montaż spotu", "info") self.logger.update_progress("video_editor", 0, "Inicjalizacja...") # Oblicz długość pojedynczego klipu clip_duration = audio_duration / len(video_paths) self.logger.log("video_editor", f"Długość pojedynczego klipu: {clip_duration:.2f}s", "debug") # Wczytaj i przygotuj klipy clips = [] for i, path in enumerate(video_paths, 1): self.logger.update_progress("video_editor", i * 20, f"Przetwarzam klip {i}/{len(video_paths)}") self.logger.log("video_editor", f"Wczytuję klip: {path}", "debug") clip = VideoFileClip(path) # Dostosuj długość klipu if clip.duration != clip_duration: clip = clip.set_duration(clip_duration) clips.append(clip) # Połącz klipy self.logger.update_progress("video_editor", 80, "Łączę klipy...") final_video = concatenate_videoclips(clips) # Dodaj ścieżkę audio self.logger.update_progress("video_editor", 90, "Dodaję audio...") audio = AudioFileClip(audio_path) final_video = final_video.set_audio(audio) # Zapisz finalny spot output_path = self.output_dir / "final_spot.mp4" self.logger.log("video_editor", f"Zapisuję finalny spot: {output_path}", "debug") final_video.write_videofile( str(output_path), codec='libx264', audio_codec='aac', temp_audiofile='temp-audio.m4a', remove_temp=True ) # Zamknij klipy aby zwolnić zasoby final_video.close() audio.close() for clip in clips: clip.close() self.logger.update_progress("video_editor", 100, "Montaż zakończony") return str(output_path) except Exception as e: self.logger.format_error("video_editor", e) raise def get_status(self) -> Dict[str, Any]: """Zwraca aktualny status montażysty.""" return { 'progress': self.logger.get_module_progress("video_editor"), 'status': self.logger.get_module_status("video_editor") }