from datetime import datetime from typing import TYPE_CHECKING, List, Optional from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database.session import Base if TYPE_CHECKING: from app.home.models import Project from app.lyric.models import Lyric from app.video.models import Video class Song(Base): """ 노래 테이블 AI를 통해 생성된 노래 정보를 저장합니다. 가사를 기반으로 생성됩니다. Attributes: id: 고유 식별자 (자동 증가) project_id: 연결된 Project의 id (외래키) lyric_id: 연결된 Lyric의 id (외래키) task_id: 노래 생성 작업의 고유 식별자 (UUID 형식) suno_task_id: Suno API 작업 고유 식별자 (선택) status: 처리 상태 (pending, processing, completed, failed 등) song_prompt: 노래 생성에 사용된 프롬프트 song_result_url: 생성 결과 URL (선택) language: 출력 언어 created_at: 생성 일시 (자동 설정) Relationships: project: 연결된 Project lyric: 연결된 Lyric videos: 이 노래를 사용한 영상 결과 목록 """ __tablename__ = "song" __table_args__ = ( { "mysql_engine": "InnoDB", "mysql_charset": "utf8mb4", "mysql_collate": "utf8mb4_unicode_ci", }, ) id: Mapped[int] = mapped_column( Integer, primary_key=True, nullable=False, autoincrement=True, comment="고유 식별자", ) project_id: Mapped[int] = mapped_column( Integer, ForeignKey("project.id", ondelete="CASCADE"), nullable=False, index=True, comment="연결된 Project의 id", ) lyric_id: Mapped[int] = mapped_column( Integer, ForeignKey("lyric.id", ondelete="CASCADE"), nullable=False, index=True, comment="연결된 Lyric의 id", ) task_id: Mapped[str] = mapped_column( String(36), nullable=False, comment="노래 생성 작업 고유 식별자 (UUID)", ) suno_task_id: Mapped[Optional[str]] = mapped_column( String(64), nullable=True, comment="Suno API 작업 고유 식별자", ) status: Mapped[str] = mapped_column( String(50), nullable=False, comment="처리 상태 (processing, completed, failed)", ) song_prompt: Mapped[str] = mapped_column( Text, nullable=False, comment="노래 생성에 사용된 프롬프트", ) song_result_url: Mapped[Optional[str]] = mapped_column( String(2048), nullable=True, comment="노래 결과 URL", ) language: Mapped[str] = mapped_column( String(50), nullable=False, default="Korean", comment="출력 언어 (Korean, English, Chinese, Japanese, Thai, Vietnamese)", ) created_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.now(), comment="생성 일시", ) # Relationships project: Mapped["Project"] = relationship( "Project", back_populates="songs", ) lyric: Mapped["Lyric"] = relationship( "Lyric", back_populates="songs", ) videos: Mapped[List["Video"]] = relationship( "Video", back_populates="song", cascade="all, delete-orphan", lazy="selectin", ) def __repr__(self) -> str: def truncate(value: str | None, max_len: int = 10) -> str: if value is None: return "None" return (value[:max_len] + "...") if len(value) > max_len else value return ( f"" )