Skip to content
Open
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b81f2cf
Allow all origins
sktbrd Aug 19, 2025
22b9542
Merge pull request #1 from sktbrd/codex/add-cors-middleware-with-env-…
sktbrd Aug 19, 2025
f5b8e45
Add CORS support and environment configuration for Pinata JWT
sktbrd Sep 5, 2025
d47cddc
Merge branch 'main' of https://github.com/bgrana75/video-worker
sktbrd Sep 5, 2025
d9dbbfe
enhance
sktbrd Sep 5, 2025
9cd440a
Enhanced video-worker with rich logging, CORS fixes, and dashboard in…
sktbrd Sep 5, 2025
a75cedd
feat: Enhance CORS configuration and logging in video transcoding ser…
sktbrd Sep 5, 2025
583b291
feat: Add logging and monitoring features with dashboard integration …
sktbrd Sep 6, 2025
2d71de9
feat: Refactor logger and server for improved user data handling and …
sktbrd Sep 6, 2025
6f1fe0c
enhance
sktbrd Sep 6, 2025
0f2110c
feat: Preserve device context in transcode completion and error logs
sktbrd Sep 6, 2025
4af01be
feat: Update user data extraction to use req.body for improved form h…
sktbrd Sep 6, 2025
f9b62a7
enhance
sktbrd Sep 6, 2025
85f6efa
feat: Simplify user data extraction in transcode endpoint by removing…
sktbrd Sep 6, 2025
27ca0d8
feat: Preserve device context in transcode completion and error logging
sktbrd Sep 6, 2025
fee06f8
Refactor: Remove old server and test scripts; consolidate logging and…
sktbrd Sep 6, 2025
46b6637
aaa logs
sktbrd Sep 13, 2025
0f2b16e
feat: Add docker-compose configuration for video worker service
sktbrd Sep 13, 2025
21d2d98
feat: Add new transcode log entries for user 'xvlad' including device…
sktbrd Sep 13, 2025
aa26ef3
chore: Update transcode log format for consistency and clarity
sktbrd Sep 13, 2025
885e7c3
feat: Add transcode log entries for user 'xvlad' with device and plat…
sktbrd Sep 13, 2025
c3dbd5d
Merge branch 'main' of https://github.com/sktbrd/skatehive-transcoder
sktbrd Sep 13, 2025
1cfeb8d
fix: Resolve merge conflict in transcode log and update entries for u…
sktbrd Sep 16, 2025
af4c6b1
Implement feature X to enhance user experience and fix bug Y in module Z
sktbrd Oct 27, 2025
670edd2
docs: update README with accurate port configuration and production s…
sktbrd Dec 5, 2025
689deb1
Refactor code structure for improved readability and maintainability
sktbrd Dec 5, 2025
a6fe339
chore: Ignore logs directory to prevent conflicts across nodes
sktbrd Dec 12, 2025
cf7c3cd
Adds recent transcode log entries and prunes old ones
sktbrd Dec 12, 2025
551813f
Add new transcode log entries and handle error for test video
sktbrd Dec 12, 2025
0f77ab0
fix: handle null userHP value in transcode request
sktbrd Dec 12, 2025
a8ea34d
fix: ensure req.body is defined in transcode request handler
sktbrd Dec 12, 2025
6b99ada
feat: enhance logging with NODE_NAME for unique log files and improve…
sktbrd Dec 12, 2025
13783b2
Adds node field to log output
sktbrd Dec 12, 2025
687ef40
feat(transcoder): add SSE progress streaming for real-time upload pro…
sktbrd Dec 12, 2025
a6f2e5e
feat: add documentation for real-time progress streaming using SSE
sktbrd Dec 12, 2025
8787520
feat: enhance health check endpoint to support HTML response format
sktbrd Jan 18, 2026
97d6f4e
feat(transcoder): enhance transcode endpoint with optional user and a…
sktbrd Jan 19, 2026
524ece7
feat: add Pinata Groups support and standardize metadata schema
sktbrd Mar 19, 2026
c1d7c96
fix: reduce Pinata keyvalues to 10 max (Pinata limit)
sktbrd Mar 19, 2026
83581c9
feat: smart transcoding — skip already-optimized videos, adaptive qua…
sktbrd Mar 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ PINATA_JWT=eyJhbGciOi...
# X264_CRF=22
# AAC_BITRATE=128k
# PORT=8080

# CORS: all origins allowed by default
NODE_ENV=development
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Ignore environment variables file
.env

node_modules
# Logs (generated per-node, should not be tracked)
logs/
*.log
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Simple ffmpeg + Node worker
FROM node:20-bullseye-slim

# Install ffmpeg
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg ca-certificates \
# Install ffmpeg and curl for health checks
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /app
Expand Down
135 changes: 132 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,97 @@ A tiny API that accepts a file upload, transcodes it to MP4 (H.264/AAC) with FFm

- `GET /healthz` — health check
- `POST /transcode` — multipart/form-data with a single field named `video`
- `GET /progress/:requestId` — **SSE (Server-Sent Events)** for real-time progress streaming
- `GET /logs` — get recent transcode operations (JSON)
- `GET /stats` — get transcoding statistics (JSON)

**Response**
```json
{ "cid": "bafy...", "gatewayUrl": "https://gateway.pinata.cloud/ipfs/bafy..." }
{
"success": true,
"data": {
"cid": "bafy...",
"gatewayUrl": "https://gateway.pinata.cloud/ipfs/bafy..."
}
}
```

## 🆕 Real-Time Progress Streaming (SSE)

The service now supports **Server-Sent Events (SSE)** for real-time progress updates during transcoding:

### How It Works

1. **Client generates a unique `correlationId`** before uploading
2. **Client opens SSE connection** to `/progress/:correlationId`
3. **Client sends POST to `/transcode`** with the same `correlationId` in form data
4. **Server broadcasts progress** to all connected SSE clients for that request

### Progress Stages

| Stage | Progress Range | Description |
|-------|---------------|-------------|
| `waiting` | 0% | SSE connected, waiting for upload |
| `receiving` | 5% | Server receiving file |
| `transcoding` | 10-80% | FFmpeg processing (based on video duration) |
| `uploading` | 80-100% | Uploading to Pinata IPFS |
| `complete` | 100% | Done! |
| `error` | 0% | Something went wrong |

### SSE Client Example

```javascript
// Generate unique ID
const requestId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 6)}`;

// Open SSE connection BEFORE uploading
const eventSource = new EventSource(`https://server/progress/${requestId}`);
eventSource.onmessage = (event) => {
const { progress, stage } = JSON.parse(event.data);
console.log(`Progress: ${progress}% - ${stage}`);
updateProgressBar(progress);
};

// Send upload with correlationId
const formData = new FormData();
formData.append('video', file);
formData.append('correlationId', requestId);
fetch('https://server/transcode', { method: 'POST', body: formData });
```

### Terminal Test

```bash
# Test SSE progress with curl
TEST_ID="test-$(date +%s)" && \
(curl -sN "https://minivlad.tail83ea3e.ts.net/video/progress/$TEST_ID" &) && \
sleep 1 && \
curl -X POST "https://minivlad.tail83ea3e.ts.net/video/transcode" \
-F "video=@/path/to/video.mov" \
-F "correlationId=$TEST_ID"
```

## Logging & Monitoring

The service now includes rich structured logging that tracks:
- User/creator information
- File details (name, size)
- Processing duration
- Success/failure status
- Client IP addresses
- IPFS CIDs and gateway URLs

**Logging Features:**
- Maintains last 100 operations in `logs/transcode.log`
- JSON-structured log entries for easy parsing
- Dashboard-friendly endpoints
- Rich console output with emojis and formatting

**Dashboard Integration:**
- `GET /logs?limit=N` - Returns recent operations for dashboard display
- `GET /stats` - Returns aggregated statistics (success rate, avg duration, etc.)
- Designed to work with the Skatehive dashboard monitoring system

## Quickstart (Docker)

```bash
Expand All @@ -22,18 +107,62 @@ cp .env.example .env

# 3) Build & run
docker build -t video-worker .

# Development (port 8080):
docker run --env-file .env -p 8080:8080 --name video-worker video-worker

# 4) Test
# Production (port 8081 external, 8080 internal):
docker run --env-file .env -p 8081:8080 --name video-worker video-worker

# Or use docker-compose (recommended for production):
docker compose up -d
```

```bash
# 4) Test (adjust port based on deployment)
curl -F "video=@/path/to/input.mov" http://localhost:8080/transcode

# 5) Test logging system (creates mock log entries)
npm run test-logs

# 6) Check logs and stats
curl http://localhost:8080/logs
curl http://localhost:8080/stats
```
```

## Environment

- `PINATA_JWT` (required) — Create in Pinata Dashboard → API Keys (JWT).
- `PINATA_GATEWAY` (optional) — Defaults to `https://gateway.pinata.cloud/ipfs`.
- `MAX_UPLOAD_MB` (optional) — Upload limit, default `512`.
- `MAX_UPLOAD_MB` (optional) — Upload limit, default `512` (set to `200` on Mac Mini M4).
- `X264_PRESET`, `X264_CRF`, `AAC_BITRATE` — FFmpeg tuning knobs.
- `PORT` (optional) — Internal port, defaults to `8080`.
- CORS is open to all origins by default.
- `NODE_ENV` — Environment mode (`development` or `production`).

## Production Deployment (Mac Mini M4)

**Current Live Configuration:**

- **External URL:** `https://minivlad.tail83ea3e.ts.net/video/transcode`
- **External Port:** `8081`
- **Internal Port:** `8080`
- **Container:** `video-worker`
- **Upload Limit:** `200MB`
- **Network:** Tailscale Funnel (publicly accessible)

**Port Mapping:**
```yaml
# docker-compose.yml
ports:
- "8081:8080" # Host:Container
```

This means:
- Service listens on port `8080` inside the container
- Accessible on port `8081` from the host (Mac Mini)
- Tailscale Funnel routes `https://minivlad.tail83ea3e.ts.net/video/*` to port `8081`

## Deploy Options

Expand Down
21 changes: 21 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: '3.8'

services:
video-worker:
build: .
ports:
- "8081:8080"
env_file:
- .env
environment:
- PORT=8080
- NODE_NAME=${NODE_NAME:-macmini}
volumes:
- ./logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:8080/healthz', (r) => process.exit(r.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
interval: 30s
timeout: 10s
retries: 3
container_name: video-worker
Loading