147 lines
3.6 KiB
JavaScript
147 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Script to preprocess image directories and generate blurhashes
|
|
* to be stored in files.lst files.
|
|
*
|
|
* Usage:
|
|
* node generate-blurhashes.js <directory>
|
|
*
|
|
* Example:
|
|
* node generate-blurhashes.js ../content/motion_source/monstera1
|
|
*/
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const { createCanvas, loadImage } = require("canvas");
|
|
const { encode } = require("blurhash");
|
|
|
|
// Configuration
|
|
const COMPONENT_X = 4; // Number of X components in blurhash
|
|
const COMPONENT_Y = 3; // Number of Y components in blurhash
|
|
|
|
/**
|
|
* Calculate blurhash for an image
|
|
* @param {string} imagePath - Path to the image
|
|
* @returns {Promise<string>} The blurhash
|
|
*/
|
|
async function calculateBlurhash(imagePath) {
|
|
try {
|
|
const image = await loadImage(imagePath);
|
|
const canvas = createCanvas(image.width, image.height);
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
// Draw image to canvas
|
|
ctx.drawImage(image, 0, 0);
|
|
|
|
// Get image data
|
|
const imageData = ctx.getImageData(0, 0, image.width, image.height);
|
|
const pixels = imageData.data;
|
|
|
|
// Encode blurhash
|
|
const hash = encode(
|
|
pixels,
|
|
image.width,
|
|
image.height,
|
|
COMPONENT_X,
|
|
COMPONENT_Y
|
|
);
|
|
return hash;
|
|
} catch (err) {
|
|
console.error(`Error processing ${imagePath}:`, err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process a directory and update its files.lst with blurhashes
|
|
* @param {string} dirPath - Path to the directory containing images
|
|
*/
|
|
async function processDirectory(dirPath) {
|
|
console.log(`Processing directory: ${dirPath}`);
|
|
|
|
// Read directory for image files
|
|
const files = fs
|
|
.readdirSync(dirPath)
|
|
.filter((file) =>
|
|
[".jpg", ".jpeg", ".png", ".webp", ".gif"].includes(
|
|
path.extname(file).toLowerCase()
|
|
)
|
|
)
|
|
.sort((a, b) => {
|
|
// Natural sort for numbered files (e.g., img001.jpg, img002.jpg)
|
|
const numA = parseInt(a.match(/\d+/) || "0");
|
|
const numB = parseInt(b.match(/\d+/) || "0");
|
|
return numA - numB;
|
|
});
|
|
|
|
if (files.length === 0) {
|
|
console.warn(`No image files found in ${dirPath}`);
|
|
return;
|
|
}
|
|
|
|
// Process each file and collect blurhashes
|
|
const fileData = [];
|
|
let count = 0;
|
|
const total = files.length;
|
|
|
|
for (const file of files) {
|
|
count++;
|
|
const filePath = path.join(dirPath, file);
|
|
process.stdout.write(`\rProcessing image ${count}/${total}: ${file}`);
|
|
|
|
const hash = await calculateBlurhash(filePath);
|
|
if (hash) {
|
|
fileData.push({ file, hash });
|
|
} else {
|
|
// Include file without hash if hash calculation fails
|
|
fileData.push({ file, hash: null });
|
|
}
|
|
}
|
|
|
|
console.log("\nWriting files.lst...");
|
|
|
|
// Write to files.lst
|
|
const outputPath = path.join(dirPath, "files.lst");
|
|
const fileLines = fileData.map((entry) => {
|
|
if (entry.hash) {
|
|
return `${entry.file}\t${entry.hash}`;
|
|
}
|
|
return entry.file;
|
|
});
|
|
|
|
fs.writeFileSync(outputPath, fileLines.join("\n"));
|
|
console.log(
|
|
`Created files.lst with ${fileData.length} entries (${
|
|
fileData.filter((e) => e.hash).length
|
|
} with blurhashes)`
|
|
);
|
|
}
|
|
|
|
// Main execution
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.length === 0) {
|
|
console.error("Please provide a directory path");
|
|
process.exit(1);
|
|
}
|
|
|
|
const dirPath = args[0];
|
|
|
|
if (!fs.existsSync(dirPath)) {
|
|
console.error(`Directory does not exist: ${dirPath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!fs.statSync(dirPath).isDirectory()) {
|
|
console.error(`Not a directory: ${dirPath}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
await processDirectory(dirPath);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("Error:", err);
|
|
process.exit(1);
|
|
});
|