Files
manage/backend

Manage Backend API

FastAPI backend serving the REST API for Jellyfin media browsing, SSH file inspection, server monitoring, and JWT-protected access for Manage.

Project structure

backend/
├── pyproject.toml
├── README.md
├── src/
│   └── media_library_viewer_api/
│       ├── __init__.py
│       ├── main.py              # FastAPI app entrypoint
│       ├── config.py            # pydantic-settings config
│       ├── dependencies.py      # Dependency injection
│       ├── path_utils.py        # Jellyfin→SSH path resolution
│       ├── jobs.py              # Job templates
│       ├── utils.py             # Formatting helpers
│       ├── routers/
│       │   ├── dashboard.py
│       │   ├── monitoring.py
│       │   ├── media.py
│       │   ├── users.py
│       │   ├── settings.py
│       │   ├── files.py
│       │   └── jobs.py
│       ├── clients/
│       │   ├── jellyfin.py
│       │   ├── jellyseerr.py
│       │   ├── local.py
│       │   ├── resources.py
│       │   └── ssh.py
│       ├── domain/
│       │   └── media.py
│       └── services/
│           ├── media_index.py
│           └── settings_store.py
└── tests/

Setup

cd backend
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

Configuration

Set environment variables directly in your shell or a wrapper script before running the app or Compose. The Docker Compose files use interpolation and do not require an env_file entry.

For local .env development, you can still create one if you prefer, but it is optional.

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

# 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

SSH_HOST=media-server.example.com
SSH_USERNAME=username
SSH_PORT=22
# Host-side directory mounted into the backend container at /root/.ssh.
SSH_KEY_HOST_DIR=/absolute/path/to/your/ssh-dir
# Container-side path assembled by the app:
SSH_KEY_DIRECTORY=/root/.ssh
SSH_KEY_NAME=id_ed25519
SSH_PASSWORD=

REMOTE_MEDIA_ROOT=/srv/media
REMOTE_PATH_PREFIX=

Running

cd backend
uvicorn media_library_viewer_api.main:app --reload --port 8000

Or with PYTHONPATH if not installed:

PYTHONPATH=src uvicorn media_library_viewer_api.main:app --reload --port 8000

API docs available at: http://localhost:8000/docs

Docker

The repository root includes a production docker-compose.yml and a development docker-compose.dev.yml. The backend media index is stored in the backend_cache Docker volume so it survives container restarts and image rebuilds. The production compose file expects required environment variables to be supplied via interpolation (shell exports or inline VAR=value docker compose ...).

Configuration workflow examples

  1. Export your runtime variables before launching Compose:
export JELLYFIN_URL=https://jellyfin.example.com
export JELLYFIN_API_KEY=your-api-key
export SSH_HOST=media-server.example.com
export SSH_USERNAME=username
export SSH_KEY_HOST_DIR=$HOME/.ssh
export SSH_KEY_NAME=id_ed25519
export BACKEND_APP_HOST=manage.example.com
export FRONTEND_APP_HOST=manage.example.com
export CERT_RESOLVER=letsencrypt
export VITE_OIDC_ISSUER=https://authentik.example/application/o/manage/
export VITE_OIDC_CLIENT_ID=manage
export VITE_OIDC_REDIRECT_URI=https://manage.example.com/
export VITE_OIDC_POST_LOGOUT_REDIRECT_URI=https://manage.example.com/

docker compose up --build
  1. After the API is running, open the app, go to Settings, and add machine entries:

    • Local: monitors the API host itself without SSH.
    • SSH: monitors another machine using a host, username, key directory, and key name.
  2. Open Monitoring to see one section per configured machine. Each section uses its own collector state, disk path, metrics queries, and recent action history, which are populated automatically by the backend poller.

API Endpoints

  • GET /api/dashboard/counts — Movie/series/episode totals
  • GET /api/dashboard/libraries — Per-library breakdown
  • GET /api/dashboard/now-playing — Active playback sessions
  • GET /api/monitoring/machines — Persistent monitoring machine definitions
  • GET /api/monitoring/status?machine_id= — Collector status for a machine
  • GET /api/monitoring/metrics?machine_id= — Resource samples (last hour)
  • GET /api/monitoring/disk?machine_id= — Disk space
  • POST /api/monitoring/start|stop|restart?machine_id= — Collector controls
  • GET /api/monitoring/diagnostics?machine_id= — Collector debug info
  • GET /api/monitoring/poller — Backend poller status and configuration
  • GET /api/monitoring/machines/{machine_id}/actions — Recent machine action history
  • GET /api/dashboard/monitoring — Dashboard-wide per-machine monitoring summary table with 10-minute averages and min/max subtext
  • GET /api/settings/machines — Manage machine definitions
  • GET /api/media/status — Index status
  • POST /api/media/build — Rebuild index
  • GET /api/media/query — Query with filters/sort/pagination
  • GET /api/files/list?path= — Directory listing
  • GET /api/files/ffprobe?path= — ffprobe JSON
  • GET /api/files/stat?path= — stat output
  • GET /api/files/resolve-path?path= — Path resolution
  • GET /api/jobs/templates — Available jobs
  • POST /api/jobs/run — Execute a job
  • GET /api/users — Jellyfin users with optional Jellyseerr enrichment