Files
haunt-fm/src/haunt_fm/api/playlists.py

82 lines
2.5 KiB
Python
Raw Normal View History

from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from haunt_fm.db import get_session
from haunt_fm.models.track import PlaylistTrack, Track
from haunt_fm.services.music_assistant import play_playlist_on_speaker
from haunt_fm.services.playlist_generator import generate_playlist
router = APIRouter(prefix="/api/playlists")
class GenerateRequest(BaseModel):
total_tracks: int = 20
known_pct: int = 30
name: str | None = None
speaker_entity: str | None = None
auto_play: bool = False
vibe: str | None = None
alpha: float = Field(default=0.5, ge=0.0, le=1.0)
@router.post("/generate")
async def generate(req: GenerateRequest, session: AsyncSession = Depends(get_session)):
# Compute text embedding for vibe description
vibe_embedding = None
if req.vibe:
from haunt_fm.services.embedding import embed_text, is_model_loaded, load_model
if not is_model_loaded():
load_model()
vibe_embedding = embed_text(req.vibe)
# Force pure taste when no vibe provided (preserves current behavior)
alpha = req.alpha if req.vibe else 1.0
playlist = await generate_playlist(
session,
total_tracks=req.total_tracks,
known_pct=req.known_pct,
name=req.name,
vibe_embedding=vibe_embedding,
alpha=alpha,
vibe_text=req.vibe,
)
# Load playlist tracks with track info
result = await session.execute(
select(PlaylistTrack, Track)
.join(Track, PlaylistTrack.track_id == Track.id)
.where(PlaylistTrack.playlist_id == playlist.id)
.order_by(PlaylistTrack.position)
)
rows = result.all()
track_list = [
{
"position": pt.position,
"artist": t.artist,
"title": t.title,
"album": t.album,
"is_known": pt.is_known,
"similarity_score": pt.similarity_score,
}
for pt, t in rows
]
# Auto-play if requested
if req.auto_play and req.speaker_entity:
await play_playlist_on_speaker(track_list, req.speaker_entity)
return {
"playlist_id": playlist.id,
"name": playlist.name,
"total_tracks": playlist.total_tracks,
"known_pct": playlist.known_pct,
"vibe": playlist.vibe,
"alpha": playlist.alpha,
"tracks": track_list,
"auto_played": req.auto_play and req.speaker_entity is not None,
}