import uuid from datetime import datetime from sqlalchemy import ( Column, DateTime, Enum, Float, ForeignKey, Index, String, Text, func, ) from sqlalchemy.dialects.postgresql import JSONB, UUID from sqlalchemy.orm import DeclarativeBase, relationship class Base(DeclarativeBase): pass class Job(Base): __tablename__ = "jobs" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) source_type = Column(Enum("youtube", "upload", name="source_type"), nullable=False) source_url = Column(Text) source_filename = Column(Text) title = Column(Text) duration = Column(Float) status = Column( Enum( "pending", "downloading", "transcribing", "analyzing", "extracting", "complete", "failed", name="job_status", ), nullable=False, default="pending", ) progress = Column(Float, nullable=False, default=0.0) stage_message = Column(Text) error_message = Column(Text) media_path = Column(Text) transcript = Column(JSONB) scene_boundaries = Column(JSONB) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) clips = relationship("Clip", back_populates="job", cascade="all, delete-orphan") class Clip(Base): __tablename__ = "clips" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) job_id = Column( UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="CASCADE"), nullable=False ) title = Column(Text, nullable=False) start_time = Column(Float, nullable=False) end_time = Column(Float, nullable=False) virality_score = Column(Float, nullable=False, default=0.0) category = Column(Text) reasoning = Column(Text) transcript_segment = Column(Text) thumbnail_path = Column(Text) raw_clip_path = Column(Text) created_at = Column(DateTime(timezone=True), server_default=func.now()) job = relationship("Job", back_populates="clips") renders = relationship( "RenderRequest", back_populates="clip", cascade="all, delete-orphan" ) class RenderRequest(Base): __tablename__ = "render_requests" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) clip_id = Column( UUID(as_uuid=True), ForeignKey("clips.id", ondelete="CASCADE"), nullable=False ) aspect_ratio = Column( Enum("16:9", "9:16", "1:1", "4:5", name="aspect_ratio"), nullable=False, default="9:16", ) subtitle_style = Column(String, nullable=False, default="tiktok") status = Column( Enum("pending", "rendering", "complete", "failed", name="render_status"), nullable=False, default="pending", ) progress = Column(Float, nullable=False, default=0.0) output_path = Column(Text) error_message = Column(Text) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) clip = relationship("Clip", back_populates="renders")