69804a9eb2
Playlist M3U regen now writes to a temp file and renames it into place, so a stray wrong-owner leftover file (root:root, from pre-migration host-cron runs) can't block nightly writes the way it did last night. Dashboard now lists tracks added to the beets library in the last 24h with playlist and format, sourced from a new beets_service.recently_added().
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
import time
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.templating import Jinja2Templates
|
|
from sqlalchemy import select
|
|
|
|
from app.db import get_db
|
|
from app.models import JobRun
|
|
from app.security.deps import require_auth
|
|
from app.services import beets_service, credential_service, playlist_service
|
|
|
|
router = APIRouter(tags=["dashboard"])
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
_RECENT_TRACKS_WINDOW_SECONDS = 24 * 60 * 60
|
|
|
|
|
|
def _human_bytes(n: int) -> str:
|
|
size = float(n)
|
|
for unit in ("B", "KB", "MB", "GB", "TB"):
|
|
if size < 1024 or unit == "TB":
|
|
return f"{size:.1f} {unit}"
|
|
size /= 1024
|
|
return f"{size:.1f} TB"
|
|
|
|
|
|
@router.get("/")
|
|
async def dashboard(request: Request, user: dict = Depends(require_auth), db=Depends(get_db)):
|
|
stats = beets_service.stats()
|
|
playlists = playlist_service.list_all(db)
|
|
recent_runs = list(
|
|
db.execute(select(JobRun).order_by(JobRun.started_at.desc()).limit(10)).scalars()
|
|
)
|
|
recent_tracks = beets_service.recently_added(time.time() - _RECENT_TRACKS_WINDOW_SECONDS)
|
|
return templates.TemplateResponse(
|
|
request,
|
|
"dashboard.html",
|
|
{
|
|
"user": user,
|
|
"stats": stats,
|
|
"total_size_human": _human_bytes(stats.get("total_bytes", 0)),
|
|
"playlist_count": len(playlists),
|
|
"active_playlist_count": len([p for p in playlists if p.active]),
|
|
"recent_runs": recent_runs,
|
|
"recent_tracks": recent_tracks,
|
|
"auth_states": credential_service.auth_states(db),
|
|
},
|
|
)
|