from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Boolean from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum import json from typing import Optional, List from app.database import Base class AssetType(str, enum.Enum): IMAGE = "image" AUDIO = "audio" VIDEO = "video" DOCUMENT = "document" MODEL_3D = "model_3d" OTHER = "other" class Asset(Base): __tablename__ = "assets" id = Column(Integer, primary_key=True, index=True) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) asset_type = Column( ENUM(AssetType, name="asset_type", create_type=False), nullable=False, ) file_path = Column(String(512), nullable=False) file_size = Column(Integer, nullable=True) mime_type = Column(String(128), nullable=True) metadata_json = Column(Text, nullable=True) world_id = Column(Integer, ForeignKey("worlds.id"), nullable=True) owner_id = Column(Integer, ForeignKey("users.id"), nullable=False) is_public = Column(Boolean, default=False, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) owner = relationship("User", back_populates="assets") world = relationship("World", back_populates="assets") class GLTFMetadata(BaseModel): """Pydantic schema for glTF asset metadata.""" mesh_count: Optional[int] = None vertex_count: Optional[int] = None has_animation: bool = False texture_refs: Optional[List[str]] = None tags: Optional[List[str]] = None model_config = {"from_attributes": True} def parse_metadata_json(metadata_text: Optional[str]) -> dict: """Parse the metadata_json text field into a dict, handling None gracefully.""" if not metadata_text: return {} try: return json.loads(metadata_text) except (json.JSONDecodeError, TypeError): return {} def serialize_metadata(metadata: dict) -> Optional[str]: """Serialize metadata dict to JSON string for storage.""" if not metadata: return None return json.dumps(metadata)