Files
manage/backend/routers/dashboard.py
T
alex 3c432473e5 Add FastAPI backend and React frontend subprojects
Backend:
- FastAPI app with 17 REST endpoints covering dashboard, monitoring,
  media index, file browser, and jobs
- Reuses existing clients/domain/services unchanged
- pydantic-settings config, dependency injection, CORS setup
- Auto-generated OpenAPI docs at /docs

Frontend:
- Vite + React + TypeScript SPA
- @tanstack/react-query for data fetching with polling
- ag-grid-react for media table and file browser
- recharts for monitoring charts
- Tailwind CSS styling
- 4 pages: Dashboard, Monitoring, Media, File Browser
- Typed API client matching all backend endpoints

Also:
- docs/MIGRATION_PLAN.md with full architecture plan
- Updated .gitignore for both subprojects
- Streamlit app preserved for now (can coexist)
2026-04-30 21:40:18 +02:00

70 lines
2.4 KiB
Python

"""Dashboard router — media counts, per-library breakdown, now-playing."""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends
from dependencies import get_jellyfin_client, get_user_id
from clients.jellyfin import JellyfinClient
router = APIRouter(prefix="/api/dashboard", tags=["dashboard"])
@router.get("/counts")
def get_counts(
client: JellyfinClient = Depends(get_jellyfin_client),
user_id: str = Depends(get_user_id),
) -> dict[str, int]:
"""Return total movie/series/episode counts."""
return client.media_counts(user_id)
@router.get("/libraries")
def get_library_counts(
client: JellyfinClient = Depends(get_jellyfin_client),
user_id: str = Depends(get_user_id),
) -> list[dict[str, Any]]:
"""Return per-library item counts broken down by type."""
libraries = client.libraries(user_id)
return client.library_item_counts(user_id, libraries)
@router.get("/now-playing")
def get_now_playing(
client: JellyfinClient = Depends(get_jellyfin_client),
) -> list[dict[str, Any]]:
"""Return currently active playback sessions with transcode info."""
sessions = client.active_sessions()
results = []
for session in sessions:
item = session.get("NowPlayingItem") or {}
play_state = session.get("PlayState") or {}
transcoding = session.get("TranscodingInfo") or {}
series = item.get("SeriesName") or ""
title = f"{series} - {item.get('Name', '')}" if series else item.get("Name", "Unknown")
is_transcoding = bool(transcoding)
transcode_type = []
if is_transcoding:
if transcoding.get("IsVideoDirect") is False:
transcode_type.append("video")
if transcoding.get("IsAudioDirect") is False:
transcode_type.append("audio")
if not transcode_type:
transcode_type.append("active")
results.append({
"user": session.get("UserName") or "Unknown",
"title": title,
"type": item.get("Type", ""),
"state": "paused" if play_state.get("IsPaused") else "playing",
"transcoding": "yes" if is_transcoding else "no",
"transcoding_type": ", ".join(transcode_type),
"device": session.get("DeviceName") or session.get("Client") or "",
"session_id": session.get("Id") or "",
})
return results