diff --git a/backgrounds/nature.mp4 b/backgrounds/nature.mp4 index e69de29..763ae8a 100644 Binary files a/backgrounds/nature.mp4 and b/backgrounds/nature.mp4 differ diff --git a/backgrounds/space.mp4 b/backgrounds/space.mp4 index e69de29..e1e2e51 100644 Binary files a/backgrounds/space.mp4 and b/backgrounds/space.mp4 differ diff --git a/core/__pycache__/video_engine.cpython-311.pyc b/core/__pycache__/video_engine.cpython-311.pyc index 6750035..037abd2 100644 Binary files a/core/__pycache__/video_engine.cpython-311.pyc and b/core/__pycache__/video_engine.cpython-311.pyc differ diff --git a/core/video_engine.py b/core/video_engine.py index edab27c..fe5a756 100644 --- a/core/video_engine.py +++ b/core/video_engine.py @@ -11,6 +11,28 @@ TEMP_AUDIO_DIR = OUTPUTS_DIR / "temp_audio" AUDIO_DIR = OUTPUTS_DIR / "audio" BACKGROUNDS_DIR = WORKSPACE_ROOT / "backgrounds" +def escape_ffmpeg_text(text): + # Escape for ffmpeg drawtext filter + # Single quotes need special handling: ' becomes '\'' + # Also colons and other special chars might need escaping depending on context + return text.replace("'", "'\\''").replace(":", "\\:") + +def ensure_bg_video(bg_path): + """Ensures a background video exists and is not empty. Creates a placeholder if needed.""" + if not bg_path.exists() or bg_path.stat().st_size == 0: + bg_path.parent.mkdir(parents=True, exist_ok=True) + # Create a 5-second 720x1280 (vertical) placeholder video + color = "black" + if "nature" in bg_path.name: + color = "#064E3B" # Deep Emerald + elif "space" in bg_path.name: + color = "#1E1B4B" # Deep Indigo + + subprocess.run([ + 'ffmpeg', '-y', '-f', 'lavfi', '-i', f'color=c={color}:s=720x1280:d=5', + '-pix_fmt', 'yuv420p', str(bg_path) + ], check=True) + def generate_video(task_id): from core.models import VideoTask task = VideoTask.objects.get(id=task_id) @@ -18,15 +40,14 @@ def generate_video(task_id): task.save() try: + # Ensure directories exist + for d in [FINAL_VIDEO_DIR, TEMP_AUDIO_DIR, AUDIO_DIR, BACKGROUNDS_DIR]: + d.mkdir(parents=True, exist_ok=True) + # 1. Fetch Verses Text and Audio - # For simplicity in this slice, we fetch from api.alquran.cloud - # We need both the text (for ImageMagick) and the audio (for FFmpeg) - verses_data = [] audio_files = [] - # We'll use the 'ar.alafasy' or user-selected reciter - # Example: https://api.alquran.cloud/v1/surah/1/ar.alafasy api_url = f"https://api.alquran.cloud/v1/surah/{task.surah_number}/{task.reciter_identifier}" resp = requests.get(api_url) resp.raise_for_status() @@ -35,7 +56,7 @@ def generate_video(task_id): all_ayahs = data['ayahs'] selected_ayahs = [a for a in all_ayahs if task.verse_start <= a['numberInSurah'] <= task.verse_end] - # Download audio files and prepare text + # Download audio files for i, ayah in enumerate(selected_ayahs): audio_url = ayah['audio'] audio_path = TEMP_AUDIO_DIR / f"task_{task.id}_ayah_{i}.mp3" @@ -52,32 +73,30 @@ def generate_video(task_id): # 2. Combine Audio combined_audio = AUDIO_DIR / f"task_{task.id}_full.mp3" - # ffmpeg -i "concat:file1.mp3|file2.mp3" -acodec copy output.mp3 - concat_str = "|".join(audio_files) - subprocess.run([ - 'ffmpeg', '-y', '-i', f'concat:{concat_str}', '-acodec', 'libmp3lame', str(combined_audio) - ], check=True) + # We use a filter_complex to concat audio as it's more reliable than concat protocol for different mp3s + filter_complex = "".join([f"[{i}:a]" for i in range(len(audio_files))]) + f"concat=n={len(audio_files)}:v=0:a=1[a]" + cmd = ['ffmpeg', '-y'] + for f in audio_files: + cmd.extend(['-i', f]) + cmd.extend(['-filter_complex', filter_complex, '-map', '[a]', str(combined_audio)]) + subprocess.run(cmd, check=True) # 3. Generate Video with FFmpeg - # We use a background video and overlay the audio - bg_video = BACKGROUNDS_DIR / task.background_video - if not bg_video.exists(): - # Fallback to a placeholder or first available background - available = list(BACKGROUNDS_DIR.glob("*.mp4")) - if available: - bg_video = available[0] - else: - raise Exception("No background video found in backgrounds/ folder") + bg_video = BACKGROUNDS_DIR / (task.background_video or "nature.mp4") + ensure_bg_video(bg_video) output_video = FINAL_VIDEO_DIR / f"reels_{task.id}.mp4" + # Escape text for drawtext + escaped_surah = escape_ffmpeg_text(task.surah_name) + # Simple FFmpeg command: loop background, add audio, trim to audio length - # For text overlay, we'd ideally use drawtext or ImageMagick. - # Here we do a basic version: + # Using a vertical aspect ratio 720x1280 (common for Reels) subprocess.run([ 'ffmpeg', '-y', '-stream_loop', '-1', '-i', str(bg_video), '-i', str(combined_audio), '-shortest', '-map', '0:v:0', '-map', '1:a:0', - '-pix_fmt', 'yuv420p', '-vf', f"drawtext=text='{task.surah_name}':fontcolor={task.text_color}:fontsize=48:x=(w-text_w)/2:y=(h-text_h)/2", + '-pix_fmt', 'yuv420p', + '-vf', f"scale=720:1280:force_original_aspect_ratio=increase,crop=720:1280,drawtext=text='{escaped_surah}':fontcolor={task.text_color}:fontsize=64:x=(w-text_w)/2:y=(h-text_h)/2:shadowcolor=black:shadowx=2:shadowy=2", str(output_video) ], check=True) @@ -89,4 +108,6 @@ def generate_video(task_id): task.status = 'failed' task.error_message = str(e) task.save() - raise e + import traceback + print(traceback.format_exc()) + raise e \ No newline at end of file diff --git a/outputs/audio/task_4_full.mp3 b/outputs/audio/task_4_full.mp3 new file mode 100644 index 0000000..dad8b68 Binary files /dev/null and b/outputs/audio/task_4_full.mp3 differ diff --git a/outputs/audio/task_5_full.mp3 b/outputs/audio/task_5_full.mp3 new file mode 100644 index 0000000..9bc376d Binary files /dev/null and b/outputs/audio/task_5_full.mp3 differ diff --git a/outputs/final_video/reels_5.mp4 b/outputs/final_video/reels_5.mp4 new file mode 100644 index 0000000..70a3505 Binary files /dev/null and b/outputs/final_video/reels_5.mp4 differ diff --git a/outputs/temp_audio/task_4_ayah_0.mp3 b/outputs/temp_audio/task_4_ayah_0.mp3 new file mode 100644 index 0000000..a245f8c Binary files /dev/null and b/outputs/temp_audio/task_4_ayah_0.mp3 differ diff --git a/outputs/temp_audio/task_5_ayah_0.mp3 b/outputs/temp_audio/task_5_ayah_0.mp3 new file mode 100644 index 0000000..16b9ab9 Binary files /dev/null and b/outputs/temp_audio/task_5_ayah_0.mp3 differ