Files
manage/backend/routers/media.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

85 lines
2.8 KiB
Python

"""Media router — index status, build, and query."""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, Query
from dependencies import get_jellyfin_client, get_user_id
from clients.jellyfin import JellyfinClient
from services.media_index import MediaIndex, build_media_index
router = APIRouter(prefix="/api/media", tags=["media"])
def get_media_index() -> MediaIndex:
return MediaIndex()
@router.get("/status")
def get_index_status(index: MediaIndex = Depends(get_media_index)) -> dict[str, Any]:
"""Return media index status (exists, count, last updated, build duration)."""
status = index.status()
return {
"exists": status.exists,
"item_count": status.item_count,
"updated_at": status.updated_at,
"updated_at_label": status.updated_at_label,
"build_duration_seconds": status.build_duration_seconds,
}
@router.post("/build")
def post_build_index(
client: JellyfinClient = Depends(get_jellyfin_client),
user_id: str = Depends(get_user_id),
index: MediaIndex = Depends(get_media_index),
) -> dict[str, Any]:
"""Rebuild the media index from Jellyfin."""
libraries = client.libraries(user_id)
count = build_media_index(client, user_id, libraries, index)
return {"indexed_items": count}
@router.get("/query")
def query_media(
libraries: str = Query("", description="Comma-separated library IDs"),
types: str = Query("Movie,Episode", description="Comma-separated media types"),
search: str = Query("", description="Search term"),
hdr_filter: str = Query("All", description="All, HDR only, SDR/unknown only"),
sort_key: str = Query("title", description="Sort field"),
sort_order: str = Query("Ascending", description="Ascending or Descending"),
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
client: JellyfinClient = Depends(get_jellyfin_client),
user_id: str = Depends(get_user_id),
index: MediaIndex = Depends(get_media_index),
) -> dict[str, Any]:
"""Query the media index with filters, sorting, and pagination."""
# If no library IDs provided, use all libraries
library_ids = [lid.strip() for lid in libraries.split(",") if lid.strip()] if libraries else None
if not library_ids:
all_libs = client.libraries(user_id)
library_ids = [lib["Id"] for lib in all_libs]
media_types = [t.strip() for t in types.split(",") if t.strip()]
rows, total = index.query(
library_ids=library_ids,
media_types=media_types,
search=search,
hdr_filter=hdr_filter,
sort_key=sort_key,
sort_order=sort_order,
limit=limit,
offset=offset,
)
return {
"items": rows,
"total": total,
"limit": limit,
"offset": offset,
}