b096eb3e79428ee15e33ec1c4077873fdbd67c7f
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 (seebackend/README.md)frontend/— React + TypeScript SPA (seefrontend/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:
docker compose up --build
Open the app at http://localhost:8080.
Local development with hot reload:
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
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
uvicorn media_library_viewer_api.main:app --reload --port 8000
cd frontend
npm install
npm run dev
Configuration
Create a .env file in the project root:
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 your private key at ./secrets/ssh/id_ed25519
# and your host key file at ./secrets/ssh/known_hosts.
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
/procand/sys/blockfor monitoring /bin/sh(POSIX shell)python3,ffprobe,find,stat,df,awk
The SSH client rejects unknown host keys. Connect manually once first:
ssh user@host
Development
# 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/webdefensively. - SSH commands run through
/bin/sh -cregardless 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).
Languages
Python
57.9%
TypeScript
40.4%
JavaScript
0.8%
Shell
0.5%
CSS
0.3%
Other
0.1%