# ☕ WeBrake **HandBrake, poured over the web.** A self-hosted web UI for [HandBrake CLI](https://handbrake.fr/features.php), built to live inside an **Alpine Linux LXC on Proxmox**. Upload media from any PC on your network, remux or re-encode it with the *entire* HandBrake flag surface, watch live progress bars, and download the result — GPU-accelerated when the container has passthru. Repo: `https://gogs.av2x.dev/av2x/WeBrake` --- ## One-liner install Run this **inside** your existing Alpine LXC (as root). It refuses to run on a Proxmox host by design — nothing is ever installed from the host: ```sh wget -qO- https://gogs.av2x.dev/av2x/WeBrake/raw/master/install.sh | ash ``` Then open `http://:8090`. The installer: 1. Verifies it's running on Alpine (and **not** on a Proxmox host — it aborts if `pveversion` exists). 2. Installs `python3`, `py3-flask`, `handbrake` (community repo, with edge fallback), and VA-API userspace drivers. 3. Pulls `app.py`, `static/index.html`, and `update.sh` **from this repo** — trying branches `master` then `main`, accepting `index.html` at the repo root as a fallback, and validating every download so a Gogs error/login page can never be installed as the app. 4. Generates a fresh **`token.json`** locally — secrets are created on your box, never fetched from nor committed to the repo (it's `.gitignore`d). 5. Installs and starts an OpenRC service (`rc-service webrake start`, enabled at boot). Custom port: `WEBRAKE_PORT=9000 wget -qO- .../install.sh | ash` ## Updating ```sh /opt/webrake/update.sh ``` Pulls the newest `app.py` and `index.html` **directly from the repo** (same `master`→`main` branch and path fallbacks as the installer), validates the downloads, backs up the old files to `/opt/webrake/.backup/`, restarts the service — and never touches `token.json`. --- ## Features - **Full HandBrake flag coverage** — structured controls for General/Container, Source (titles, chapters, angles, start/stop), Video (all detected encoders, presets/tune/profile/level, CQ/bitrate, 2-pass, framerate modes, `--encopts`), Audio (tracks, encoders, copy masks, mixdown, DRC, gain…), Subtitles (incl. SRT/SSA import, burn-in, forced), Picture (crop, scale, anamorphic, color matrix), and Filters (deinterlace, decomb, detelecine, denoise, sharpen, deblock, rotate, pad, colorspace, grayscale) — plus a **raw flags** box in *Advanced* that passes anything else verbatim to `HandBrakeCLI`, so every current and future flag is reachable. - **Presets** — the container's `HandBrakeCLI --preset-list` is loaded into a categorized dropdown; custom preset JSON works via `--preset-import-file` in the raw flags box. - **Upload → track → download** — drag-and-drop upload with a live progress bar, a job queue with per-job status bars (scan %, encode %, fps, ETA, pass number, tail of the encode log), cancel/delete controls, and one-click download of finished files back to the uploading PC. - **GPU passthru aware** — the backend probes `/dev/dri` + `/dev/nvidia*` and asks HandBrake which hardware encoders (QSV / NVENC / AMD VCE / VAAPI) actually work; those show up with a ⚡ badge and hardware decoding can be toggled on. - **Source scanning** — one click runs `HandBrakeCLI --scan --json` and lists titles, resolutions, and audio/subtitle track counts. - **Modern UI** — cream primary with gold accents; dark mode steeps everything in a dark-roast coffee palette with the same gold. Responsive, keyboard-accessible, reduced-motion friendly. ## GPU passthru (host configuration — done once, manually) WeBrake never installs anything on the Proxmox host, but the host must *lend* the GPU to the container. On the **host**, edit `/etc/pve/lxc/.conf`: **Intel / AMD (VA-API & QuickSync):** ``` lxc.cgroup2.devices.allow: c 226:* rwm lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir ``` **NVIDIA (NVENC)** — host needs the NVIDIA driver; then: ``` lxc.cgroup2.devices.allow: c 195:* rwm lxc.cgroup2.devices.allow: c 509:* rwm lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file ``` Restart the container. The header of the UI will show ⚡ chips for whatever hardware paths HandBrake can actually use. (Note: Alpine's `handbrake` package supports QSV/VAAPI paths depending on build; NVENC requires a HandBrake build with NVENC enabled.) ## `token.json` Created at `/opt/webrake/token.json` during install, mode `0600`: ```json { "api_token": "", "secret_key": "", "require_auth": false, "bots": { "my-bot": "" } } ``` - **Never in the repo** — it's listed in `.gitignore`, generated locally, and the updater never overwrites it. - Set `"require_auth": true` and restart (`rc-service webrake restart`) to require a token for **all** API access. The web UI detects this and highlights the **key button** in the header — paste the token there, or simply **upload your `token.json`** into the panel (it's parsed locally in the browser and only ever leaves as the `X-API-Token` request header). The token is remembered per-browser. - Bots/automation authenticate with header `X-API-Token: ` (any value from `api_token` or the `bots` map), e.g.: ```sh curl -H "X-API-Token: $TOKEN" -F file=@movie.mkv http://ct:8090/api/upload curl -H "X-API-Token: $TOKEN" -H 'Content-Type: application/json' \ -d '{"upload_id":"","options":{"format":"av_mkv","aencoder":"copy","extra_args":"--all-audio --all-subtitles"}}' \ http://ct:8090/api/jobs ``` ## API overview | Method | Path | Purpose | |---|---|---| | `POST` | `/api/upload` | multipart upload (`file`) | | `GET` | `/api/uploads` | list sources | | `DELETE` | `/api/uploads/` | remove a source | | `GET` | `/api/scan/` | HandBrake JSON title scan | | `GET` | `/api/presets` | preset list from the CLI | | `GET` | `/api/capabilities` | encoders + GPU device probe | | `POST` | `/api/jobs` | queue an encode (`upload_id`, `options`) | | `GET` | `/api/jobs` / `/api/jobs/` | queue & live progress | | `GET` | `/api/jobs//log` | encode log tail | | `POST` | `/api/jobs//cancel` | cancel | | `DELETE` | `/api/jobs/` | delete job + output | | `GET` | `/api/download/` | download finished file | `options` keys mirror HandBrake flags (`encoder`, `quality`, `two_pass`, `hqdn3d`, …) and `extra_args` is appended verbatim — see `FLAG_MAP` in `app.py`. ## Paths | Path | What | |---|---| | `/opt/webrake/` | app, UI, updater, `token.json` | | `/var/lib/webrake/uploads` | uploaded sources | | `/var/lib/webrake/output` | finished encodes | | `/var/lib/webrake/jobs` | job state (survives restarts) | | `/var/log/webrake.log` | service log | ## Remux quick-tip For a pure remux (no re-encode), set **Container** to your target, put `copy` in the audio encoder, and add `--all-audio --all-subtitles` in Advanced — HandBrake passes streams through where the container allows. --- *Not affiliated with the HandBrake project. HandBrake is © the HandBrake Team, GPLv2.*