from datetime import datetime from sqlalchemy import ( Boolean, DateTime, ForeignKey, Integer, String, Table, Column, UniqueConstraint ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base media_item_tags = Table( "media_item_tags", Base.metadata, Column("media_item_id", Integer, ForeignKey("media_items.id", ondelete="CASCADE"), primary_key=True), Column("tag_id", Integer, ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True), ) class Library(Base): __tablename__ = "libraries" id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) path: Mapped[str] = mapped_column(String, nullable=False, unique=True) items: Mapped[list["MediaItem"]] = relationship( "MediaItem", back_populates="library", cascade="all, delete-orphan" ) class MediaItem(Base): __tablename__ = "media_items" __table_args__ = (UniqueConstraint("library_id", "rel_path"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) library_id: Mapped[int] = mapped_column(Integer, ForeignKey("libraries.id", ondelete="CASCADE"), nullable=False) rel_path: Mapped[str] = mapped_column(String, nullable=False) filename: Mapped[str] = mapped_column(String, nullable=False) file_hash: Mapped[str | None] = mapped_column(String) media_type: Mapped[str] = mapped_column(String, nullable=False) # 'image' | 'video' size_bytes: Mapped[int | None] = mapped_column(Integer) missing: Mapped[bool] = mapped_column(Boolean, default=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) library: Mapped["Library"] = relationship("Library", back_populates="items") tags: Mapped[list["Tag"]] = relationship("Tag", secondary=media_item_tags, back_populates="items") class Tag(Base): __tablename__ = "tags" __table_args__ = (UniqueConstraint("name", "category"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String, nullable=False) category: Mapped[str] = mapped_column(String, nullable=False) items: Mapped[list["MediaItem"]] = relationship("MediaItem", secondary=media_item_tags, back_populates="tags")