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) profile: str | None = None @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, profile_name=req.profile or "default", ) # 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, }