Files
Project_Animatix/backend/app/api/routes/assets.py
2026-04-17 19:11:57 +05:30

121 lines
4.0 KiB
Python

from datetime import datetime, timedelta, timezone
from typing import List, Optional
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from sqlalchemy.orm import Session
from app.core.deps import get_current_user
from app.db.session import get_db
from app.models import Asset, User
from app.schemas import AssetResponse, AssetTrashRequest
from app.services.storage import asset_storage
router = APIRouter(prefix="/api/assets", tags=["assets"])
ALLOWED_TYPES = {
"image": ["image/jpeg", "image/png", "image/webp"],
"video": ["video/mp4", "video/webm", "video/quicktime"],
"audio": ["audio/mpeg", "audio/mp4", "audio/wav", "audio/ogg", "audio/x-wav"],
"pose_sheet": ["image/jpeg", "image/png", "image/webp"],
}
MAX_SIZE_BYTES = 500 * 1024 * 1024
@router.post("/upload", response_model=AssetResponse, status_code=201)
async def upload_asset(
file: UploadFile = File(...),
asset_type: str = Form(...),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if asset_type not in ALLOWED_TYPES:
raise HTTPException(400, f"asset_type must be one of {list(ALLOWED_TYPES.keys())}")
mime = file.content_type or ""
if mime not in ALLOWED_TYPES[asset_type]:
raise HTTPException(400, f"Unsupported mime type {mime} for {asset_type}")
subfolder = f"{current_user.id}/{asset_type}"
storage_path, size_bytes = await asset_storage.save_upload(file, subfolder)
if size_bytes > MAX_SIZE_BYTES:
raise HTTPException(413, "File too large (max 500MB)")
thumbnail_path = None
width = height = None
duration_seconds = None
if asset_type in ("image", "pose_sheet"):
thumbnail_path = asset_storage.generate_thumbnail(storage_path, f"{current_user.id}/thumbs")
try:
from PIL import Image
abs_path = asset_storage.absolute_path(storage_path)
with Image.open(abs_path) as image:
width, height = image.size
except Exception:
pass
elif asset_type == "video":
thumbnail_path = asset_storage.generate_video_thumbnail(storage_path, f"{current_user.id}/thumbs")
duration_seconds = asset_storage.detect_duration_seconds(storage_path)
else:
duration_seconds = asset_storage.detect_duration_seconds(storage_path)
asset = Asset(
owner_id=current_user.id,
asset_type=asset_type,
mime_type=mime,
original_filename=file.filename or "upload",
storage_path=storage_path,
thumbnail_path=thumbnail_path,
size_bytes=size_bytes,
width=width,
height=height,
duration_seconds=duration_seconds,
)
db.add(asset)
db.commit()
db.refresh(asset)
return asset
@router.get("/", response_model=List[AssetResponse])
def list_assets(
asset_type: Optional[str] = None,
include_trashed: bool = False,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
query = db.query(Asset).filter(Asset.owner_id == current_user.id)
if not include_trashed:
query = query.filter(Asset.is_trashed.is_(False))
if asset_type:
query = query.filter(Asset.asset_type == asset_type)
return query.order_by(Asset.created_at.desc()).all()
@router.post("/trash", status_code=200)
def move_assets_to_trash(
payload: AssetTrashRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
if not payload.asset_ids:
raise HTTPException(400, "No asset ids provided")
assets = (
db.query(Asset)
.filter(Asset.owner_id == current_user.id, Asset.id.in_(payload.asset_ids))
.all()
)
if not assets:
raise HTTPException(404, "No matching assets found")
delete_after_at = datetime.now(timezone.utc) + timedelta(days=30)
for asset in assets:
asset.is_trashed = True
asset.delete_after_at = delete_after_at
db.commit()
return {
"moved_to_trash": len(assets),
"delete_after_at": delete_after_at.isoformat(),
}