import enum import uuid from datetime import date, datetime from sqlalchemy import ( Date, DateTime, Enum, Float, ForeignKey, Integer, String, Text, UniqueConstraint, func, ) from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import Mapped, mapped_column from app.models.base import Base, TimestampMixin, UUIDPKMixin class FingerprintKind(str, enum.Enum): phash = "phash" oshash = "oshash" md5 = "md5" class Scene(UUIDPKMixin, TimestampMixin, Base): __tablename__ = "scenes" title: Mapped[str] = mapped_column(String, nullable=False) title_normalized: Mapped[str] = mapped_column(String, nullable=False, index=True) slug: Mapped[str | None] = mapped_column(String, index=True) release_date: Mapped[date | None] = mapped_column(Date, index=True) studio_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("studios.id", ondelete="SET NULL"), index=True ) duration_sec: Mapped[int | None] = mapped_column(Integer) description: Mapped[str | None] = mapped_column(Text) code: Mapped[str | None] = mapped_column(String(128), index=True) director: Mapped[str | None] = mapped_column(String(256)) class SceneExternalRef(Base): __tablename__ = "scene_external_refs" source_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("sources.id", ondelete="CASCADE"), primary_key=True ) external_id: Mapped[str] = mapped_column(String, primary_key=True) scene_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("scenes.id", ondelete="CASCADE"), nullable=False, index=True ) confidence: Mapped[float] = mapped_column(Float, nullable=False, default=1.0, server_default="1.0") url: Mapped[str | None] = mapped_column(String(1024)) first_seen: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) last_seen: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) class SceneFingerprint(Base): """Fingerprinty zaciągnięte ze źródeł (TPDB/StashDB) — nie liczone lokalnie.""" __tablename__ = "scene_fingerprints" __table_args__ = (UniqueConstraint("scene_id", "kind", "value"),) id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, server_default=func.gen_random_uuid() ) scene_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("scenes.id", ondelete="CASCADE"), nullable=False, index=True ) kind: Mapped[FingerprintKind] = mapped_column(Enum(FingerprintKind, name="fingerprint_kind")) value: Mapped[str] = mapped_column(String(128), nullable=False, index=True) source_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("sources.id", ondelete="SET NULL") ) class ScenePerformer(Base): __tablename__ = "scene_performers" scene_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("scenes.id", ondelete="CASCADE"), primary_key=True ) performer_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("performers.id", ondelete="CASCADE"), primary_key=True ) role: Mapped[str | None] = mapped_column(String(64)) position: Mapped[int | None] = mapped_column(Integer) as_alias: Mapped[str | None] = mapped_column(String(256)) class SceneTag(Base): __tablename__ = "scene_tags" scene_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("scenes.id", ondelete="CASCADE"), primary_key=True ) tag_id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("tags.id", ondelete="CASCADE"), primary_key=True ) source_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("sources.id", ondelete="SET NULL") )