169 lines
5.6 KiB
Python
169 lines
5.6 KiB
Python
"""Stream model for storing streaming service links to sounds."""
|
|
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING, Optional
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
|
|
from app.database import db
|
|
|
|
if TYPE_CHECKING:
|
|
from app.models.sound import Sound
|
|
|
|
|
|
class Stream(db.Model):
|
|
"""Model for storing streaming service information linked to sounds."""
|
|
|
|
__tablename__ = "stream"
|
|
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
|
service: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
service_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
sound_id: Mapped[int] = mapped_column(
|
|
Integer, ForeignKey("sound.id"), nullable=False
|
|
)
|
|
url: Mapped[str] = mapped_column(Text, nullable=False)
|
|
title: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
|
|
track: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
|
|
artist: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
album: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
genre: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
|
status: Mapped[str] = mapped_column(String(50), nullable=False, default="active")
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
default=lambda: datetime.now(tz=ZoneInfo("UTC")),
|
|
nullable=False,
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime,
|
|
default=lambda: datetime.now(tz=ZoneInfo("UTC")),
|
|
onupdate=lambda: datetime.now(tz=ZoneInfo("UTC")),
|
|
nullable=False,
|
|
)
|
|
|
|
# Relationships
|
|
sound: Mapped["Sound"] = relationship("Sound", back_populates="streams")
|
|
|
|
def __repr__(self) -> str:
|
|
"""String representation of the stream."""
|
|
return f"<Stream(id={self.id}, service='{self.service}', service_id='{self.service_id}', sound_id={self.sound_id})>"
|
|
|
|
def to_dict(self) -> dict:
|
|
"""Convert stream to dictionary representation."""
|
|
return {
|
|
"id": self.id,
|
|
"service": self.service,
|
|
"service_id": self.service_id,
|
|
"sound_id": self.sound_id,
|
|
"url": self.url,
|
|
"title": self.title,
|
|
"track": self.track,
|
|
"artist": self.artist,
|
|
"album": self.album,
|
|
"genre": self.genre,
|
|
"status": self.status,
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
|
}
|
|
|
|
@classmethod
|
|
def create_stream(
|
|
cls,
|
|
service: str,
|
|
service_id: str,
|
|
sound_id: int,
|
|
url: str,
|
|
title: Optional[str] = None,
|
|
track: Optional[str] = None,
|
|
artist: Optional[str] = None,
|
|
album: Optional[str] = None,
|
|
genre: Optional[str] = None,
|
|
status: str = "active",
|
|
commit: bool = True,
|
|
) -> "Stream":
|
|
"""Create a new stream record."""
|
|
stream = cls(
|
|
service=service,
|
|
service_id=service_id,
|
|
sound_id=sound_id,
|
|
url=url,
|
|
title=title,
|
|
track=track,
|
|
artist=artist,
|
|
album=album,
|
|
genre=genre,
|
|
status=status,
|
|
)
|
|
|
|
db.session.add(stream)
|
|
if commit:
|
|
db.session.commit()
|
|
|
|
return stream
|
|
|
|
@classmethod
|
|
def find_by_service_and_id(
|
|
cls, service: str, service_id: str
|
|
) -> Optional["Stream"]:
|
|
"""Find stream by service and service_id."""
|
|
return cls.query.filter_by(service=service, service_id=service_id).first()
|
|
|
|
@classmethod
|
|
def find_by_sound(cls, sound_id: int) -> list["Stream"]:
|
|
"""Find all streams for a specific sound."""
|
|
return cls.query.filter_by(sound_id=sound_id).all()
|
|
|
|
@classmethod
|
|
def find_by_service(cls, service: str) -> list["Stream"]:
|
|
"""Find all streams for a specific service."""
|
|
return cls.query.filter_by(service=service).all()
|
|
|
|
@classmethod
|
|
def find_by_status(cls, status: str) -> list["Stream"]:
|
|
"""Find all streams with a specific status."""
|
|
return cls.query.filter_by(status=status).all()
|
|
|
|
@classmethod
|
|
def find_active_streams(cls) -> list["Stream"]:
|
|
"""Find all active streams."""
|
|
return cls.query.filter_by(status="active").all()
|
|
|
|
def update_metadata(
|
|
self,
|
|
title: Optional[str] = None,
|
|
track: Optional[str] = None,
|
|
artist: Optional[str] = None,
|
|
album: Optional[str] = None,
|
|
genre: Optional[str] = None,
|
|
commit: bool = True,
|
|
) -> None:
|
|
"""Update stream metadata."""
|
|
if title is not None:
|
|
self.title = title
|
|
if track is not None:
|
|
self.track = track
|
|
if artist is not None:
|
|
self.artist = artist
|
|
if album is not None:
|
|
self.album = album
|
|
if genre is not None:
|
|
self.genre = genre
|
|
|
|
if commit:
|
|
db.session.commit()
|
|
|
|
def set_status(self, status: str, commit: bool = True) -> None:
|
|
"""Update stream status."""
|
|
self.status = status
|
|
if commit:
|
|
db.session.commit()
|
|
|
|
def is_active(self) -> bool:
|
|
"""Check if stream is active."""
|
|
return self.status == "active"
|
|
|
|
def get_display_name(self) -> str:
|
|
"""Get a display name for the stream (title or track or service_id)."""
|
|
return self.title or self.track or self.service_id |