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:
JSC
2025-08-16 21:16:02 +02:00
parent 5e6cc04ad2
commit a947fd830b
14 changed files with 1005 additions and 13 deletions

95
app/schemas/sound.py Normal file
View 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")