106 lines
3.2 KiB
Python
106 lines
3.2 KiB
Python
from datetime import datetime
|
|
from typing import List, Optional
|
|
from sqlalchemy import String, Integer, ForeignKey, DateTime, Enum as SQLEnum, func
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
import enum
|
|
|
|
from .database import Base
|
|
|
|
|
|
class Difficulty(str, enum.Enum):
|
|
EASY = "easy"
|
|
MEDIUM = "medium"
|
|
HARD = "hard"
|
|
|
|
|
|
class Opening(Base):
|
|
"""Anime opening entity."""
|
|
__tablename__ = "openings"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
anime_name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
|
op_number: Mapped[str] = mapped_column(String(20), nullable=False) # e.g., "OP1", "ED2"
|
|
song_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
audio_file: Mapped[str] = mapped_column(String(512), nullable=False) # S3 key
|
|
|
|
# Usage tracking
|
|
last_usage: Mapped[Optional[datetime]] = mapped_column(
|
|
DateTime(timezone=True),
|
|
nullable=True,
|
|
default=None
|
|
)
|
|
|
|
# Timestamps
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
server_default=func.now()
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
server_default=func.now(),
|
|
onupdate=func.now()
|
|
)
|
|
|
|
# Relationships
|
|
posters: Mapped[List["OpeningPoster"]] = relationship(
|
|
"OpeningPoster",
|
|
back_populates="opening",
|
|
cascade="all, delete-orphan"
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<Opening {self.anime_name} - {self.op_number}>"
|
|
|
|
|
|
class OpeningPoster(Base):
|
|
"""Poster image for an opening."""
|
|
__tablename__ = "opening_posters"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
opening_id: Mapped[int] = mapped_column(
|
|
Integer,
|
|
ForeignKey("openings.id", ondelete="CASCADE"),
|
|
nullable=False
|
|
)
|
|
poster_file: Mapped[str] = mapped_column(String(512), nullable=False) # S3 key
|
|
is_default: Mapped[bool] = mapped_column(default=False)
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
server_default=func.now()
|
|
)
|
|
|
|
# Relationships
|
|
opening: Mapped["Opening"] = relationship("Opening", back_populates="posters")
|
|
|
|
def __repr__(self):
|
|
return f"<OpeningPoster {self.poster_file}>"
|
|
|
|
|
|
class Background(Base):
|
|
"""Background video entity."""
|
|
__tablename__ = "backgrounds"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
video_file: Mapped[str] = mapped_column(String(512), nullable=False) # S3 key
|
|
difficulty: Mapped[Difficulty] = mapped_column(
|
|
SQLEnum(Difficulty, native_enum=False),
|
|
nullable=False,
|
|
default=Difficulty.MEDIUM
|
|
)
|
|
|
|
# Timestamps
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
server_default=func.now()
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True),
|
|
server_default=func.now(),
|
|
onupdate=func.now()
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<Background {self.name} ({self.difficulty})>"
|