camera-settings.json.
PicUpper - Webcam Picture Upload Service
A Node.js service that receives pictures from webcams and stores them in a structured way for creating time-lapse movies.
Features
- Multi-camera support: Handle uploads from multiple webcams simultaneously
- API key authentication: Secure your upload endpoint
- Time-based storage: Images organized by camera/year/month/day for easy time-lapse creation
- High-frequency uploads: Handles uploads up to every second
Installation
cd /path/to/picUpper
npm install
User Management
PicUpper supports multiple users, each with their own API key. Manage users with the CLI tool:
# Add a new user (returns the API key - save it!)
node manage-users.js add webcam1 "Front door camera"
node manage-users.js add backyard "Backyard camera"
# List all users
node manage-users.js list
# Regenerate API key for a user
node manage-users.js regenerate webcam1
# Disable/enable a user
node manage-users.js disable webcam1
node manage-users.js enable webcam1
# Remove a user
node manage-users.js remove webcam1
Users are stored in users.json. Each user has an individual API key that can be used for authentication.
Configuration
| Variable | Required | Description |
|---|---|---|
PICUPPER_PORT |
Yes | Port the service listens on |
PICUPPER_UPLOAD_DIR |
No | Custom upload directory (default: ./uploads) |
PICUPPER_USERS_FILE |
No | Custom users file path (default: ./users.json) |
Running the Service
# First, create at least one user
node manage-users.js add mywebcam "My webcam"
# Start the service
export PICUPPER_PORT=3080
npm start
API Reference
Health Check
GET /health
No authentication required. Returns service status.
Response:
{
"status": "ok",
"timestamp": "2025-12-18T12:00:00.000Z",
"uptime": 1234.56
}
Upload Picture
POST /upload
Upload a picture from a webcam.
Headers:
X-API-Key: Your API key
Form Data:
image: The image file (JPEG, PNG, WebP, or GIF)cameraId: Identifier for the camera (optional, default: "default")
Example with curl:
curl -X POST \
-H "X-API-Key: your-secret-key" \
-F "image=@snapshot.jpg" \
-F "cameraId=front-door" \
http://localhost:3080/upload
Response:
{
"success": true,
"cameraId": "front-door",
"filename": "front-door_2025-12-18T12-00-00-000Z.jpg",
"path": "/path/to/uploads/front-door/2025/12/18/front-door_2025-12-18T12-00-00-000Z.jpg",
"size": 45678,
"timestamp": "2025-12-18T12:00:00.000Z"
}
List Cameras
GET /cameras
List all cameras that have uploaded images.
Headers:
X-API-Key: Your API key
Example:
curl -H "X-API-Key: your-secret-key" http://localhost:3080/cameras
Camera Statistics
GET /stats/:cameraId
Get upload statistics for a specific camera.
Example:
curl -H "X-API-Key: your-secret-key" http://localhost:3080/stats/front-door
Response:
{
"cameraId": "front-door",
"totalImages": 1440,
"totalSizeBytes": 72000000,
"totalSizeMB": "68.66",
"oldestImage": "2025-12-01T00:00:00.000Z",
"newestImage": "2025-12-18T12:00:00.000Z"
}
Get Camera Settings
GET /settings/:cameraId
Get v4l2 camera settings (focus, exposure, etc.) for a camera.
Example:
curl -H "X-API-Key: your-secret-key" http://localhost:3080/settings/front-door
Response:
{
"cameraId": "front-door",
"settings": {
"focus_automatic_continuous": 0,
"focus_absolute": 30,
"exposure_auto": 1,
"exposure_absolute": 200,
"brightness": 0,
"contrast": 32
}
}
Update Camera Settings
PUT /settings/:cameraId
Update camera settings. Settings are applied by the capture client via v4l2-ctl.
Example:
curl -X PUT \
-H "X-API-Key: your-secret-key" \
-H "Content-Type: application/json" \
-d '{"focus_absolute": 40, "exposure_absolute": 250}' \
http://localhost:3080/settings/front-door
Storage Structure
Images are stored in a hierarchical directory structure optimized for time-lapse processing:
uploads/
└── {cameraId}/
└── {YYYY}/
└── {MM}/
└── {DD}/
└── {cameraId}_{timestamp}.jpg
This structure allows files to be concatenated in chronological order for movie creation.
Nginx Configuration
Current deployment: https://dev.seedheads.de/picUploadApi/
To expose the API under /picUploadApi/ on your website, add this to your nginx configuration:
location /picUploadApi/ {
# Strip the /picUploadApi prefix when proxying
rewrite ^/picUploadApi/(.*)$ /$1 break;
proxy_pass http://127.0.0.1:3080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Important for file uploads
client_max_body_size 5M;
proxy_request_buffering off;
}
After adding, reload nginx:
sudo nginx -t && sudo systemctl reload nginx
Now you can access the API at https://yourwebsite.com/picUploadApi/upload.
Creating Time-Lapse Movies
Once you have collected images, you can create a time-lapse movie using ffmpeg:
# Navigate to a specific day's images
cd uploads/front-door/2025/12/18
# Create a video at 30fps (each frame shows for 1/30th of a second)
ffmpeg -pattern_type glob -i '*.jpg' -c:v libx264 -pix_fmt yuv420p -r 30 timelapse.mp4
# Or create a slower video at 10fps
ffmpeg -pattern_type glob -i '*.jpg' -c:v libx264 -pix_fmt yuv420p -r 10 timelapse-slow.mp4
For multi-day time-lapses:
# Create a file list
find uploads/front-door/2025/12 -name '*.jpg' | sort > filelist.txt
# Convert to ffmpeg format
sed "s/^/file '/; s/$/'/" filelist.txt > ffmpeg-list.txt
# Create video from list
ffmpeg -f concat -safe 0 -i ffmpeg-list.txt -c:v libx264 -pix_fmt yuv420p -r 30 december-timelapse.mp4
Webcam Upload Script Example
Here's a simple bash script to capture and upload from a webcam:
#!/bin/bash
# capture-and-upload.sh
API_URL="http://localhost:3080/upload"
API_KEY="your-secret-key"
CAMERA_ID="webcam1"
TEMP_FILE="/tmp/webcam_snapshot.jpg"
# Capture from webcam (requires fswebcam)
fswebcam -r 1920x1080 --no-banner "$TEMP_FILE"
# Upload to PicUpper
curl -s -X POST \
-H "X-API-Key: $API_KEY" \
-F "image=@$TEMP_FILE" \
-F "cameraId=$CAMERA_ID" \
"$API_URL"
rm -f "$TEMP_FILE"
Run it every minute with cron:
* * * * * /path/to/capture-and-upload.sh
License
MIT