Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -23,12 +23,12 @@ from fastapi.responses import FileResponse, JSONResponse
|
|
| 23 |
from starlette.background import BackgroundTask
|
| 24 |
|
| 25 |
# ==============================================================================
|
| 26 |
-
# 1.
|
| 27 |
# ==============================================================================
|
| 28 |
app = FastAPI(
|
| 29 |
title="FFmpeg as a Service for n8n",
|
| 30 |
description="A robust API to create videos from image and audio Gradio URLs.",
|
| 31 |
-
version="
|
| 32 |
)
|
| 33 |
|
| 34 |
TEMP_DIR = "/tmp/ffmpeg_processing"
|
|
@@ -75,7 +75,7 @@ async def process_video_from_urls(payload: Dict = Body(...)):
|
|
| 75 |
unique_id = str(uuid.uuid4())
|
| 76 |
temp_processing_dir = os.path.join(TEMP_DIR, unique_id)
|
| 77 |
os.makedirs(temp_processing_dir)
|
| 78 |
-
|
| 79 |
try:
|
| 80 |
image_urls = payload.get("image_urls", [])
|
| 81 |
audio_url = payload.get("audio_url")
|
|
@@ -85,39 +85,37 @@ async def process_video_from_urls(payload: Dict = Body(...)):
|
|
| 85 |
if not all([image_urls, audio_url, total_duration]):
|
| 86 |
raise HTTPException(status_code=400, detail="Bad Request: 'image_urls', 'audio_url', and 'total_duration' are required.")
|
| 87 |
|
| 88 |
-
# --- Step 1: Download All Media
|
| 89 |
-
print("--- STEP 1: DOWNLOADING ALL MEDIA ---")
|
| 90 |
async with aiohttp.ClientSession(headers=BROWSER_HEADERS) as session:
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
# --- Step 2: Create Silent Video Slideshow ---
|
| 114 |
print("--- STEP 2: CREATING SILENT VIDEO ---")
|
| 115 |
duration_per_image = total_duration / len(image_urls)
|
| 116 |
silent_video_path = os.path.join(temp_processing_dir, "silent_video.mp4")
|
| 117 |
-
|
| 118 |
-
# Usando a sintaxe de -framerate que é mais robusta
|
| 119 |
input_pattern = shlex.quote(os.path.join(temp_processing_dir, 'image_%02d.jpg'))
|
| 120 |
framerate = 1 / duration_per_image
|
|
|
|
| 121 |
cmd_video = f"ffmpeg -framerate {framerate} -i {input_pattern} -c:v libx264 -pix_fmt yuv420p -r 25 -y {shlex.quote(silent_video_path)}"
|
| 122 |
await run_subprocess(cmd_video)
|
| 123 |
print("--- SUCCESS: SILENT VIDEO CREATED ---")
|
|
|
|
| 23 |
from starlette.background import BackgroundTask
|
| 24 |
|
| 25 |
# ==============================================================================
|
| 26 |
+
# 1. APPLICATION CONFIGURATION & SETUP
|
| 27 |
# ==============================================================================
|
| 28 |
app = FastAPI(
|
| 29 |
title="FFmpeg as a Service for n8n",
|
| 30 |
description="A robust API to create videos from image and audio Gradio URLs.",
|
| 31 |
+
version="4.0.0",
|
| 32 |
)
|
| 33 |
|
| 34 |
TEMP_DIR = "/tmp/ffmpeg_processing"
|
|
|
|
| 75 |
unique_id = str(uuid.uuid4())
|
| 76 |
temp_processing_dir = os.path.join(TEMP_DIR, unique_id)
|
| 77 |
os.makedirs(temp_processing_dir)
|
| 78 |
+
|
| 79 |
try:
|
| 80 |
image_urls = payload.get("image_urls", [])
|
| 81 |
audio_url = payload.get("audio_url")
|
|
|
|
| 85 |
if not all([image_urls, audio_url, total_duration]):
|
| 86 |
raise HTTPException(status_code=400, detail="Bad Request: 'image_urls', 'audio_url', and 'total_duration' are required.")
|
| 87 |
|
| 88 |
+
# --- Step 1: Download All Media SEQUENTIALLY ---
|
| 89 |
+
print("--- STEP 1: DOWNLOADING ALL MEDIA (SEQUENTIAL MODE) ---")
|
| 90 |
async with aiohttp.ClientSession(headers=BROWSER_HEADERS) as session:
|
| 91 |
+
# First, download the audio file.
|
| 92 |
+
narracao_path = os.path.join(temp_processing_dir, "narracao.mp3")
|
| 93 |
+
print(f"Downloading audio from {audio_url}...")
|
| 94 |
+
async with session.get(audio_url) as resp:
|
| 95 |
+
if resp.status != 200:
|
| 96 |
+
raise HTTPException(status_code=502, detail=f"Failed to download audio. Status: {resp.status}, URL: {audio_url}")
|
| 97 |
+
with open(narracao_path, "wb") as f: f.write(await resp.read())
|
| 98 |
+
print("SUCCESS: Audio downloaded.")
|
| 99 |
+
|
| 100 |
+
# Now, loop and download images one by one.
|
| 101 |
+
local_image_paths = []
|
| 102 |
+
for i, url in enumerate(image_urls):
|
| 103 |
+
local_path = os.path.join(temp_processing_dir, f"image_{i:02d}.jpg")
|
| 104 |
+
print(f"Downloading image {i+1}/{len(image_urls)} from {url}...")
|
| 105 |
+
async with session.get(url) as resp:
|
| 106 |
+
if resp.status != 200:
|
| 107 |
+
raise HTTPException(status_code=502, detail=f"Failed to download image {i+1}. Status: {resp.status}, URL: {url}")
|
| 108 |
+
with open(local_path, "wb") as f: f.write(await resp.read())
|
| 109 |
+
local_image_paths.append(local_path)
|
| 110 |
+
print("SUCCESS: All images downloaded.")
|
| 111 |
+
|
|
|
|
| 112 |
# --- Step 2: Create Silent Video Slideshow ---
|
| 113 |
print("--- STEP 2: CREATING SILENT VIDEO ---")
|
| 114 |
duration_per_image = total_duration / len(image_urls)
|
| 115 |
silent_video_path = os.path.join(temp_processing_dir, "silent_video.mp4")
|
|
|
|
|
|
|
| 116 |
input_pattern = shlex.quote(os.path.join(temp_processing_dir, 'image_%02d.jpg'))
|
| 117 |
framerate = 1 / duration_per_image
|
| 118 |
+
|
| 119 |
cmd_video = f"ffmpeg -framerate {framerate} -i {input_pattern} -c:v libx264 -pix_fmt yuv420p -r 25 -y {shlex.quote(silent_video_path)}"
|
| 120 |
await run_subprocess(cmd_video)
|
| 121 |
print("--- SUCCESS: SILENT VIDEO CREATED ---")
|