The Studio service provides flexible media management capabilities for images, videos, audio, and other file types from your IoT devices.
Overview
Studio supports multiple methods for publishing and managing media events:
- File-based upload: Simple directory-based file drop system
- Harvesting: Automatic polling of device events
- Direct upload: HTTP POST endpoint for file uploads
- URL-based upload: Pre-signed URLs for secure uploads
- Hook system: Event-driven automation
Directory structure
Studio automatically creates a directory structure for each device:
data/gallery/
├── device1/
│ ├── incoming/ # Drop new files here
│ ├── processed/ # Successfully processed files
│ └── thumbnails/ # Optional thumbnail files
└── device2/
├── incoming/
├── processed/
└── thumbnails/
File-based upload
The simplest way to publish media events is by copying files to the device’s incoming directory.
Get device paths
Query the Studio API to get the device-specific upload paths:curl http://localhost:8001/api/gallery/device123/paths
Response:{
"success": true,
"paths": {
"incoming": "/path/to/data/gallery/device123/incoming",
"thumbnails": "/path/to/data/gallery/device123/thumbnails"
}
}
Copy event files
Copy your media files to the incoming directory:cp capture.jpg /path/to/data/gallery/device123/incoming/
Add thumbnails (optional)
If you have thumbnails, copy them with the same filename as the event file:cp thumb.jpg /path/to/data/gallery/device123/thumbnails/capture.jpg
Thumbnail files must have the same name as the corresponding event file for automatic association.
Automatic processing
The Studio service will automatically:
- Detect new files in the incoming directory
- Create gallery records with metadata
- Move processed files to the processed directory
- Associate thumbnails if present
- Trigger configured hooks
Automated upload script
Create a script to automate file uploads:
#!/bin/bash
EVENT_FILE=$1
THUMB_FILE=$2
DEVICE_ID="device123"
# Get device paths
PATHS=$(curl -s "http://localhost:8001/api/gallery/$DEVICE_ID/paths")
INCOMING_PATH=$(echo $PATHS | jq -r '.paths.incoming')
THUMB_PATH=$(echo $PATHS | jq -r '.paths.thumbnails')
# Copy event file
cp "$EVENT_FILE" "$INCOMING_PATH/"
# Copy thumbnail if provided
if [ -n "$THUMB_FILE" ]; then
EVENT_NAME=$(basename "$EVENT_FILE")
THUMB_NAME="${EVENT_NAME%.*}.jpg"
cp "$THUMB_FILE" "$THUMB_PATH/$THUMB_NAME"
fi
echo "Upload complete for $EVENT_FILE"
Direct HTTP upload
Upload media files directly via HTTP POST:
# Upload image with metadata
curl -X POST "http://localhost:8001/api/gallery/device123/upload" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=capture.jpg" \
-F "event=@/path/to/capture.jpg" \
-F "media_type=image" \
-F "metadata={\"timestamp\":\"2026-03-02T10:00:00Z\"}"
# Upload video with thumbnail
curl -X POST "http://localhost:8001/api/gallery/device123/upload" \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "name=video.mp4" \
-F "event=@/path/to/video.mp4" \
-F "thumbnail=@/path/to/thumb.jpg" \
-F "has_thumbnail=true" \
-F "media_type=video"
Pre-signed URL upload
For secure uploads from untrusted clients, use pre-signed URLs:
Request upload URLs
curl "http://localhost:8001/api/gallery/device123/upload-url?filename=event.mp4&thumbnail=true" \
-H "Authorization: Bearer YOUR_TOKEN"
Response:{
"success": true,
"upload_url": "https://...",
"thumbnail_upload_url": "https://..."
}
Upload to pre-signed URLs
# Upload event file
curl -X PUT "$UPLOAD_URL" --upload-file /path/to/event.mp4
# Upload thumbnail
curl -X PUT "$THUMBNAIL_UPLOAD_URL" --upload-file /path/to/thumb.jpg
Pre-signed URLs expire after a limited time. Upload your files promptly after receiving the URLs.
Hook system
Hooks allow you to execute device actions automatically when specific events occur.
Available hook events
Studio supports 7 hook event types:
after_event_pulled - After an event is successfully pulled from device
after_all_events_pulled - After all events in a batch are processed
after_event_processed - After an event is processed and stored
after_thumbnail_generated - After a thumbnail is generated
on_file_detected - When a new file is detected in incoming directory
on_error - When an error occurs during processing
on_service_start - When the media service starts for a device
Creating hooks
Create a hook
curl -X POST "http://localhost:8001/api/hooks" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"event_type": "after_event_processed",
"device_id": "device123",
"action_id": "notify-on-event",
"enabled": true
}'
Define the action
Create the corresponding device action in PocketBase:{
"name": "notify-on-event",
"device": "device123",
"command": "curl -X POST https://api.slack.com/webhook -d '{\"text\":\"New event: {{event_name}}\"}"
}
Test the hook
Upload a file and verify the hook executes:cp test.jpg /path/to/data/gallery/device123/incoming/
Hook parameter injection
Hooks support automatic parameter injection in action commands:
# Process event with ffmpeg
ffmpeg -i {{file_path}} -vf scale=640:480 /processed/{{event_name}}
# Archive event to cloud storage
aws s3 cp {{file_path}} s3://bucket/{{device_id}}/{{date}}/{{event_name}}
# Generate custom thumbnail
ffmpeg -i {{file_path}} -vf scale=320:240 -frames:v 1 /thumbs/{{event_name}}.jpg
Available parameters:
{{device_id}} - Current device ID
{{event_name}} - Event filename
{{media_type}} - Media type (image, video, audio)
{{file_path}} - Full file path
{{timestamp}} - Current timestamp
{{date}} - Current date (YYYY-MM-DD)
Managing hooks
# List all hooks
curl http://localhost:8001/api/hooks
# List hooks for specific device
curl "http://localhost:8001/api/hooks?device=device123"
# Get hooks by event type
curl http://localhost:8001/api/hooks/events/after_event_processed
# Update hook
curl -X PATCH "http://localhost:8001/api/hooks/HOOK_ID" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
# Delete hook
curl -X DELETE "http://localhost:8001/api/hooks/HOOK_ID"
File watcher management
Studio automatically manages file watchers to monitor incoming directories.
Automatic management
- File watchers are created for all devices on service startup
- Watchers sync with device list every 5 minutes
- Orphaned watchers are automatically cleaned up
- Failed watchers are automatically restarted
Manual management
# Check all watcher statuses
curl http://localhost:8001/api/watchers
# Add watcher for new device
curl -X POST http://localhost:8001/api/watchers/device123/add
# Remove watcher
curl -X DELETE http://localhost:8001/api/watchers/device123
# Sync watchers with device list
curl -X POST http://localhost:8001/api/watchers/sync
# Check specific device watcher
curl http://localhost:8001/api/watchers/device123/status
# Refresh all watchers
curl -X POST http://localhost:8001/api/watchers/refresh
# Clean up orphaned watchers
curl -X POST http://localhost:8001/api/watchers/cleanup-orphaned
If you add or remove devices frequently, use the sync endpoint to ensure watchers are up to date without restarting the service.
Studio automatically classifies media based on file extensions:
- Image:
.jpg, .jpeg, .png, .gif, .bmp, .webp
- Video:
.mp4, .avi, .mov, .mkv, .webm
- Audio:
.mp3, .wav, .ogg, .flac, .aac
- Document:
.pdf, .doc, .docx, .txt
- Other: Any other file type
# Get all images
curl "http://localhost:8001/api/gallery/device123/events?media_type=image"
# Get all videos
curl "http://localhost:8001/api/gallery/device123/events?media_type=video"
# Get statistics by type
curl http://localhost:8001/api/gallery/device123/stats
Configure device actions to list, retrieve, and remove media files.
List events action
# Create list-events action
curl -X POST "http://localhost:8090/api/collections/actions/records" \
-H "Content-Type: application/json" \
-d '{
"name": "list-events",
"device": "device123",
"command": "ls -la /events"
}'
Remove event action
# Create remove-event action with parameter
curl -X POST "http://localhost:8090/api/collections/actions/records" \
-H "Content-Type: application/json" \
-d '{
"name": "remove-event",
"device": "device123",
"command": "rm -f /events/{{event_name}}"
}'
Bulk cleanup action
# Remove events older than 30 days
curl -X POST "http://localhost:8090/api/collections/actions/records" \
-H "Content-Type: application/json" \
-d '{
"name": "cleanup-old-events",
"device": "device123",
"command": "find /events -type f -mtime +30 -delete"
}'
Always test removal commands carefully to avoid accidentally deleting important files. Consider implementing backup procedures before bulk deletions.
Troubleshooting
Files not being detected
-
Check watcher status:
curl http://localhost:8001/api/watchers/device123/status
-
Verify file permissions on incoming directory
-
Check Studio service logs:
-
Manually refresh watchers:
curl -X POST http://localhost:8001/api/watchers/refresh
Hooks not executing
-
Verify hook is enabled:
curl http://localhost:8001/api/hooks
-
Check that the associated action exists and is valid
-
Verify the event type matches the trigger condition
-
Check Studio logs for hook execution errors
Thumbnails not associating
-
Ensure thumbnail filename exactly matches event filename
-
Verify thumbnail is placed in the correct thumbnails directory
-
Check that
has_thumbnail flag is set when using direct upload
-
Verify file permissions allow Studio to read the thumbnail