143 lines
5.2 KiB
Python
143 lines
5.2 KiB
Python
"""Background Remover Service - Clipping Magic API"""
|
|
import httpx
|
|
import os
|
|
import base64
|
|
from uuid import uuid4
|
|
from datetime import datetime
|
|
|
|
from app.database import SessionLocal
|
|
from app.models.job import Job
|
|
from app.models.asset import Asset
|
|
from app.config import settings
|
|
|
|
|
|
async def remove_background(job_id: str):
|
|
"""Remove background from image using Clipping Magic"""
|
|
db = SessionLocal()
|
|
try:
|
|
job = db.query(Job).filter(Job.id == job_id).first()
|
|
if not job:
|
|
return
|
|
|
|
input_data = job.input_data
|
|
input_asset_ids = job.input_asset_ids
|
|
|
|
if not input_asset_ids:
|
|
raise ValueError("No input asset provided")
|
|
|
|
input_asset = db.query(Asset).filter(Asset.id == input_asset_ids[0]).first()
|
|
if not input_asset:
|
|
raise ValueError("Input asset not found")
|
|
|
|
job.progress = 10
|
|
job.api_provider = "clipping_magic"
|
|
db.commit()
|
|
|
|
# Read input image
|
|
with open(input_asset.file_path, "rb") as f:
|
|
image_data = f.read()
|
|
|
|
output_format = input_data.get("output_format", "png")
|
|
|
|
job.progress = 20
|
|
db.commit()
|
|
|
|
# Call Clipping Magic API
|
|
async with httpx.AsyncClient(timeout=120) as client:
|
|
# Use API ID and Secret for HTTP Basic Auth
|
|
api_id = settings.clipping_magic_api_id or settings.clipping_magic_api_key
|
|
api_secret = settings.clipping_magic_api_secret or ""
|
|
|
|
response = await client.post(
|
|
"https://clippingmagic.com/api/v1/images",
|
|
auth=(api_id, api_secret),
|
|
files={"image": (input_asset.original_filename, image_data, input_asset.mime_type)},
|
|
data={
|
|
"format": "clipping_path_tiff" if output_format == "tiff" else ("result" if output_format == "png" else "result")
|
|
}
|
|
)
|
|
response.raise_for_status()
|
|
|
|
content_type = response.headers.get("content-type", "")
|
|
|
|
if "application/json" in content_type:
|
|
# Flow 1: API returns JSON with image ID (async processing or default)
|
|
result = response.json()
|
|
image_id = result.get("image", {}).get("id")
|
|
|
|
job.progress = 50
|
|
db.commit()
|
|
|
|
if image_id:
|
|
# Download the result
|
|
download_response = await client.get(
|
|
f"https://clippingmagic.com/api/v1/images/{image_id}",
|
|
auth=(api_id, api_secret),
|
|
params={"format": "clipping_path_tiff" if output_format == "tiff" else "result"}
|
|
)
|
|
download_response.raise_for_status()
|
|
processed_data = download_response.content
|
|
else:
|
|
# Flow 2: API returns the image directly (synchronous processing requested via format='result')
|
|
processed_data = response.content
|
|
job.progress = 70
|
|
db.commit()
|
|
|
|
job.progress = 80
|
|
db.commit()
|
|
|
|
# Save output
|
|
ext = "tiff" if output_format == "tiff" else ("png" if output_format == "png" else "webp")
|
|
filename = f"nobg_{uuid4()}.{ext}"
|
|
storage_path = os.path.join(settings.storage_path, "images")
|
|
os.makedirs(storage_path, exist_ok=True)
|
|
file_path = os.path.join(storage_path, filename)
|
|
|
|
with open(file_path, "wb") as f:
|
|
f.write(processed_data)
|
|
|
|
# Create output asset
|
|
output_asset = Asset(
|
|
user_id=job.user_id,
|
|
project_id=job.project_id,
|
|
original_filename=filename,
|
|
stored_filename=filename,
|
|
file_path=file_path,
|
|
file_type="image",
|
|
mime_type=f"image/{ext}",
|
|
file_size_bytes=len(processed_data),
|
|
width=input_asset.width,
|
|
height=input_asset.height,
|
|
source_module="background_remover",
|
|
source_job_id=job.id,
|
|
parent_asset_id=input_asset.id,
|
|
asset_metadata={"output_format": output_format}
|
|
)
|
|
db.add(output_asset)
|
|
db.commit()
|
|
db.refresh(output_asset)
|
|
|
|
job.output_asset_ids = [output_asset.id]
|
|
job.output_data = {"asset_id": str(output_asset.id), "file_path": file_path}
|
|
|
|
# Delete from Clipping Magic if we have an image_id (Only needed for Flow 1)
|
|
if "application/json" in content_type and 'image_id' in locals() and image_id:
|
|
try:
|
|
await client.post(
|
|
f"https://clippingmagic.com/api/v1/images/{image_id}/delete",
|
|
auth=(api_id, api_secret)
|
|
)
|
|
except Exception as e:
|
|
pass # Ignore cleanup errors
|
|
|
|
job.progress = 100
|
|
job.status = "completed"
|
|
job.completed_at = datetime.utcnow()
|
|
db.commit()
|
|
|
|
except Exception as e:
|
|
job.status = "failed"
|
|
job.error_message = str(e)
|
|
db.commit()
|
|
finally:
|
|
db.close()
|