# Process user-generated content for moderation

Generate thumbnails and metadata from uploads for review queues

Every upload needs to be screened before it goes live. "Feedloop" (a social platform like Reddit or TikTok) processes thousands of video uploads per hour and routes each one through a review queue with representative thumbnails and a web-ready transcode.

## Step 1: Extract thumbnails at intervals

Pull frames at regular intervals so moderators can scan the video without watching it:

<CodeGroup labels={["CLI", "TypeScript", "Python", "curl"]}>
```bash
ittybit image \
  -i https://feedloop-app.com/uploads/user-video-8812.mp4 \
  --start 2 \
  --width 640 \
  --format webp
```

```typescript
const thumbnails = [2, 10, 30, 60].map((start) => ({
  input: 'https://feedloop-app.com/uploads/user-video-8812.mp4',
  kind: 'image',
  options: {
    start,
    width: 640,
    format: 'webp',
  },
}));

for (const task of thumbnails) {
  const res = await fetch('https://api.ittybit.com/jobs', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.ITTYBIT_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(task),
  });
  const data = await res.json();
}
```

```python

api_key = "your_api_key"

for start in [2, 10, 30, 60]:
    task = {
        "input": "https://feedloop-app.com/uploads/user-video-8812.mp4",
        "kind": "image",
        "options": {
            "start": start,
            "width": 640,
            "format": "webp",
        },
    }

    res = requests.post(
        "https://api.ittybit.com/jobs",
        headers={"Authorization": f"Bearer {api_key}"},
        json=task,
    )
    data = res.json()
```

```bash
TASK='{
  "input": "https://feedloop-app.com/uploads/user-video-8812.mp4",
  "kind": "image",
  "options": {
    "start": 2,
    "width": 640,
    "format": "webp"
  }
}'

curl -X POST https://api.ittybit.com/jobs \
  -H "Authorization: Bearer $ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$TASK"
```

</CodeGroup>

Repeat with `start` values at 10, 30, and 60 to cover the full video. Moderators see a filmstrip without downloading the original.

## Step 2: Transcode for the review player

Moderators need a lightweight preview. Transcode to a capped resolution so the review UI loads fast:

```json
{
  "input": "https://feedloop-app.com/uploads/user-video-8812.mp4",
  "kind": "video",
  "options": {
    "width": 720,
    "format": "mp4",
    "quality": "medium"
  }
}
```

## CLI

Run both steps locally:

```bash
# Thumbnails at 2s, 10s, 30s, 60s
for t in 2 10 30 60; do
  ittybit image \
    -i user-video-8812.mp4 \
    -o "thumb-${t}s.webp" \
    --start $t \
    --width 640
done

# Review-quality transcode
ittybit video \
  -i user-video-8812.mp4 \
  -o review-preview.mp4 \
  --width 720 \
  --quality medium
```

## Feeding the review queue

A typical webhook payload after both tasks complete:

```json
{
  "type": "job.succeeded",
  "data": {
    "id": "task_abc123",
    "kind": "image",
    "status": "completed",
    "output": {
      "url": "https://cdn.ittybit.com/feedloop/thumb-2s.webp"
    }
  }
}
```

Listen for `job.succeeded` events, collect the thumbnail URLs and the preview MP4, then insert a row into your moderation queue with the original upload ID and the generated assets.

## Moderation pipeline summary

| Step       | Kind    | Output          | Purpose                     |
| ---------- | ------- | --------------- | --------------------------- |
| Thumbnails | `image` | 4 x WebP stills | Visual scan for reviewers   |
| Transcode  | `video` | 720p MP4        | In-browser preview playback |

Thumbnails arrive in seconds. The transcode follows. Once both are done, the upload is ready for a human decision.