Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
from fastapi import APIRouter, Depends
|
2026-02-22 13:14:28 -06:00
|
|
|
from pydantic import BaseModel, Field
|
Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
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
|
2026-02-22 13:14:28 -06:00
|
|
|
vibe: str | None = None
|
|
|
|
|
alpha: float = Field(default=0.5, ge=0.0, le=1.0)
|
2026-02-22 19:14:34 -06:00
|
|
|
profile: str | None = None
|
Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/generate")
|
|
|
|
|
async def generate(req: GenerateRequest, session: AsyncSession = Depends(get_session)):
|
2026-02-22 13:14:28 -06:00
|
|
|
# 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
|
|
|
|
|
|
Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
playlist = await generate_playlist(
|
|
|
|
|
session,
|
|
|
|
|
total_tracks=req.total_tracks,
|
|
|
|
|
known_pct=req.known_pct,
|
|
|
|
|
name=req.name,
|
2026-02-22 13:14:28 -06:00
|
|
|
vibe_embedding=vibe_embedding,
|
|
|
|
|
alpha=alpha,
|
|
|
|
|
vibe_text=req.vibe,
|
2026-02-22 19:14:34 -06:00
|
|
|
profile_name=req.profile or "default",
|
Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# 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,
|
2026-02-22 13:14:28 -06:00
|
|
|
"vibe": playlist.vibe,
|
|
|
|
|
"alpha": playlist.alpha,
|
Initial haunt-fm implementation
Full music recommendation pipeline: listening history capture via webhook,
Last.fm candidate discovery, iTunes preview download, CLAP audio embeddings
(512-dim), pgvector cosine similarity recommendations, playlist generation
with known/new track interleaving, and Music Assistant playback via HA.
Includes: FastAPI app, SQLAlchemy models, Alembic migrations, Docker Compose
with pgvector/pg17, status dashboard, and all API endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 08:36:36 -06:00
|
|
|
"tracks": track_list,
|
|
|
|
|
"auto_played": req.auto_play and req.speaker_entity is not None,
|
|
|
|
|
}
|