diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-17 15:25:52 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-17 15:25:52 +0300 |
| commit | 914bd7cd6aa14e839332a98d91c30b19865b0cf2 (patch) | |
| tree | 02b11537237a204048589e14ed24938b805e42f7 /docs | |
| parent | 3b24f0e1be832584d6550e8cc3e5d24329f66f90 (diff) | |
Restructure repo: move Go server into player-server/
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/admin.md | 50 | ||||
| -rw-r--r-- | docs/api.md | 84 | ||||
| -rw-r--r-- | docs/configuration.md | 19 | ||||
| -rw-r--r-- | docs/keyboard-shortcuts.md | 33 | ||||
| -rw-r--r-- | docs/podcasts.md | 31 | ||||
| -rw-r--r-- | docs/quick-start.md | 56 | ||||
| -rw-r--r-- | docs/theming.md | 53 |
7 files changed, 0 insertions, 326 deletions
diff --git a/docs/admin.md b/docs/admin.md deleted file mode 100644 index fcc0cdc..0000000 --- a/docs/admin.md +++ /dev/null @@ -1,50 +0,0 @@ -Admin Guide -=========== - -Admin endpoints are gated by `RequireAdmin` middleware (checks `users.is_admin`). The admin panel is opened via the "Admin" button in the SPA header (shown only when the current user is an admin). - -### Bootstrap - -On first visit (no users exist), you are redirected to `/bootstrap.html` to create the initial admin account. - -### Creating Users - -1. Open the admin panel. -2. Enter username, password, and check "Is admin" if desired. -3. Submit — the frontend calls `POST /api/admin/users`. -4. Admins cannot delete themselves via `DELETE /api/admin/users/:id`. - -### Managing Set Permissions - -- `GET /api/admin/permissions` — list permissions matrix -- `POST /api/admin/permissions` — grant access to a set (`body: { set_id, user_id, role: "owner" | "viewer" }`) -- `DELETE /api/admin/permissions` — revoke access (`body: { set_id, user_id }`) - -Roles: -- `owner` — can upload to the set, soft-delete / restore media, regenerate thumbnails -- `viewer` — can browse and play media in the set - -Admins implicitly see all sets without explicit permission rows. - -### Rescanning the Library - -Click **Rescan** in the admin panel, or call: - -```bash -curl -X POST -b session=<cookie> http://<host>/api/admin/rescan -``` - -This triggers `FSScanner.Scan()`, which: -1. Scans immediate subdirectories of `MEDIA_ROOT` as **sets** -2. Recursively walks each set for supported media files -3. Probes new files with `ffprobe` -4. Generates thumbnails for video files -5. Inserts new records into the `media` table - -### Managing Trash - -- `GET /api/admin/trash` — list soft-deleted media -- `DELETE /api/media/{id}` — soft-delete a media item -- `POST /api/media/{id}/restore` — restore a soft-deleted item - -Soft-deleted media remains on disk until garbage collection removes it (see `GC_INTERVAL_MINUTES`).
\ No newline at end of file diff --git a/docs/api.md b/docs/api.md deleted file mode 100644 index 0132300..0000000 --- a/docs/api.md +++ /dev/null @@ -1,84 +0,0 @@ -API Reference -============= - -### Public - -| Method | Path | Description | -|--------|------|-------------| -| `POST` | `/api/bootstrap` | Create the first admin account | -| `POST` | `/api/login` | Login | -| `GET` | `/healthz` | Liveness probe (no DB) | -| `GET` | `/readyz` | Readiness probe (DB ping) | -| `GET` | `/s/{token}` | View a shared media item | -| `GET` | `/s/{token}/stream` | Stream shared media | -| `GET` | `/s/{token}/thumbnail` | Thumbnail for shared media | -| `GET` | `/s/{token}/download` | Download shared media | - -### Session-required - -| Method | Path | Description | -|--------|------|-------------| -| `POST` | `/api/logout` | Logout | -| `GET` | `/api/sets` | List sets | -| `GET` | `/api/sets/{id}/browse` | Browse a set (folder navigation) | -| `GET` | `/api/sets/{id}/cover` | Get set cover image | -| `POST` | `/api/sets/{id}/cover` | Update set cover image | -| `POST` | `/api/sets/{id}/upload` | Upload file to set | -| `GET` | `/api/media` | List/search media (with filters) | -| `GET` | `/api/media/{id}` | Get media details | -| `GET` | `/api/media/{id}/stream` | Stream media (range support) | -| `GET` | `/api/media/{id}/download` | Download original file | -| `GET` | `/api/media/{id}/thumbnail` | Get thumbnail | -| `POST` | `/api/media/{id}/thumbnail` | Regenerate thumbnail | -| `POST` | `/api/media/{id}/favorite` | Toggle favorite | -| `POST` | `/api/media/{id}/tags` | Add tag | -| `DELETE` | `/api/media/{id}/tags/{tag}` | Remove tag | -| `POST` | `/api/media/{id}/shares` | Create share link | -| `GET` | `/api/media/{id}/shares` | List shares for a media item | -| `GET` | `/api/media/{id}/notes` | Get note | -| `POST` | `/api/media/{id}/notes` | Upsert note | -| `DELETE` | `/api/media/{id}/notes` | Delete note | -| `POST` | `/api/progress` | Save playback progress | -| `DELETE` | `/api/media/{id}` | Soft-delete media | -| `POST` | `/api/media/{id}/restore` | Restore soft-deleted media | -| `GET` | `/api/shares` | List my shares | -| `DELETE` | `/api/shares/{token}` | Revoke share | -| `GET` | `/api/podcasts` | List podcasts | -| `GET` | `/api/podcasts/{id}/episodes` | List episodes | -| `POST` | `/api/podcasts/episodes/{id}/download` | Download episode to server | -| `POST` | `/api/podcasts/episodes/{id}/complete` | Toggle episode listened | - -### Admin-only - -| Method | Path | Description | -|--------|------|-------------| -| `GET` | `/api/admin/users` | List users | -| `POST` | `/api/admin/users` | Create user | -| `DELETE` | `/api/admin/users/{id}` | Delete user | -| `GET` | `/api/admin/permissions` | List permissions | -| `POST` | `/api/admin/permissions` | Grant set permission | -| `DELETE` | `/api/admin/permissions` | Revoke set permission | -| `POST` | `/api/admin/rescan` | Trigger library rescan | -| `GET` | `/api/admin/scan-progress` | Get scan progress | -| `GET` | `/api/admin/trash` | List soft-deleted media | -| `POST` | `/api/podcasts` | Subscribe to podcast feed | - -### Query parameters for `GET /api/media` - -| Parameter | Description | -|-----------|-------------| -| `search` | Plain text search on file name | -| `set_id` | Filter by a single set ID | -| `set_ids` | Filter by comma-separated set IDs | -| `type` | Filter by media type (e.g. `video`, `audio`, `image`) | -| `favorites` | `true` or `1` to show favorites only | -| `tags` | Comma-separated tag names | -| `min_duration` | Minimum duration in minutes | -| `max_duration` | Maximum duration in minutes | -| `filesize_min` | Minimum file size in MB | -| `filesize_max` | Maximum file size in MB | -| `sort` | Sort order (e.g. `random`) | -| `limit` | Page size | -| `offset` | Page offset | -| `folder` | Filter to a specific folder path | -| `parent` | Filter to items within a parent folder |
\ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md deleted file mode 100644 index 60d6ad6..0000000 --- a/docs/configuration.md +++ /dev/null @@ -1,19 +0,0 @@ -Configuration -============= - -All settings are environment variables. Unset variables use defaults. - -| Variable | Default | Validation | Description | -|----------|---------|------------|-------------| -| `PORT` | `8080` | 0–65535 | HTTP listen port (0 = ephemeral, used in tests) | -| `MEDIA_ROOT` | `./media` | — | Root path for media set directories | -| `DB_PATH` | `data.db` | — | SQLite database file path | -| `MAX_UPLOAD_SIZE_MB` | `100` | ≥ 1 | Max upload size per file (MB) | -| `SESSION_TIMEOUT_HOURS` | `24` | ≥ 1 | Cookie / session expiry | -| `GC_INTERVAL_MINUTES` | `30` | ≥ 1 | Garbage collector tick interval | -| `SHARE_DEFAULT_EXPIRY_DAYS` | `7` | ≥ 1 | Default share link lifetime | -| `PODCAST_CHECK_INTERVAL_MINUTES` | `60` | ≥ 1 | Podcast feed refresh interval | -| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error` | Log verbosity | -| `SECURE_COOKIES` | `true` | `true` / `false` | Set `Secure` flag on session cookies; set to `false` for plain-HTTP local deployments | - -**Important:** The K8s `Deployment` overrides `DB_PATH` to `/data/media.db` and `MEDIA_ROOT` to `/media` so the PVC mounts are used. Do not rely on the local defaults in a container.
\ No newline at end of file diff --git a/docs/keyboard-shortcuts.md b/docs/keyboard-shortcuts.md deleted file mode 100644 index 080d02d..0000000 --- a/docs/keyboard-shortcuts.md +++ /dev/null @@ -1,33 +0,0 @@ -Keyboard Shortcuts -================== - -Global shortcuts are registered in `web/js/keyboard.js`. They are **disabled** while the user is focused on an `INPUT`, `TEXTAREA`, or `contentEditable` element (except `Escape` to blur). - -| Key | Action | -|-----|--------| -| `↑` / `↓` | Navigate media list (up / down) | -| `k` / `j` | Navigate media list (up / down) | -| `←` / `→` | Seek or switch pages | -| `h` / `l` | Seek or switch pages | -| `Enter` | Open selected media (navigate to detail) | -| `Space` / `p` | Play / pause / switch to selected item | -| `N` / `P` | Next / previous track | -| `f` | Toggle fullscreen on the player wrapper | -| `Esc` | Exit fullscreen, or deselect current item | -| `r` | Enable shuffle, or reshuffle the current filtered result set | -| `s` | Generate a share link for the selected media | -| `/` | Focus the quick search bar (debounced) | -| `n` | Open notes modal for the selected media | -| `i` | Show / hide media info | -| `C` | Minimize player | -| `d` | Detach / reattach player | -| `D` | Download selected media | -| `+` / `−` | Zoom in / out (image viewer) | -| `Shift+S` | Toggle slideshow (images) | -| `L` | My Shares | -| `Backspace` | Go up one folder | -| `?` | Show / hide help | - -Search syntax: plain text searches file name. Modifiers: `min:30`, `max:55`, `tag:a,b`, `like:1`, `type:video`, `sort:random`, `minsize:10`, `maxsize:500`. - -My Shares modal: `↑` / `↓` or `k` / `j` navigate, `Enter` copies, `Delete` revokes, `Esc` closes. diff --git a/docs/podcasts.md b/docs/podcasts.md deleted file mode 100644 index 6d03601..0000000 --- a/docs/podcasts.md +++ /dev/null @@ -1,31 +0,0 @@ -Podcast Support -=============== - -Podcasts are **special sets** (`sets.is_podcast = 1`). They reuse set permissions, browsing, and cover images, while adding feed management and episode tracking. - -### Subscribing - -Admin opens the **Podcasts** button in the admin panel (or calls `POST /api/podcasts`): -- Submit an RSS/Atom feed URL and optional folder name. -- Server creates a set folder, parses the feed, downloads the cover image, and inserts episodes into `podcast_episodes`. - -### Episode Management - -Episodes are stored in `podcast_episodes` and rendered in the browse grid for podcast sets: -- **Undownloaded** episodes show a **Download to server** button (calls `POST /api/podcasts/episodes/{id}/download`). -- **Downloaded** episodes become regular `media` rows and appear as normal media cards. -- Users can mark episodes as listened/unlistened via the checkmark button. - -### Background Feed Checker - -A background goroutine (`CheckFeeds`) refreshes feeds every hour (configurable via `PODCAST_CHECK_INTERVAL_MINUTES`). It uses conditional GET (`If-None-Match`, `If-Modified-Since`) to avoid re-downloading unchanged feeds. - -### API Endpoints - -| Method | Path | Description | -|--------|------|-------------| -| `GET` | `/api/podcasts` | List podcast sets | -| `POST` | `/api/podcasts` | Subscribe to a new feed (admin) | -| `GET` | `/api/podcasts/{id}/episodes` | List episodes with status | -| `POST` | `/api/podcasts/episodes/{id}/download` | Server-side download | -| `POST` | `/api/podcasts/episodes/{id}/complete` | Toggle completion |
\ No newline at end of file diff --git a/docs/quick-start.md b/docs/quick-start.md deleted file mode 100644 index d38304d..0000000 --- a/docs/quick-start.md +++ /dev/null @@ -1,56 +0,0 @@ -Quick Start -=========== - -### Running from source - -```bash -go build -o player ./cmd/player -./player -``` - -Open `http://localhost:8080` — on first visit you'll be redirected to the bootstrap page to create an admin account. - -### Running with Docker - -```bash -docker build -t player:latest . -docker run -p 8080:8080 \ - -v player-data:/data \ - -v /path/to/media:/media \ - -e MEDIA_ROOT=/media \ - -e DB_PATH=/data/media.db \ - player:latest -``` - -### Deploying to Kubernetes - -```bash -kubectl apply -f k8s/ -``` - -This creates a Deployment (non-root, probes included), ClusterIP Service, PVCs for `/data` and `/media`, and an optional Secret. - -The `Deployment` overrides two settings for K8s: -- `DB_PATH=/data/media.db` -- `MEDIA_ROOT=/media` - -Probes: -- **Liveness:** `GET /healthz` (no DB dependency) -- **Readiness:** `GET /readyz` (DB ping) - -Security: -- `runAsNonRoot: true` -- `runAsUser: 65534` / `runAsGroup: 65534` -- `allowPrivilegeEscalation: false` -- `readOnlyRootFilesystem: true` - -### Mage targets - -| Target | Description | -|--------|-------------| -| `mage build` | Compile the binary | -| `mage test` | Run `go test ./...` | -| `mage install` | Build and copy binary to `$GOPATH/bin` | -| `mage clean` | Remove build artifacts | -| `mage docker-build` | Build Docker image as `player:latest` | -| `mage docker-push` | Push `player:latest` to registry |
\ No newline at end of file diff --git a/docs/theming.md b/docs/theming.md deleted file mode 100644 index 245a801..0000000 --- a/docs/theming.md +++ /dev/null @@ -1,53 +0,0 @@ -Theming -======= - -All colors live in `web/css/theme.css` as CSS Custom Properties on `:root`. - -### Current Implementation - -`themes.js` swaps the active theme by setting `document.documentElement.setAttribute('data-theme', ...)` and saves the preference to `localStorage`. Override blocks in `theme.css` handle the light variant: - -```css -/* Default (dark) — defined on :root */ -:root { - --bg-body: #0f1117; - --text-primary: #e6e8ef; - --accent: #5e9eff; - ... -} - -/* Light theme overrides */ -[data-theme="light"] { - --bg-body: #f4f5f8; - --text-primary: #12131a; - --accent: #2b6cb0; - ... -} -``` - -### Adding a New Theme - -Option A — inline override (recommended for small additions): - -1. Open `web/css/theme.css`. -2. Append a new attribute selector after the light block, e.g.: - -```css -[data-theme="solarized"] { - --bg-body: #002b36; - --text-primary: #839496; - --accent: #268bd2; - ... -} -``` - -3. Wire the toggle in `web/js/themes.js` (or expose a selector UI in `index.html`) to call `apply('solarized')`. - -Option B — separate file (if you prefer a stylesheet swap): - -1. Create `web/css/themes/<name>.css` containing `:root { ... }` overrides. -2. Dynamically create or swap a `<link rel="stylesheet">` in `themes.js` instead of using `data-theme`. - -**Rules:** -- No color literals in component styles — everything must go through `var(--*)`. -- Do not add inline styles in HTML or JS.
\ No newline at end of file |
