Processing User-Generated Content at Scale
Dispatched Team
12/28/2024
#tutorial#content#processing
The UGC Challenge
Processing user-generated content is tricky. Images need resizing, videos need transcoding, and content needs moderation. Do this in a single request, and you'll hit timeouts. Let's fix that.
Image Processing Pipeline
app.post('/upload-image', async (req, res) => {
const image = req.files.image;
// 1. Upload original to storage
const imageUrl = await uploadToStorage(image);
const apiKey = process.env.DISPATCHED_API_KEY;
// 2. Queue processing job
const response = await fetch('https://dispatched.dev/api/jobs/dispatch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
payload: {
task: 'process_image',
imageUrl,
sizes: [
{ width: 800, height: 600 },
{ width: 400, height: 300 },
{ width: 200, height: 150 }
]
}
})
});
const { jobId } = await response.json();
res.json({ jobId, originalUrl: imageUrl });
})
// Process images in background
app.post('/webhooks/dispatched', express.json(), async (req, res) => {
const { payload } = req.body;
try {
// Download original
const image = await downloadFromStorage(payload.imageUrl);
// Generate variants
const variants = await Promise.all(
payload.sizes.map(async size => {
const resized = await sharp(image)
.resize(size.width, size.height)
.jpeg({ quality: 80 })
.toBuffer();
const url = await uploadToStorage(
resized,
`${payload.imageUrl}-${size.width}x${size.height}.jpg`
);
return { size, url };
})
);
res.json({ status: 'success', variants });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Video Processing Example
app.post('/upload-video', async (req, res) => {
const video = req.files.video;
const videoUrl = await uploadToStorage(video);
const apiKey = process.env.DISPATCHED_API_KEY;
await fetch('https://dispatched.dev/api/jobs/dispatch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
payload: {
task: 'process_video',
videoUrl,
formats: ['mp4', 'webm'],
generateThumbnails: true
}
})
});
res.json({ status: 'processing', videoUrl });
})
Content Moderation
async function moderateContent(payload) {
const content = await downloadFromStorage(payload.contentUrl);
// Run moderation checks
const [toxicityScore, nsfw] = await Promise.all([
checkToxicity(content),
checkNSFW(content)
]);
if (toxicityScore > 0.8 || nsfw) {
await markContentAsFlagged(payload.contentUrl);
return false;
}
return true;
}
Best Practices
- Upload files to storage first
- Process in background via webhooks
- Generate multiple formats/sizes
- Store results back in storage
- Update database with new URLs
Performance Stats
- Average processing time:
- Images: 2-3 seconds
- Videos: 1-5 minutes
- Supports files up to 5GB
- Parallel processing of variants
Start processing content at scale. Try Dispatched today.