from pydantic import BaseModel from typing import Optional, List from datetime import datetime from enum import Enum class MediaStatus(str, Enum): PENDING = "pending" UPLOADING = "uploading" SUCCESS = "success" FAILED = "failed" class MediaTagBase(BaseModel): tag_name: str tag_color: Optional[str] = "#3b82f6" sort_order: Optional[int] = 0 class MediaTagCreate(MediaTagBase): pass class MediaTagUpdate(BaseModel): tag_name: Optional[str] = None tag_color: Optional[str] = None sort_order: Optional[int] = None class MediaTagResponse(MediaTagBase): id: int company_code: str created_at: datetime updated_at: datetime class Config: from_attributes = True class MediaTagMappingResponse(BaseModel): id: int media_id: str tag_id: int created_at: datetime class Config: from_attributes = True class MediaBase(BaseModel): file_name: str r2_key: Optional[str] = "" r2_url: Optional[str] = "" thumbnail_r2_key: Optional[str] = "" thumbnail_r2_url: Optional[str] = "" media_type: Optional[str] = "image" mime_type: Optional[str] = "" file_size: Optional[int] = 0 width: Optional[int] = None height: Optional[int] = None duration: Optional[float] = None directory_id: Optional[int] = None status: Optional[MediaStatus] = MediaStatus.PENDING error_message: Optional[str] = None batch_id: Optional[str] = None tags: Optional[List[int]] = [] class MediaCreate(MediaBase): pass class MediaUpdate(BaseModel): file_name: Optional[str] = None directory_id: Optional[int] = None status: Optional[MediaStatus] = None error_message: Optional[str] = None tags: Optional[List[int]] = None class MediaResponse(MediaBase): id: int media_id: str company_code: str created_at: datetime updated_at: datetime is_deleted: Optional[bool] = False deleted_at: Optional[datetime] = None class Config: from_attributes = True class MediaBatchResponse(BaseModel): batch_id: str total: int successful: int failed: int results: List[dict] class MediaBatchCreate(BaseModel): files: List[str] directory_id: Optional[int] = None class MediaBatchDeleteRequest(BaseModel): media_ids: List[str] class MediaBatchUpdateRequest(BaseModel): media_ids: List[str] directory_id: Optional[int] = None tags: Optional[List[int]] = None class MediaBatchTagRequest(BaseModel): media_ids: List[str] tag_ids: List[int] class MediaSearchRequest(BaseModel): keyword: Optional[str] = None media_type: Optional[str] = None directory_id: Optional[int] = None tag_ids: Optional[List[int]] = None start_date: Optional[str] = None end_date: Optional[str] = None min_size: Optional[int] = None max_size: Optional[int] = None status: Optional[MediaStatus] = None class MediaDirectoryBase(BaseModel): name: str parent_id: Optional[int] = None path: Optional[str] = "" level: Optional[int] = 0 sort_order: Optional[int] = 0 cover_r2_key: Optional[str] = None cover_r2_url: Optional[str] = None description: Optional[str] = None is_public: Optional[bool] = False media_count: Optional[int] = 0 total_size: Optional[int] = 0 class MediaDirectoryCreate(MediaDirectoryBase): pass class MediaDirectoryUpdate(BaseModel): name: Optional[str] = None parent_id: Optional[int] = None sort_order: Optional[int] = None cover_r2_key: Optional[str] = None cover_r2_url: Optional[str] = None description: Optional[str] = None is_public: Optional[bool] = None class MediaDirectoryResponse(MediaDirectoryBase): id: int company_code: str created_at: datetime updated_at: datetime class Config: from_attributes = True