feat: Implement favorites management API; add endpoints for adding, removing, and retrieving favorites for sounds and playlists
feat: Create Favorite model and repository for managing user favorites in the database feat: Add FavoriteService to handle business logic for favorites management feat: Enhance Playlist and Sound response schemas to include favorite indicators and counts refactor: Update API routes to include favorites functionality in playlists and sounds
This commit is contained in:
39
app/schemas/favorite.py
Normal file
39
app/schemas/favorite.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Favorite response schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class FavoriteResponse(BaseModel):
|
||||
"""Response schema for a favorite."""
|
||||
|
||||
id: int = Field(description="Favorite ID")
|
||||
user_id: int = Field(description="User ID")
|
||||
sound_id: int | None = Field(
|
||||
description="Sound ID if this is a sound favorite", default=None
|
||||
)
|
||||
playlist_id: int | None = Field(
|
||||
description="Playlist ID if this is a playlist favorite", default=None
|
||||
)
|
||||
created_at: datetime = Field(description="Creation timestamp")
|
||||
updated_at: datetime = Field(description="Last update timestamp")
|
||||
|
||||
class Config:
|
||||
"""Pydantic config."""
|
||||
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class FavoritesListResponse(BaseModel):
|
||||
"""Response schema for a list of favorites."""
|
||||
|
||||
favorites: list[FavoriteResponse] = Field(description="List of favorites")
|
||||
|
||||
|
||||
class FavoriteCountsResponse(BaseModel):
|
||||
"""Response schema for favorite counts."""
|
||||
|
||||
total: int = Field(description="Total number of favorites")
|
||||
sounds: int = Field(description="Number of favorited sounds")
|
||||
playlists: int = Field(description="Number of favorited playlists")
|
||||
@@ -33,12 +33,23 @@ class PlaylistResponse(BaseModel):
|
||||
is_main: bool
|
||||
is_current: bool
|
||||
is_deletable: bool
|
||||
is_favorited: bool = False
|
||||
favorite_count: int = 0
|
||||
created_at: str
|
||||
updated_at: str | None
|
||||
|
||||
@classmethod
|
||||
def from_playlist(cls, playlist: Playlist) -> "PlaylistResponse":
|
||||
"""Create response from playlist model."""
|
||||
def from_playlist(cls, playlist: Playlist, is_favorited: bool = False, favorite_count: int = 0) -> "PlaylistResponse":
|
||||
"""Create response from playlist model.
|
||||
|
||||
Args:
|
||||
playlist: The Playlist model
|
||||
is_favorited: Whether the playlist is favorited by the current user
|
||||
favorite_count: Number of users who favorited this playlist
|
||||
|
||||
Returns:
|
||||
PlaylistResponse instance
|
||||
"""
|
||||
if playlist.id is None:
|
||||
msg = "Playlist ID cannot be None"
|
||||
raise ValueError(msg)
|
||||
@@ -50,6 +61,8 @@ class PlaylistResponse(BaseModel):
|
||||
is_main=playlist.is_main,
|
||||
is_current=playlist.is_current,
|
||||
is_deletable=playlist.is_deletable,
|
||||
is_favorited=is_favorited,
|
||||
favorite_count=favorite_count,
|
||||
created_at=playlist.created_at.isoformat(),
|
||||
updated_at=playlist.updated_at.isoformat() if playlist.updated_at else None,
|
||||
)
|
||||
|
||||
95
app/schemas/sound.py
Normal file
95
app/schemas/sound.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Sound response schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.models.sound import Sound
|
||||
|
||||
|
||||
class SoundResponse(BaseModel):
|
||||
"""Response schema for a sound with favorite indicator."""
|
||||
|
||||
id: int = Field(description="Sound ID")
|
||||
type: str = Field(description="Sound type")
|
||||
name: str = Field(description="Sound name")
|
||||
filename: str = Field(description="Sound filename")
|
||||
duration: int = Field(description="Duration in milliseconds")
|
||||
size: int = Field(description="File size in bytes")
|
||||
hash: str = Field(description="File hash")
|
||||
normalized_filename: str | None = Field(
|
||||
description="Normalized filename", default=None
|
||||
)
|
||||
normalized_duration: int | None = Field(
|
||||
description="Normalized duration in milliseconds", default=None
|
||||
)
|
||||
normalized_size: int | None = Field(
|
||||
description="Normalized file size in bytes", default=None
|
||||
)
|
||||
normalized_hash: str | None = Field(
|
||||
description="Normalized file hash", default=None
|
||||
)
|
||||
thumbnail: str | None = Field(description="Thumbnail filename", default=None)
|
||||
play_count: int = Field(description="Number of times played")
|
||||
is_normalized: bool = Field(description="Whether the sound is normalized")
|
||||
is_music: bool = Field(description="Whether the sound is music")
|
||||
is_deletable: bool = Field(description="Whether the sound can be deleted")
|
||||
is_favorited: bool = Field(
|
||||
description="Whether the sound is favorited by the current user", default=False
|
||||
)
|
||||
favorite_count: int = Field(
|
||||
description="Number of users who favorited this sound", default=0
|
||||
)
|
||||
created_at: datetime = Field(description="Creation timestamp")
|
||||
updated_at: datetime = Field(description="Last update timestamp")
|
||||
|
||||
class Config:
|
||||
"""Pydantic config."""
|
||||
|
||||
from_attributes = True
|
||||
|
||||
@classmethod
|
||||
def from_sound(
|
||||
cls, sound: Sound, is_favorited: bool = False, favorite_count: int = 0
|
||||
) -> "SoundResponse":
|
||||
"""Create a SoundResponse from a Sound model.
|
||||
|
||||
Args:
|
||||
sound: The Sound model
|
||||
is_favorited: Whether the sound is favorited by the current user
|
||||
favorite_count: Number of users who favorited this sound
|
||||
|
||||
Returns:
|
||||
SoundResponse instance
|
||||
"""
|
||||
if sound.id is None:
|
||||
raise ValueError("Sound ID cannot be None")
|
||||
|
||||
return cls(
|
||||
id=sound.id,
|
||||
type=sound.type,
|
||||
name=sound.name,
|
||||
filename=sound.filename,
|
||||
duration=sound.duration,
|
||||
size=sound.size,
|
||||
hash=sound.hash,
|
||||
normalized_filename=sound.normalized_filename,
|
||||
normalized_duration=sound.normalized_duration,
|
||||
normalized_size=sound.normalized_size,
|
||||
normalized_hash=sound.normalized_hash,
|
||||
thumbnail=sound.thumbnail,
|
||||
play_count=sound.play_count,
|
||||
is_normalized=sound.is_normalized,
|
||||
is_music=sound.is_music,
|
||||
is_deletable=sound.is_deletable,
|
||||
is_favorited=is_favorited,
|
||||
favorite_count=favorite_count,
|
||||
created_at=sound.created_at,
|
||||
updated_at=sound.updated_at,
|
||||
)
|
||||
|
||||
|
||||
class SoundsListResponse(BaseModel):
|
||||
"""Response schema for a list of sounds."""
|
||||
|
||||
sounds: list[SoundResponse] = Field(description="List of sounds")
|
||||
Reference in New Issue
Block a user