# Media Library Viewer Media library management tool with Jellyfin integration, SSH file inspection, server monitoring, and safe remote job templates. See `docs/REQUIREMENTS.md` for the living requirements, decisions, and planning history. See `docs/MIGRATION_PLAN.md` for the FastAPI + React architecture plan. Project policy/docs: - License: `LICENSE` (MIT) - Contributing guide: `CONTRIBUTING.md` ## Architecture The project consists of two subprojects: - **`backend/`** — FastAPI Python API (see `backend/README.md`) - **`frontend/`** — React + TypeScript SPA (see `frontend/README.md`) - **`archive/`** — Original Streamlit prototype (preserved for reference) ## Features - Dashboard with now-playing sessions, server monitoring overview, and per-library media counts - Server monitoring with CPU, IO wait, RAM, network, and disk I/O charts - SQLite-indexed media table with full-library sort/filter - Read-only Users tab with Jellyfin as the base source and optional Jellyseerr enrichment - Remote file browser with ffprobe preview and job execution - Jellyfin API integration for library metadata and user identity data - SSH-based file inspection and remote job templates ## Quick Start ### Docker Compose (recommended) Production-style deployment with the frontend serving the SPA and proxying `/api` to the backend: ```bash docker compose up --build ``` Open the app at http://localhost:8080. Local development with hot reload: ```bash docker compose -f docker-compose.dev.yml up --build ``` Frontend runs on http://localhost:5173 and the backend on http://localhost:8000. ### Manual backend/frontend development ```bash cd backend python -m venv .venv source .venv/bin/activate pip install -e '.[dev]' uvicorn media_library_viewer_api.main:app --reload --port 8000 ``` ```bash cd frontend npm install npm run dev ``` ## Configuration Create a `.env` file in the project root: ```bash JELLYFIN_URL=https://jellyfin.example.com JELLYFIN_API_KEY=your-api-key JELLYFIN_USER_ID= # Optional Jellyseerr enrichment for the Users tab JELLYSEERR_URL=https://requests.example.com JELLYSEERR_API_KEY=your-jellyseerr-api-key # Optional backend logging level LOG_LEVEL=INFO SSH_HOST=media-server.example.com SSH_USERNAME=username SSH_PORT=22 # In Docker Compose, mount ./secrets/ssh to /root/.ssh inside the backend container. # The app uses SSH_KEY_DIRECTORY + SSH_KEY_NAME to build the full key path. SSH_KEY_DIRECTORY=/root/.ssh SSH_KEY_NAME=id_ed25519 # Optional legacy override if you want to provide the full path directly. # SSH_KEY_FILENAME=/root/.ssh/id_ed25519 SSH_PASSWORD= REMOTE_MEDIA_ROOT=/srv/media REMOTE_PATH_PREFIX= # Authentik / OIDC AUTH_ENABLED=true OIDC_ISSUER_URL=https://authentik.example/application/o/media-library-viewer/ OIDC_AUDIENCE=media-library-viewer OIDC_JWKS_URL= OIDC_CLOCK_SKEW_SECONDS=30 # Frontend OIDC settings VITE_OIDC_ENABLED=true VITE_OIDC_ISSUER=https://authentik.example/application/o/media-library-viewer/ VITE_OIDC_CLIENT_ID=media-library-viewer VITE_OIDC_SCOPE=openid profile email VITE_OIDC_REDIRECT_URI=http://localhost:8080/ VITE_OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:8080/ ``` ## Remote server requirements The remote server needs: - Linux `/proc` and `/sys/block` for monitoring - `/bin/sh` (POSIX shell) - `python3`, `ffprobe`, `find`, `stat`, `df`, `awk` The SSH client rejects unknown host keys. Connect manually once first: ```bash ssh user@host ``` ## Development ```bash # Backend cd backend && PYTHONPATH=src python -m py_compile src/media_library_viewer_api/main.py # Frontend cd frontend && npx tsc --noEmit && npm run build ``` ## Notes - Jellyfin server root URL required (not `/web`). The client strips trailing `/web` defensively. - SSH commands run through `/bin/sh -c` regardless of remote login shell. - Job templates are shell-quoted. Add new templates in `backend/src/media_library_viewer_api/jobs.py`. - Monitoring collector uses JSONL in `/tmp`, pruned to 7 days / 70k lines. - Root-level Docker Compose files are provided for production (`docker-compose.yml`) and local development (`docker-compose.dev.yml`).