3c432473e5
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)
149 lines
5.5 KiB
Markdown
149 lines
5.5 KiB
Markdown
# Media Library Viewer
|
|
|
|
Small Streamlit app for browsing a remote Jellyfin library, inspecting files on disk over SSH, previewing media metadata with `ffprobe`, and running safe remote job templates.
|
|
|
|
See `docs/REQUIREMENTS.md` for the living requirements, decisions, and planning history.
|
|
|
|
Project policy/docs:
|
|
|
|
- License: `LICENSE` (MIT)
|
|
- Contributing guide: `CONTRIBUTING.md`
|
|
|
|
## Phase 1 features
|
|
|
|
- Dashboard media counts for movies, series, and episodes, plus now-playing sessions (user, title, playback state, transcoding)
|
|
- SQLite-indexed Media tab with full-library sort/filter for runtime, size, bitrate, explicit HDR yes/no flag, date added, codec, resolution, series, season, episode, path, and row-based selection that automatically syncs File browser to the selected item's folder
|
|
- Server monitoring dashboard plus detailed Monitoring tab for CPU, IO wait, RAM, network, disk I/O, and disk space
|
|
- Jellyfin API-key connection using `GET /Users` plus user-scoped library endpoints
|
|
- Compact SSH remote directory browser with clickable rows, search, filters, sorting, pagination, and `[UP] ..` navigation
|
|
- Blocking selected-file `ffprobe` preview for known video files
|
|
- Separate container, video, audio, and subtitle metadata sections
|
|
- `stat` inspection
|
|
- Safe/read-only job templates, designed to extend later
|
|
|
|
## Project structure
|
|
|
|
```text
|
|
.
|
|
├── app.py # Thin Streamlit entrypoint
|
|
├── pyproject.toml # Package metadata, dependencies, tool config
|
|
├── requirements.txt # Convenience install file (-e .)
|
|
├── docs/
|
|
│ └── REQUIREMENTS.md # Living requirements and decision log
|
|
├── src/
|
|
│ └── media_library_viewer/
|
|
│ ├── app.py # Thin Streamlit orchestration layer
|
|
│ ├── config.py # Environment/.env config
|
|
│ ├── jobs.py # Remote job templates
|
|
│ ├── utils.py # Formatting and ffprobe summaries
|
|
│ ├── domain/ # UI-independent normalization/domain helpers
|
|
│ ├── services/ # UI-independent application services/indexes
|
|
│ ├── ui/ # Streamlit UI modules split by feature area
|
|
│ └── clients/
|
|
│ ├── jellyfin.py # Jellyfin API client
|
|
│ └── ssh.py # SSH/ffprobe client
|
|
└── tests/
|
|
```
|
|
|
|
## Setup
|
|
|
|
```bash
|
|
python -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
For development tools:
|
|
|
|
```bash
|
|
pip install -e '.[dev]'
|
|
```
|
|
|
|
Create `.env` or use Streamlit secrets/env vars:
|
|
|
|
```bash
|
|
JELLYFIN_URL=https://jellyfin.example.com
|
|
JELLYFIN_API_KEY=your-api-key
|
|
# Optional fallback if user selection via GET /Users does not work.
|
|
JELLYFIN_USER_ID=
|
|
|
|
SSH_HOST=media-server.example.com
|
|
SSH_USERNAME=username
|
|
SSH_PORT=22
|
|
SSH_KEY_FILENAME=/home/username/.ssh/id_rsa
|
|
# SSH_PASSWORD=optional-password-or-key-passphrase
|
|
REMOTE_MEDIA_ROOT=/mnt/media
|
|
# Optional fallback prefix when REMOTE_MEDIA_ROOT mapping is not enough.
|
|
# Example fallback: Jellyfin /media/... -> SSH /srv/media/...
|
|
REMOTE_PATH_PREFIX=/srv
|
|
```
|
|
|
|
Run:
|
|
|
|
```bash
|
|
streamlit run app.py
|
|
```
|
|
|
|
## Before publishing / committing
|
|
|
|
Keep these out of git:
|
|
|
|
- `.env` and any real secrets/tokens/passwords
|
|
- `.streamlit/secrets.toml`
|
|
- `.venv/`, local IDE files (`.idea/`, `.vscode/`)
|
|
- local cache/index files under `.cache/`
|
|
- logs and temporary files
|
|
|
|
The included `.gitignore` already excludes these.
|
|
|
|
## Remote server requirements
|
|
|
|
For file browsing, resource metrics, and media metadata:
|
|
|
|
```bash
|
|
ffprobe -version
|
|
python3 --version
|
|
find --version
|
|
stat --version
|
|
df --version
|
|
awk --version || true
|
|
```
|
|
|
|
The resource dashboard uses Linux `/proc`, `/sys/block`, `/bin/sh`, and a lightweight collector started over SSH. No full monitoring stack is required, but last-hour charts require the collector to have been running long enough to gather samples. Network is shown as download/upload in bytes per second, and disk I/O is shown as read/write in bytes per second. The metrics JSONL file is pruned to 7 days with a 70,000-line safety cap. SSH commands are explicitly run through `/bin/sh -c`, so the remote user's login shell may be fish or another shell. If no samples appear after 10-20 seconds, use the dashboard's `Restart` button and inspect `Collector diagnostics`.
|
|
|
|
The SSH client uses your local `known_hosts` and rejects unknown host keys. Connect once manually first:
|
|
|
|
```bash
|
|
ssh user@host
|
|
```
|
|
|
|
## Extending jobs
|
|
|
|
Add templates in `src/media_library_viewer/jobs.py`:
|
|
|
|
```python
|
|
JOB_TEMPLATES["my_job"] = JobTemplate(
|
|
name="My job",
|
|
description="What it does.",
|
|
command_template="my-command --input {path}",
|
|
destructive=False,
|
|
)
|
|
```
|
|
|
|
Template variables are shell-quoted before insertion. For destructive jobs, add confirmation UI before executing.
|
|
|
|
## Development checks
|
|
|
|
```bash
|
|
PYTHONPATH=src python -m py_compile app.py src/media_library_viewer/*.py src/media_library_viewer/clients/*.py
|
|
ruff check .
|
|
pytest
|
|
```
|
|
|
|
## Notes
|
|
|
|
- Prefer Jellyfin API for library metadata.
|
|
- Enter the Jellyfin server root URL, for example `https://jellyfin.example.com`, not `https://jellyfin.example.com/web`. The client strips a trailing `/web` defensively.
|
|
- Prefer SSH/ffprobe for authoritative disk-level media metadata such as actual bitrate, color transfer, HDR metadata, audio channels, subtitles, and container details.
|
|
- Keep cleanup/transcode jobs explicit and template-based; avoid free-form command execution in the UI unless this app is only used locally by trusted users.
|