36 lines
1.4 KiB
JavaScript
36 lines
1.4 KiB
JavaScript
import { createHash } from 'node:crypto';
|
|
import { readFileSync, statSync } from 'node:fs';
|
|
import { mkdir, stat, rename } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import sharp from 'sharp';
|
|
|
|
const versions = new Map();
|
|
const pending = new Map();
|
|
const encoding = 'webp-q90-v1';
|
|
|
|
export function screenshotVersion(path) {
|
|
const info = statSync(path);
|
|
const signature = `${info.mtimeMs}:${info.ctimeMs}:${info.size}`;
|
|
const cached = versions.get(path);
|
|
if (cached?.signature === signature) return cached.version;
|
|
const version = createHash('sha256').update(encoding).update(readFileSync(path)).digest('hex');
|
|
versions.set(path, { signature, version });
|
|
return version;
|
|
}
|
|
|
|
export async function cachedScreenshot(source, directory, version) {
|
|
const destination = join(directory, `${version}.webp`);
|
|
if (pending.has(destination)) return pending.get(destination);
|
|
const conversion = (async () => {
|
|
await mkdir(directory, { recursive: true });
|
|
try { await stat(destination); return destination; }
|
|
catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
const temporary = `${destination}.${process.pid}.tmp`;
|
|
await sharp(source).webp({ quality: 90 }).toFile(temporary);
|
|
await rename(temporary, destination);
|
|
return destination;
|
|
})();
|
|
pending.set(destination, conversion);
|
|
try { return await conversion; }
|
|
finally { pending.delete(destination); }
|
|
}
|