Goon — self-hosted aggregator for adult-content scene metadata. Indexes scenes from TPDB, StashDB, and 30+ public adult tube sites. Cross-source deduplication via perceptual hash + Levenshtein distance. FastAPI backend + APScheduler worker + React Native (Expo) mobile client. FOSS, ad-free, donation-funded. See README for details.
40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
import enum
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import DateTime, Enum, ForeignKey, Integer, func
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.models.base import Base, UUIDPKMixin
|
|
|
|
|
|
class IngestStatus(str, enum.Enum):
|
|
running = "running"
|
|
success = "success"
|
|
partial = "partial"
|
|
failed = "failed"
|
|
|
|
|
|
class IngestRun(UUIDPKMixin, Base):
|
|
__tablename__ = "ingest_runs"
|
|
|
|
source_id: Mapped[uuid.UUID] = mapped_column(
|
|
UUID(as_uuid=True), ForeignKey("sources.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
started_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
)
|
|
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
status: Mapped[IngestStatus] = mapped_column(
|
|
Enum(IngestStatus, name="ingest_status"),
|
|
nullable=False,
|
|
default=IngestStatus.running,
|
|
server_default=IngestStatus.running.value,
|
|
)
|
|
records_seen: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
|
records_new: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
|
records_updated: Mapped[int] = mapped_column(
|
|
Integer, nullable=False, default=0, server_default="0"
|
|
)
|
|
errors: Mapped[dict | None] = mapped_column(JSONB)
|