feat(stream): add Stream model for managing streaming service links to sounds; update Sound model to include relationship with Stream
This commit is contained in:
169
app/models/stream.py
Normal file
169
app/models/stream.py
Normal file
@@ -0,0 +1,169 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user