114 lines
4.1 KiB
Python
114 lines
4.1 KiB
Python
|
|
from datetime import datetime, timedelta, timezone
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends
|
||
|
|
from sqlalchemy import func, select, text
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
|
||
|
|
from haunt_fm.db import get_session
|
||
|
|
from haunt_fm.models.track import (
|
||
|
|
ListenEvent,
|
||
|
|
Playlist,
|
||
|
|
TasteProfile,
|
||
|
|
Track,
|
||
|
|
TrackEmbedding,
|
||
|
|
)
|
||
|
|
from haunt_fm.config import settings
|
||
|
|
from haunt_fm.services.embedding import is_model_loaded
|
||
|
|
from haunt_fm.services.embedding_worker import is_running as is_worker_running
|
||
|
|
from haunt_fm.services.embedding_worker import last_processed as worker_last_processed
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/api")
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/status")
|
||
|
|
async def status(session: AsyncSession = Depends(get_session)):
|
||
|
|
# DB connectivity
|
||
|
|
try:
|
||
|
|
await session.execute(text("SELECT 1"))
|
||
|
|
db_connected = True
|
||
|
|
except Exception:
|
||
|
|
db_connected = False
|
||
|
|
|
||
|
|
if not db_connected:
|
||
|
|
return {"healthy": False, "db_connected": False}
|
||
|
|
|
||
|
|
now = datetime.now(timezone.utc)
|
||
|
|
day_ago = now - timedelta(days=1)
|
||
|
|
|
||
|
|
# Listen events
|
||
|
|
total_events = (await session.execute(select(func.count(ListenEvent.id)))).scalar() or 0
|
||
|
|
events_24h = (
|
||
|
|
await session.execute(
|
||
|
|
select(func.count(ListenEvent.id)).where(ListenEvent.listened_at >= day_ago)
|
||
|
|
)
|
||
|
|
).scalar() or 0
|
||
|
|
latest_event = (
|
||
|
|
await session.execute(select(func.max(ListenEvent.listened_at)))
|
||
|
|
).scalar()
|
||
|
|
|
||
|
|
# Tracks
|
||
|
|
total_tracks = (await session.execute(select(func.count(Track.id)))).scalar() or 0
|
||
|
|
from_history = (
|
||
|
|
await session.execute(
|
||
|
|
select(func.count(func.distinct(ListenEvent.track_id)))
|
||
|
|
)
|
||
|
|
).scalar() or 0
|
||
|
|
from_discovery = total_tracks - from_history
|
||
|
|
|
||
|
|
# Embeddings
|
||
|
|
def _embedding_count(status_val: str):
|
||
|
|
return select(func.count(Track.id)).where(Track.embedding_status == status_val)
|
||
|
|
|
||
|
|
emb_done = (await session.execute(_embedding_count("done"))).scalar() or 0
|
||
|
|
emb_pending = (await session.execute(_embedding_count("pending"))).scalar() or 0
|
||
|
|
emb_failed = (await session.execute(_embedding_count("failed"))).scalar() or 0
|
||
|
|
emb_no_preview = (await session.execute(_embedding_count("no_preview"))).scalar() or 0
|
||
|
|
|
||
|
|
# Taste profile
|
||
|
|
taste = (await session.execute(select(TasteProfile).where(TasteProfile.name == "default"))).scalar()
|
||
|
|
|
||
|
|
# Playlists
|
||
|
|
total_playlists = (await session.execute(select(func.count(Playlist.id)))).scalar() or 0
|
||
|
|
last_playlist = (await session.execute(select(func.max(Playlist.created_at)))).scalar()
|
||
|
|
|
||
|
|
return {
|
||
|
|
"healthy": db_connected,
|
||
|
|
"db_connected": db_connected,
|
||
|
|
"clap_model_loaded": is_model_loaded(),
|
||
|
|
"pipeline": {
|
||
|
|
"listen_events": {
|
||
|
|
"total": total_events,
|
||
|
|
"last_24h": events_24h,
|
||
|
|
"latest": latest_event.isoformat() if latest_event else None,
|
||
|
|
},
|
||
|
|
"tracks": {
|
||
|
|
"total": total_tracks,
|
||
|
|
"from_history": from_history,
|
||
|
|
"from_discovery": from_discovery,
|
||
|
|
},
|
||
|
|
"embeddings": {
|
||
|
|
"done": emb_done,
|
||
|
|
"pending": emb_pending,
|
||
|
|
"failed": emb_failed,
|
||
|
|
"no_preview": emb_no_preview,
|
||
|
|
"worker_running": is_worker_running(),
|
||
|
|
"worker_last_processed": worker_last_processed().isoformat() if worker_last_processed() else None,
|
||
|
|
},
|
||
|
|
"taste_profile": {
|
||
|
|
"exists": taste is not None,
|
||
|
|
"track_count": taste.track_count if taste else 0,
|
||
|
|
"updated_at": taste.updated_at.isoformat() if taste else None,
|
||
|
|
},
|
||
|
|
"playlists": {
|
||
|
|
"total_generated": total_playlists,
|
||
|
|
"last_generated": last_playlist.isoformat() if last_playlist else None,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"dependencies": {
|
||
|
|
"lastfm_api": "configured" if settings.lastfm_api_key else "not_configured",
|
||
|
|
"itunes_api": "ok", # no auth needed
|
||
|
|
"ha_reachable": bool(settings.ha_token),
|
||
|
|
"music_assistant_reachable": bool(settings.ha_token),
|
||
|
|
},
|
||
|
|
}
|