feat: Add backfill script for image brightness stats and display them in the UI.

This commit is contained in:
sebseb7
2025-12-19 16:43:20 +01:00
parent 8e02178c3e
commit 4ddbca1246
3 changed files with 304 additions and 19 deletions

View File

@@ -116,14 +116,43 @@ app.post('/upload', authenticate, upload.single('image'), async (req, res) => {
// Generate thumbnail
try {
const image = sharp(req.file.path);
const metadata = await image.metadata(); // Get metadata first
const stats = await image.stats(); // Get stats (includes mean for channels)
// Calculate Average Brightness
// Simple average of R, G, B mean values (0-255)
const brightness = Math.round((stats.channels[0].mean + stats.channels[1].mean + stats.channels[2].mean) / 3);
const thumbnailFilename = req.file.filename.replace(path.extname(req.file.filename), '_thumb.avif');
const thumbnailPath = path.join(path.dirname(req.file.path), thumbnailFilename);
const dirPath = path.dirname(req.file.path);
await sharp(req.file.path)
await image
.resize(320) // Resize to 320px width, auto height
.toFormat('avif')
.toFile(thumbnailPath);
// Update daily stats.json
const statsFile = path.join(dirPath, 'stats.json');
let dailyStats = [];
try {
if (fs.existsSync(statsFile)) {
dailyStats = JSON.parse(fs.readFileSync(statsFile, 'utf8'));
}
} catch (e) { console.error('Error reading stats.json', e); }
dailyStats.push({
timestamp: new Date().toISOString(),
filename: req.file.filename,
brightness
});
// Write back stats
try {
fs.writeFileSync(statsFile, JSON.stringify(dailyStats, null, 2));
} catch (e) { console.error('Error writing stats.json', e); }
res.json({
success: true,
cameraId,
@@ -132,17 +161,18 @@ app.post('/upload', authenticate, upload.single('image'), async (req, res) => {
thumbnail: thumbnailFilename,
thumbnailPath: thumbnailPath,
size: req.file.size,
brightness,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Thumbnail generation failed:', error);
// Still return success for the upload even if thumbnail fails
console.error('Processing failed:', error);
// Still return success for the upload even if processing fails
res.json({
success: true,
cameraId,
filename: req.file.filename,
path: req.file.path,
thumbnailError: error.message,
error: error.message,
size: req.file.size,
timestamp: new Date().toISOString()
});
@@ -378,7 +408,16 @@ app.get('/cameras/:cameraId/:year/:month/:day', async (req, res) => {
// Sort by filename (timestamp) descending
images.sort((a, b) => b.filename.localeCompare(a.filename));
res.json({ images });
// Load stats if available
let stats = [];
try {
const statsFile = path.join(datePath, 'stats.json');
if (fs.existsSync(statsFile)) {
stats = JSON.parse(fs.readFileSync(statsFile, 'utf8'));
}
} catch (e) { console.error('Error reading stats.json', e); }
res.json({ images, stats });
} catch (error) {
res.status(500).json({ error: 'Failed to list images', details: error.message });
}