# Remote Transcoder Worker

Runs on **Windows Docker Desktop** (or any machine with Docker).  
Pulls video jobs from the Hetzner CMS over HTTPS, transcodes them locally using
all available CPU cores, and pushes the finished HLS package back — no SSH,
no shared drives, no VPS CPU load.

---

## How it works

```
Docker Desktop (local)                  Hetzner VPS (CMS)
──────────────────────                  ─────────────────
1. POST /api/transcoder/claim     →     Lock next uploaded video
   ← { videoId, downloadUrl }
2. GET  /api/transcoder/download/:id →  Stream source MP4
3. ffmpeg (local CPU)
   Generate HLS segments + playlists
4. Create ZIP of HLS output
5. POST /api/transcoder/upload/:id  →  Extract ZIP, validate, rename
                                        Update status → "transcoded"
6. Repeat until queue empty, exit 0
```

---

## Prerequisites

| Requirement | Notes |
|---|---|
| Docker Desktop | Windows, Mac, or Linux |
| CMS environment variable `TRANSCODER_SECRET` | Add to CMS `.env` and redeploy |
| CMS redeployed with new API routes | See **CMS changes** section |

---

## Setup

### 1 — Add `TRANSCODER_SECRET` to the CMS

In your Hetzner CMS `.env` (or however you manage secrets):

```env
TRANSCODER_SECRET=replace-with-a-strong-random-secret
```

Generate a secret with:
```bash
openssl rand -hex 32
```

Redeploy the CMS so the new API routes are live.

### 2 — Configure the worker

```bash
cd transcoder-remote
cp .env.example .env
# Edit .env and fill in CMS_URL and TRANSCODER_SECRET
```

`.env` example:
```env
CMS_URL=https://cms.yourdomain.com
TRANSCODER_SECRET=replace-with-a-strong-random-secret
```

---

## Running

### Build and run (one command)

```powershell
docker compose up --build
```

The container will:
1. Build the TypeScript worker
2. Start processing all queued videos
3. Exit automatically when the queue is empty

### Run again later (no rebuild)

```powershell
docker compose up
```

### Run without docker-compose

```powershell
docker build -t transcoder-remote .
docker run --rm --env-file .env transcoder-remote
```

---

## Log output

```
[Worker] Remote Transcoder starting
[Worker] CMS     : https://cms.yourdomain.com
[Worker] Work dir: /work

[Job] cm1a2b3c4d5e6f7g8h9i0j

[Download] Starting...
[Download] 312 MB received...
[Download] 624 MB in 38s

[Probe] Resolution: 1920x1080
[HLS] Creating 1080p
[HLS] Creating 720p
[HLS] Creating 480p
[Transcode] 1080p, 720p, 480p in 8m 14s

[Package] Creating zip...
[Package] 1.1 GB in 22s

[Upload] Starting...
[Upload] Done in 31s

[Complete] cm1a2b3c4d5e6f7g8h9i0j in 9m 45s

[Worker] Queue empty. Jobs processed this run: 1
[Worker] Exiting.
```

---

## CMS changes (already applied)

Four new API routes were added to the CMS Next.js app:

| Route | Purpose |
|---|---|
| `POST /api/transcoder/claim` | Atomically claim next job |
| `GET  /api/transcoder/download/:videoId` | Stream source MP4 |
| `POST /api/transcoder/upload/:videoId` | Receive HLS zip |
| `POST /api/transcoder/fail/:videoId` | Mark job failed |

All routes require `Authorization: Bearer <TRANSCODER_SECRET>`.

The claim endpoint uses `SELECT … FOR UPDATE SKIP LOCKED` so multiple workers can run concurrently without racing on the same job.

---

## Disk space requirements

Each job requires roughly:

| Step | Space |
|---|---|
| Downloaded MP4 | Up to ~2 GB |
| HLS output (all variants) | ~1–4× source size |
| ZIP archive | ~same as HLS output |
| **Total per job** | **~4–8 GB** |

Ensure Docker Desktop's virtual disk limit is large enough (Settings → Resources → Disk image size). 80 GB+ is recommended for large educational videos.

Temp files are deleted automatically after each job.

---

## Transcoding settings

Identical to the VPS transcoder — output is fully compatible with the existing
HLS player:

| Variant | Resolution | Video bitrate | Audio |
|---|---|---|---|
| 1080p | 1920×1080 | 3500k (max 4000k) | AAC 128k |
| 720p  | 1280×720  | 1800k (max 2000k) | AAC 128k |
| 480p  | 854×480   | 900k  (max 1000k) | AAC 128k |

Variants are skipped if the source resolution is smaller than the preset.

---

## Environment variables

| Variable | Required | Description |
|---|---|---|
| `CMS_URL` | ✅ | Public HTTPS base URL of the CMS |
| `TRANSCODER_SECRET` | ✅ | Shared secret matching `TRANSCODER_SECRET` on CMS |
| `WORK_DIR` | optional | Temp directory inside container (default: `/work`) |

---

## Exit codes

| Code | Meaning |
|---|---|
| `0` | Success (all jobs processed, or no jobs found) |
| `1` | Fatal error (missing env vars, DB unreachable, etc.) |
