import os import subprocess import tempfile import shutil import asyncio from fastapi import FastAPI, UploadFile, File, HTTPException, BackgroundTasks from fastapi.responses import FileResponse from fastapi.middleware.cors import CORSMiddleware app = FastAPI(title="Background Remover API") # Configure CORS so the React app can call this API app.add_middleware( CORSMiddleware, allow_origins=["*"], # Adjust in production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) def cleanup_files(*file_paths): """Deletes temporary files after the response is sent.""" for file_path in file_paths: try: if file_path and os.path.exists(file_path): os.remove(file_path) print(f"Cleaned up {file_path}") except Exception as e: print(f"Error cleaning up {file_path}: {e}") @app.get("/health") def health_check(): return {"status": "ok"} @app.post("/api/v1/remove-background") async def remove_background( background_tasks: BackgroundTasks, file: UploadFile = File(...) ): if not file.filename: raise HTTPException(status_code=400, detail="No file uploaded") # We will use secure temp files temp_input_fd, temp_input_path = tempfile.mkstemp(suffix=".mp4") temp_output_mov_fd, temp_output_mov_path = tempfile.mkstemp(suffix=".mov") temp_final_webm_fd, temp_final_webm_path = tempfile.mkstemp(suffix=".webm") os.close(temp_input_fd) os.close(temp_output_mov_fd) os.close(temp_final_webm_fd) try: # 1. Save uploaded video to temp file with open(temp_input_path, "wb") as buffer: shutil.copyfileobj(file.file, buffer) print(f"Saved uploaded file to {temp_input_path}") # 2. Run backgroundremover # backgroundremover -i input.mp4 -mk -o output.mov cmd_bg_remover = [ "backgroundremover", "-i", temp_input_path, "-mk", # This flag tells it to create an alpha matte video (.mov) "-o", temp_output_mov_path ] print("Starting background removal process...") process_bg = await asyncio.create_subprocess_exec( *cmd_bg_remover, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await process_bg.communicate() if process_bg.returncode != 0: print(f"backgroundremover error: {stderr.decode()}") raise HTTPException(status_code=500, detail="Error processing video background removal.") print("Background removal finished. Starting WebM conversion...") # 3. Convert .mov to .webm with VP9 and yuva420p (alpha channel) # ffmpeg -i output.mov -c:v libvpx-vp9 -pix_fmt yuva420p -auto-alt-ref 0 final.webm cmd_ffmpeg = [ "ffmpeg", "-y", # Overwrite output "-i", temp_output_mov_path, "-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", "-auto-alt-ref", "0", temp_final_webm_path ] process_ffmpeg = await asyncio.create_subprocess_exec( *cmd_ffmpeg, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout_ff, stderr_ff = await process_ffmpeg.communicate() if process_ffmpeg.returncode != 0: print(f"ffmpeg error: {stderr_ff.decode()}") raise HTTPException(status_code=500, detail="Error converting video to WebM.") print("WebM conversion finished successfully.") # 4. Schedule cleanup of ALL temporary files after response is sent background_tasks.add_task( cleanup_files, temp_input_path, temp_output_mov_path, temp_final_webm_path ) # 5. Return the file return FileResponse( temp_final_webm_path, media_type="video/webm", filename=f"transparent_{file.filename.split('.')[0]}.webm" ) except Exception as e: # If an error occurs, clean up immediately cleanup_files(temp_input_path, temp_output_mov_path, temp_final_webm_path) if isinstance(e, HTTPException): raise e raise HTTPException(status_code=500, detail=str(e)) @app.post("/api/v1/remove-image-background") async def remove_image_background( background_tasks: BackgroundTasks, file: UploadFile = File(...) ): if not file.filename: raise HTTPException(status_code=400, detail="No file uploaded") # We will use secure temp files temp_input_fd, temp_input_path = tempfile.mkstemp(suffix=".png") temp_output_png_fd, temp_output_png_path = tempfile.mkstemp(suffix=".png") os.close(temp_input_fd) os.close(temp_output_png_fd) try: # 1. Save uploaded image to temp file with open(temp_input_path, "wb") as buffer: shutil.copyfileobj(file.file, buffer) print(f"Saved uploaded image to {temp_input_path}") # 2. Run backgroundremover for image # backgroundremover -i input.jpg -o output.png cmd_bg_remover = [ "backgroundremover", "-i", temp_input_path, "-o", temp_output_png_path ] print("Starting image background removal process...") process_bg = await asyncio.create_subprocess_exec( *cmd_bg_remover, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await process_bg.communicate() if process_bg.returncode != 0: print(f"backgroundremover error: {stderr.decode()}") raise HTTPException(status_code=500, detail="Error processing image background removal.") print("Image background removal finished successfully.") # 3. Schedule cleanup of ALL temporary files after response is sent background_tasks.add_task( cleanup_files, temp_input_path, temp_output_png_path ) # 4. Return the file return FileResponse( temp_output_png_path, media_type="image/png", filename=f"transparent_{file.filename.split('.')[0]}.png" ) except Exception as e: # If an error occurs, clean up immediately cleanup_files(temp_input_path, temp_output_png_path) if isinstance(e, HTTPException): raise e raise HTTPException(status_code=500, detail=str(e))