Spaces:
Running
Running
# @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") | |
} |