"""Track model.""" import uuid from datetime import datetime from typing import TYPE_CHECKING from sqlalchemy import String, Integer, ForeignKey from sqlalchemy.dialects.postgresql import UUID, JSONB from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base if TYPE_CHECKING: from app.models.artist import Artist from app.models.album import Album class Track(Base): """Track model representing music tracks.""" __tablename__ = "tracks" # Primary key id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True, ) # Basic info title: Mapped[str] = mapped_column( String(255), nullable=False, index=True, ) duration: Mapped[int | None] = mapped_column( Integer, comment="Duration in seconds", ) # Track position track_number: Mapped[int | None] = mapped_column( Integer, ) disc_number: Mapped[int] = mapped_column( Integer, default=1, ) # Cover art image_url: Mapped[str | None] = mapped_column( String(500), ) # Audio URLs audio_url: Mapped[str | None] = mapped_column( String(500), comment="Cached audio URL", ) audio_quality: Mapped[str | None] = mapped_column( String(20), comment="low, medium, high", ) # Genre and mood genre: Mapped[str | None] = mapped_column( String(100), ) mood: Mapped[str | None] = mapped_column( String(100), ) # Play count play_count: Mapped[int] = mapped_column( Integer, default=0, ) # Foreign keys artist_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("artists.id", ondelete="CASCADE"), nullable=True, index=True, ) album_id: Mapped[uuid.UUID | None] = mapped_column( UUID(as_uuid=True), ForeignKey("albums.id", ondelete="SET NULL"), nullable=True, index=True, ) # External IDs (unique indices) spotify_id: Mapped[str | None] = mapped_column( String(100), unique=True, index=True, ) youtube_id: Mapped[str | None] = mapped_column( String(100), unique=True, index=True, ) soundcloud_id: Mapped[str | None] = mapped_column( String(100), unique=True, index=True, ) # Additional metadata stored as JSON metadata: Mapped[dict] = mapped_column( JSONB, default=dict, ) # Timestamps created_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, nullable=False, ) updated_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False, ) # Relationships artist: Mapped["Artist"] = relationship( "Artist", back_populates="tracks", lazy="selectin", ) album: Mapped["Album"] = relationship( "Album", back_populates="tracks", lazy="selectin", ) def __repr__(self) -> str: return f"" def to_dict(self) -> dict: """Convert track model to dictionary.""" return { "id": str(self.id), "title": self.title, "duration": self.duration, "track_number": self.track_number, "disc_number": self.disc_number, "image_url": self.image_url, "audio_url": self.audio_url, "audio_quality": self.audio_quality, "genre": self.genre, "mood": self.mood, "play_count": self.play_count, "artist_id": str(self.artist_id) if self.artist_id else None, "album_id": str(self.album_id) if self.album_id else None, "spotify_id": self.spotify_id, "youtube_id": self.youtube_id, "soundcloud_id": self.soundcloud_id, "metadata": self.metadata, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(), }