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>
This commit is contained in:
2026-02-22 08:36:36 -06:00
parent 897d0fe1fb
commit 7ff69449d6
39 changed files with 2049 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
from datetime import datetime, timezone
from pathlib import Path
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
from jinja2 import Environment, FileSystemLoader
from sqlalchemy.ext.asyncio import AsyncSession
from haunt_fm.api.status import status as get_status_data
from haunt_fm.db import get_session
router = APIRouter()
_template_dir = Path(__file__).parent.parent / "templates"
_jinja_env = Environment(loader=FileSystemLoader(str(_template_dir)), autoescape=True)
@router.get("/", response_class=HTMLResponse)
async def status_page(request: Request, session: AsyncSession = Depends(get_session)):
data = await get_status_data(session)
template = _jinja_env.get_template("status.html")
html = template.render(data=data, now=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC"))
return HTMLResponse(html)