feat: initial commit of AI Comic Generator
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import shutil
|
||||
import os
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlmodel import Session
|
||||
from app.core.database import get_session
|
||||
from app.models.models import Project
|
||||
from app.services.image_service import split_comic_page
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/{project_id}")
|
||||
def export_project(
|
||||
project_id: str,
|
||||
split_images: bool = False,
|
||||
session: Session = Depends(get_session)
|
||||
):
|
||||
project = session.get(Project, project_id)
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="Project not found")
|
||||
|
||||
# Check if any images generated
|
||||
has_images = any(item.image_url for item in project.storyboard_items) or any(c.image_url for c in project.characters)
|
||||
if not has_images:
|
||||
raise HTTPException(status_code=400, detail="No images generated yet. Cannot export.")
|
||||
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
project_static_dir = os.path.join(base_dir, "static", project_id)
|
||||
export_dir = os.path.join(project_static_dir, "export")
|
||||
|
||||
if os.path.exists(export_dir):
|
||||
shutil.rmtree(export_dir)
|
||||
os.makedirs(export_dir)
|
||||
|
||||
# Export Characters
|
||||
chars_dir = os.path.join(export_dir, "characters")
|
||||
os.makedirs(chars_dir)
|
||||
for char in project.characters:
|
||||
if char.image_url:
|
||||
# Resolve absolute path from relative URL
|
||||
# URL: /static/{project_id}/characters/xxx.png
|
||||
# Path: backend/static/{project_id}/characters/xxx.png
|
||||
# We can construct it directly if we know the structure, but let's parse url
|
||||
rel_path = char.image_url.lstrip("/") # static/...
|
||||
local_path = os.path.join(base_dir, rel_path.replace("/", os.sep))
|
||||
|
||||
if os.path.exists(local_path):
|
||||
shutil.copy(local_path, os.path.join(chars_dir, f"{char.name}.png"))
|
||||
|
||||
panels_dir = os.path.join(export_dir, "panels")
|
||||
if split_images:
|
||||
os.makedirs(panels_dir)
|
||||
|
||||
# Sort items
|
||||
items = sorted(project.storyboard_items, key=lambda x: x.sequence)
|
||||
|
||||
for item in items:
|
||||
if item.image_url:
|
||||
rel_path = item.image_url.lstrip("/")
|
||||
local_path = os.path.join(base_dir, rel_path.replace("/", os.sep))
|
||||
|
||||
if os.path.exists(local_path):
|
||||
shutil.copy(local_path, os.path.join(export_dir, f"comic_part_{item.sequence}.png"))
|
||||
|
||||
if split_images:
|
||||
with open(local_path, "rb") as f:
|
||||
img_bytes = f.read()
|
||||
|
||||
try:
|
||||
panels = split_comic_page(img_bytes)
|
||||
for idx, panel_bytes in enumerate(panels):
|
||||
p_name = f"panel_{item.sequence}_{idx+1}.png"
|
||||
with open(os.path.join(panels_dir, p_name), "wb") as f:
|
||||
f.write(panel_bytes)
|
||||
except Exception as e:
|
||||
print(f"Failed to split panel {item.id}: {e}")
|
||||
|
||||
zip_path_base = os.path.join(project_static_dir, "export_archive")
|
||||
shutil.make_archive(zip_path_base, 'zip', export_dir)
|
||||
|
||||
return {"download_url": f"/static/{project_id}/export_archive.zip"}
|
||||
Reference in New Issue
Block a user