clip-forge/backend/app/models.py

111 lines
3.2 KiB
Python

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")