# app/modules/cms/models/media.py """ Generic media file model for store media library. This is a consumer-agnostic media storage model. MediaFile provides store-uploaded media files (images, documents, videos) without knowing what entities will use them. Modules that need media (catalog, art-gallery, etc.) define their own association tables that reference MediaFile. For product-media associations: from app.modules.catalog.models import ProductMedia Files are stored in store-specific directories: uploads/stores/{store_id}/{folder}/{filename} """ from sqlalchemy import ( Boolean, Column, ForeignKey, Index, Integer, String, Text, ) from sqlalchemy.dialects.sqlite import JSON from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class MediaFile(Base, TimestampMixin): """Store media file record. Stores metadata about uploaded files. Actual files are stored in the filesystem at uploads/stores/{store_id}/{folder}/ """ __tablename__ = "media_files" id = Column(Integer, primary_key=True, index=True) store_id = Column(Integer, ForeignKey("stores.id"), nullable=False) # File identification filename = Column(String(255), nullable=False) # Stored filename (UUID-based) original_filename = Column(String(255)) # Original uploaded filename file_path = Column(String(500), nullable=False) # Relative path from uploads/ # File properties media_type = Column(String(20), nullable=False) # image, video, document mime_type = Column(String(100)) file_size = Column(Integer) # bytes # Image/video dimensions width = Column(Integer) height = Column(Integer) # Thumbnail (for images/videos) thumbnail_path = Column(String(500)) # Metadata alt_text = Column(String(500)) description = Column(Text) folder = Column(String(100), default="general") # products, general, etc. tags = Column(JSON) # List of tags for categorization extra_metadata = Column(JSON) # Additional metadata (EXIF, etc.) # Status is_optimized = Column(Boolean, default=False) optimized_size = Column(Integer) # Size after optimization # Usage tracking usage_count = Column(Integer, default=0) # How many times used # Relationships store = relationship("Store", back_populates="media_files") # Note: Consumer-specific associations (ProductMedia, etc.) are defined # in their respective modules. CMS doesn't know about specific consumers. __table_args__ = ( Index("idx_media_store_id", "store_id"), Index("idx_media_store_folder", "store_id", "folder"), Index("idx_media_store_type", "store_id", "media_type"), Index("idx_media_filename", "filename"), ) def __repr__(self): return ( f"" ) @property def file_url(self) -> str: """Get the public URL for this file.""" return f"/uploads/{self.file_path}" @property def thumbnail_url(self) -> str | None: """Get the thumbnail URL if available.""" if self.thumbnail_path: return f"/uploads/{self.thumbnail_path}" return None @property def is_image(self) -> bool: """Check if this is an image file.""" return self.media_type == "image" @property def is_video(self) -> bool: """Check if this is a video file.""" return self.media_type == "video" @property def is_document(self) -> bool: """Check if this is a document file.""" return self.media_type == "document" __all__ = ["MediaFile"]