1be974afbb
Previously watermarks were applied on-the-fly when viewing photos in the lightbox, causing 1+ minute load times for high-resolution images. This change pre-generates watermarked versions during upload and when watermark settings change, enabling instant image loading (~50-100ms). - Add database migration for watermark_path tracking (061) - Add watermarkGeneratorService for batch operations - Extend watermarkService with save-to-disk capability - Modify gallery endpoint to serve pre-generated files - Add background regeneration when branding settings change - Add npm script for migrating existing photos Closes #112
162 lines
5.1 KiB
JavaScript
162 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Script to generate pre-watermarked versions for existing photos
|
|
* This is a one-time migration script to populate watermarks for photos
|
|
* that existed before the pre-generation feature was implemented.
|
|
*
|
|
* Usage: node scripts/generate-watermarks.js [eventId]
|
|
*
|
|
* Options:
|
|
* eventId - Optional: Only generate watermarks for a specific event
|
|
*
|
|
* Examples:
|
|
* node scripts/generate-watermarks.js # Generate for all photos
|
|
* node scripts/generate-watermarks.js 5 # Generate for event ID 5
|
|
*/
|
|
|
|
const path = require('path');
|
|
const { db } = require('../src/database/db');
|
|
const watermarkService = require('../src/services/watermarkService');
|
|
const watermarkGeneratorService = require('../src/services/watermarkGeneratorService');
|
|
|
|
async function generateWatermarks(eventId = null) {
|
|
try {
|
|
console.log('='.repeat(60));
|
|
console.log('PicPeak Watermark Generation Script');
|
|
console.log('='.repeat(60));
|
|
|
|
// Check if watermarking is enabled
|
|
const settings = await watermarkService.getWatermarkSettings();
|
|
|
|
if (!settings || !settings.enabled) {
|
|
console.log('\nWatermarking is currently DISABLED in settings.');
|
|
console.log('Enable watermarking in Admin > Branding settings first.');
|
|
console.log('Exiting without generating watermarks.');
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log('\nWatermark Settings:');
|
|
console.log(` Enabled: ${settings.enabled}`);
|
|
console.log(` Position: ${settings.position}`);
|
|
console.log(` Opacity: ${settings.opacity}%`);
|
|
console.log(` Size: ${settings.size}%`);
|
|
console.log(` Logo: ${settings.logoPath || '(using text fallback)'}`);
|
|
|
|
// Build query
|
|
let query = db('photos')
|
|
.join('events', 'photos.event_id', 'events.id')
|
|
.whereNull('photos.watermark_path')
|
|
.whereNot(function() {
|
|
this.where('photos.media_type', 'video')
|
|
.orWhere('photos.mime_type', 'like', 'video/%');
|
|
})
|
|
.select(
|
|
'photos.id',
|
|
'photos.filename',
|
|
'photos.event_id',
|
|
'events.event_name'
|
|
);
|
|
|
|
if (eventId) {
|
|
query = query.where('photos.event_id', eventId);
|
|
console.log(`\nFiltering to event ID: ${eventId}`);
|
|
}
|
|
|
|
const photos = await query;
|
|
|
|
if (photos.length === 0) {
|
|
console.log('\nNo photos found without watermarks.');
|
|
if (eventId) {
|
|
console.log(`(Checked event ID: ${eventId})`);
|
|
}
|
|
console.log('All photos already have pre-generated watermarks or watermarking is disabled.');
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log(`\nFound ${photos.length} photos without watermarks.`);
|
|
|
|
// Group by event for display
|
|
const eventCounts = {};
|
|
photos.forEach(p => {
|
|
eventCounts[p.event_name] = (eventCounts[p.event_name] || 0) + 1;
|
|
});
|
|
|
|
console.log('\nPhotos by event:');
|
|
Object.entries(eventCounts).forEach(([name, count]) => {
|
|
console.log(` ${name}: ${count} photos`);
|
|
});
|
|
|
|
console.log('\nStarting watermark generation...\n');
|
|
|
|
let successCount = 0;
|
|
let failCount = 0;
|
|
const startTime = Date.now();
|
|
|
|
// Process photos with progress display
|
|
for (let i = 0; i < photos.length; i++) {
|
|
const photo = photos[i];
|
|
const progress = Math.round(((i + 1) / photos.length) * 100);
|
|
|
|
process.stdout.write(`\r[${progress}%] Processing photo ${i + 1}/${photos.length}: ${photo.filename.substring(0, 30)}...`);
|
|
|
|
try {
|
|
const result = await watermarkGeneratorService.generateForPhoto(photo.id);
|
|
|
|
if (result.success) {
|
|
successCount++;
|
|
} else {
|
|
failCount++;
|
|
console.log(`\n Failed: ${photo.filename} - ${result.error}`);
|
|
}
|
|
} catch (error) {
|
|
failCount++;
|
|
console.log(`\n Error: ${photo.filename} - ${error.message}`);
|
|
}
|
|
}
|
|
|
|
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
|
|
console.log('\n');
|
|
console.log('='.repeat(60));
|
|
console.log('Watermark Generation Complete');
|
|
console.log('='.repeat(60));
|
|
console.log(` Total processed: ${photos.length}`);
|
|
console.log(` Successful: ${successCount}`);
|
|
console.log(` Failed: ${failCount}`);
|
|
console.log(` Duration: ${duration} seconds`);
|
|
console.log(` Average: ${(photos.length / parseFloat(duration)).toFixed(1)} photos/second`);
|
|
|
|
if (failCount > 0) {
|
|
console.log('\nSome watermarks failed to generate. Check the errors above.');
|
|
console.log('You can re-run this script to retry failed photos.');
|
|
}
|
|
|
|
process.exit(failCount > 0 ? 1 : 0);
|
|
} catch (error) {
|
|
console.error('\nFatal error:', error.message);
|
|
console.error(error.stack);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Parse command line arguments
|
|
const args = process.argv.slice(2);
|
|
const eventId = args[0] ? parseInt(args[0], 10) : null;
|
|
|
|
if (args[0] && isNaN(eventId)) {
|
|
console.error('Error: eventId must be a number');
|
|
console.log('Usage: node scripts/generate-watermarks.js [eventId]');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Run the script
|
|
generateWatermarks(eventId)
|
|
.then(() => {
|
|
process.exit(0);
|
|
})
|
|
.catch(error => {
|
|
console.error('Unhandled error:', error);
|
|
process.exit(1);
|
|
});
|