184 lines
16 KiB
Markdown
184 lines
16 KiB
Markdown
# Manage - Requirements and Decision Log
|
|
|
|
This is a living document for the project. Update it whenever requirements, UX expectations, architecture decisions, constraints, or implementation plans change.
|
|
|
|
## Product Goal
|
|
|
|
Build Manage, a compact web application for browsing a remote Jellyfin media library and inspecting the corresponding media files on disk over SSH. The app should support library metadata review, direct file-system navigation, detailed media metadata inspection, and safe remote maintenance/job workflows.
|
|
|
|
## Current Phase
|
|
|
|
Phase 1: Jellyfin media index, SSH-based remote filesystem inspection, server monitoring, and safe job templates.
|
|
|
|
## Core Requirements
|
|
|
|
### Jellyfin Library
|
|
|
|
- Connect to a remote Jellyfin server using an API key.
|
|
- Use the Jellyfin server root URL, not the `/web` UI URL.
|
|
- Handle API-key auth correctly:
|
|
- use `GET /Users` to list available users;
|
|
- allow a manual `JELLYFIN_USER_ID` override;
|
|
- do not rely on `/Users/Me` for API-key auth.
|
|
- List Jellyfin libraries for the selected user.
|
|
- Browse library items with search, pagination, media type filtering, and poster cards.
|
|
- Show item details including overview, genres, ratings where available, file path, media sources/streams, and raw Jellyfin JSON.
|
|
- Provide a Media tab with a paginated inventory table for large libraries.
|
|
- Media inventory should show title, series name, season, episode number, type, year, runtime, file size, bitrate, explicit HDR yes/no flag, video codec, resolution, date added, and path where available.
|
|
- Media inventory should use Jellyfin metadata only for now; full ffprobe enrichment for all media should be deferred to a cached/background scan to avoid expensive per-item SSH probing.
|
|
- Media inventory should support multi-library selection, type, search, full-index sort/order, HDR filter, page size, and page controls.
|
|
- Media inventory should use a local SQLite index so sorting/filtering by nested/derived fields such as size, bitrate, HDR, codec, resolution, series, season, and episode can apply to the whole indexed library instead of only one Jellyfin page.
|
|
- Media inventory table should be read-only; full-index sorting/filtering should be handled by the service/query layer rather than relying on frontend table sorting.
|
|
- Media inventory should use row-based table selection and automatically sync the File browser tab to the selected row's containing directory.
|
|
- Media row selection should update both Media selected-row state and File browser location without requiring an extra action button.
|
|
- File browser interaction should stay explicit and simple: read-only listing plus explicit Open/Select actions rather than another row-selection grid.
|
|
- Use valid Jellyfin `Fields` query values only, because invalid field names can cause `400 Bad Request` responses.
|
|
|
|
### Users & Communication
|
|
|
|
- Provide a Users tab that lists all available users the system knows about.
|
|
- Use Jellyfin as the base source of truth for the user list.
|
|
- Optionally enrich Jellyfin users with Jellyseerr data when Jellyseerr is configured and reachable.
|
|
- Be tolerant of Jellyseerr response-shape differences across versions; for example, some endpoints may return a wrapped `{ users: [...] }` payload instead of a raw list.
|
|
- Surface whatever contact/identity fields are available from the configured source(s), such as email, avatar/thumb, role/permissions, and notification/contact eligibility.
|
|
- Email should only render actual email addresses; usernames or other non-email identifiers should be suppressed instead of shown as email.
|
|
- Keep communication actions separate from listing/identity data so the UI can support future email/notification workflows without redesigning the user list.
|
|
- SMTP-backed user messages should be queued asynchronously and return immediately; delivery must not block the rest of the API request path.
|
|
- The Users tab should expose a live queue status indicator so users can see when the outbound email queue is idle, busy, stopped, or failing.
|
|
- The queue status indicator should clearly show the current queue item count.
|
|
- The Users tab should include a one-click SMTP test action that validates connectivity/authentication without sending a real message.
|
|
- The SMTP test action should visibly show when it is running.
|
|
- The SMTP test should surface the chosen protocol/port and, for Fastmail, try both 465/SSL and 587/STARTTLS so configuration mismatches are easier to diagnose.
|
|
- Rework the Users data into an internal merged state so user identity can be combined with related now-playing/session data.
|
|
- Clicking a now-playing row should navigate to the Users tab and open the matching user detail drawer, keeping the selection deep-linkable.
|
|
- Provide an explicit "Open in Users" action in now-playing rows in addition to row-click navigation.
|
|
- Provide a compact per-field source summary for the Users detail drawer so it is obvious which backend source supplied name, email, avatar, and access data.
|
|
- Jellyseerr user list pagination must use `take`/`skip`, not `page`/`pageSize`.
|
|
- The Users tab table should stay compact and readable: center the avatar and email cells, keep backend source diagnostics out of the table itself, and prefer a simpler hand-built row layout when a dense grid makes text positioning awkward.
|
|
- The Users table activity column should stay compact and show only a brief status badge for playing/paused/idle/no-session state instead of a multi-line activity summary.
|
|
- The Dashboard activity panel should reuse the same compact session-table styling as the Users activity details so the two views feel consistent.
|
|
- In the shared session activity table, the user column should come before state, title/type, and device because the user is the most relevant identifier.
|
|
- The shared session table should keep a compact overall status summary line above the rows that reports total sessions plus playing, paused, and idle counts.
|
|
- The shared session table should keep the session identifier under the user name in a caption instead of giving it a full column, to keep the table tighter.
|
|
- The Users tab may open a read-only detail drawer for a selected user, but any communication actions in that drawer should remain clearly disabled/placeholders until the workflow is implemented.
|
|
- The frontend shell should use a polished two-row header with branding on the left, user/logout controls on the right, and primary navigation in a dedicated tab row beneath.
|
|
- The frontend shell and primary pages should remain responsive and mobile-safe, with compact navigation, stacked controls on narrow screens, and reduced table column density where needed.
|
|
- The frontend should hydrate the API bearer token from persisted OIDC user storage immediately on reload so early requests do not race the auth provider lifecycle.
|
|
- Backend startup should log a secret-safe configuration summary and request/activity diagnostics so configuration issues can be debugged without exposing API keys.
|
|
|
|
### Remote Filesystem over SSH
|
|
|
|
- Connect to a remote media server via SSH.
|
|
- Use strict SSH host key behavior; users should connect manually once to populate `known_hosts`.
|
|
- Browse remote directories and files rooted at a configurable default media path.
|
|
- File browser handoff should map Jellyfin paths to `REMOTE_MEDIA_ROOT` when possible (for example `/media/...` -> `/srv/media/...` when root is `/srv/media`).
|
|
- Media index paths should be stored in the SSH-visible form by default, using the same Jellyfin-to-SSH mapping so the Media tab and file browser agree on paths.
|
|
- Support a configurable Jellyfin-to-SSH fallback path prefix for cases where `REMOTE_MEDIA_ROOT` mapping alone is not sufficient.
|
|
- Support manual path entry and refresh.
|
|
- Remote file listing must be compact, structured, and navigable.
|
|
- The file table should be read-only.
|
|
- The file table should use row selection (single-select) in an AG Grid format consistent with the Media tab.
|
|
- The file table should not expose a visible checkbox selection column.
|
|
- The file table should not show a visible `selected` column.
|
|
- Include a top `[UP] ..` row, when not at `/`, to navigate to the parent directory.
|
|
- The selected path should be visibly shown outside the table.
|
|
- Selecting a directory row (including `[UP] ..`) should open it immediately.
|
|
- Selecting a file row should set the selected file target for metadata/jobs.
|
|
- Avoid emojis and hard-to-render characters in labels.
|
|
|
|
### Remote Listing Controls
|
|
|
|
- Show compact listing summary information:
|
|
- total entries;
|
|
- directories;
|
|
- files;
|
|
- total file size for files in the current directory.
|
|
- Support filtering by:
|
|
- all entries;
|
|
- directories;
|
|
- files;
|
|
- file extension.
|
|
- Support case-insensitive filename search.
|
|
- Support sorting by:
|
|
- name;
|
|
- kind/type;
|
|
- size;
|
|
- modified time.
|
|
- Support ascending/descending sort order.
|
|
- Support pagination and configurable rows per page.
|
|
|
|
### Disk-Level Metadata
|
|
|
|
- Run `ffprobe` on selected remote files to inspect authoritative media metadata directly from disk.
|
|
- When a known video/movie file is selected in the remote file browser, automatically run a blocking `ffprobe` preview and show a spinner while it completes.
|
|
- Cache preview results briefly to keep repeated Streamlit reruns responsive; allow users to reload the preview manually.
|
|
- Display `ffprobe` results in separate sections instead of one sparse all-streams table:
|
|
- container/format summary;
|
|
- video streams;
|
|
- audio streams;
|
|
- subtitle streams.
|
|
- Video metadata should include codec, profile, resolution, pixel format, bitrate, frame rate, color range/space/transfer/primaries, side data/HDR-related metadata where available, language, title, and default flag.
|
|
- Audio metadata should include codec, profile, channels/layout, sample rate, bitrate, language, title, default, and forced flags.
|
|
- Subtitle metadata should include codec, language, title, default, forced, and hearing-impaired flags where available.
|
|
- Expose raw `ffprobe` JSON for deeper inspection.
|
|
- Keep a manual `ffprobe` action available for selected paths.
|
|
- Support `stat` on selected paths.
|
|
|
|
### Dashboard / Server Monitoring
|
|
|
|
- Provide a dashboard tab with a compact Jellyfin media library overview and server resource overview.
|
|
- Support OIDC login in the frontend using an OIDC client library, with backend JWT validation for protected API requests.
|
|
- Persist frontend OIDC auth state across tab reloads by storing the OIDC user and request state in browser localStorage.
|
|
- Provide Docker Compose deployment files at the repository root for production and local development.
|
|
- Backend Docker deployment should mount a private SSH key and a known_hosts file into the container rather than baking them into the image.
|
|
- Production compose should also pass the root `.env` into the backend container so runtime auth settings like `OIDC_ISSUER_URL` are available there, not just at compose interpolation time.
|
|
- The backend media index should persist in a Docker volume so a container restart or image rebuild does not force a new full index build.
|
|
- The SSH key configuration should support separate directory/name inputs so Docker Compose can mount an arbitrary host SSH directory into `/root/.ssh` while the app assembles the full key path.
|
|
- Show Jellyfin media counts for movies, series, and series episodes on the dashboard.
|
|
- Show dashboard session activity from Jellyfin, including both currently playing sessions and logged-in idle sessions.
|
|
- Activity rows should include user, media title (or `(idle)`), playback state (`playing`/`paused`/`idle`), and whether transcoding is active.
|
|
- Provide a dashboard tab with a compact server resource overview over SSH.
|
|
- Provide a separate Monitoring tab for detailed resource charts, collector controls, diagnostics, and raw samples.
|
|
- The Monitoring tab should request all retained collector samples by default, while the dashboard overview can continue to use a shorter recent window.
|
|
- Show CPU and RAM usage for the last hour.
|
|
- Show IO wait percentage for the last hour.
|
|
- On the dashboard overview, summarize monitoring metrics as 10-minute averages with high/low values for quick inspection.
|
|
- Show average and spike/peak values for network throughput and disk I/O.
|
|
- Show used, available, and total disk space for the configured media root, falling back to `/`.
|
|
- Render Monitoring charts directly with D3 so the UI can support brush-based range selection, hover tooltips with a moving vertical cursor and snapped point markers, summary chips, and moving averages without a separate wrapper library.
|
|
- Use a lightweight remote collector that reads Linux `/proc`, `/sys/block`, and `df` data into a JSONL file under `/tmp`.
|
|
- The collector should rotate/prune its JSONL metrics file so it does not grow unbounded; default retention is 7 days with a 70,000-line safety cap.
|
|
- The collector should be startable/stoppable/restartable from the dashboard and should not require installing a full monitoring stack.
|
|
- Last-hour charts require the collector to have been running long enough to collect samples.
|
|
- Because the collector keeps only a bounded history, the Monitoring tab can safely load all retained samples up to the retention/max-lines cap.
|
|
- Network throughput should be shown as a combined traffic chart with download and upload lines.
|
|
- Network throughput should use bytes-per-second display units such as KB/s, MB/s, and GB/s to avoid bit/byte ambiguity.
|
|
- Disk throughput should be shown as a combined I/O chart with read and write lines.
|
|
- Network and disk throughput charts should scale values into readable units such as KB/s, MB/s, and GB/s.
|
|
- Each Monitoring chart should show compact summary chips such as min/avg/max for quick inspection.
|
|
- The Monitoring toolbar should offer quick time-range buttons such as 1h, 8h, 1 day, and 7 days in addition to free brush selection.
|
|
|
|
### Remote Jobs
|
|
|
|
- Support running remote jobs over SSH using explicit templates.
|
|
- Phase 1 jobs should be safe/read-only by default.
|
|
- Avoid arbitrary free-form command execution in the UI.
|
|
- Job templates should be centralized in `jobs.py` for future extension.
|
|
- Command template values must be shell-quoted before execution.
|
|
- Future destructive jobs should require explicit confirmation.
|
|
|
|
## Decision Log
|
|
|
|
- 2026-05-03: Reaffirmed that the Monitoring tab charts should be rendered directly with D3 and expose brush-based time-range selection plus moving averages.
|
|
- 2026-05-03: Added hover tooltips, summary chips, a moving vertical cursor, snapped point markers, and a selected-range label to the D3 Monitoring charts for faster visual inspection.
|
|
- 2026-05-03: Combined network download/upload into one traffic chart and disk read/write into one I/O chart for clearer Monitoring layout.
|
|
- 2026-05-03: Confirmed the shared session activity table should keep the session identifier as a caption under the user name instead of a full column.
|
|
- 2026-05-03: Confirmed the shared session table should keep the compact overall status summary line above the rows.
|
|
- 2026-05-03: Updated the dashboard monitoring cards to show 10-minute averages with high/low subtext instead of only the latest sample.
|
|
- 2026-05-03: Added OIDC/JWT auth support plus root-level Docker Compose deployment files for production and dev workflows.
|
|
- 2026-05-04: Backend Docker Compose now mounts a host SSH directory into `/root/.ssh` so Paramiko can use a private key and strict host-key checking without baking secrets into the image.
|
|
- 2026-05-04: The frontend was adjusted to be more mobile-safe by making the app shell tabs scrollable, stacking header controls on narrow screens, and hiding low-priority table columns on smaller displays.
|
|
- 2026-05-04: The app header was upgraded to a two-row branded layout with a left logo mark, right-side username/logout controls, and a separate navigation row.
|
|
- 2026-05-04: Frontend OIDC storage was switched from session-only defaults to localStorage-backed user/state stores so reloads keep the auth flow intact.
|
|
- 2026-05-04: API requests now fall back to the persisted OIDC user store for the bearer token so the first render after reload can avoid spurious 401s.
|