Files
shopApiNg/picture-syncer.js

102 lines
3.5 KiB
JavaScript

import fs from 'fs/promises';
import path from 'path';
import sharp from 'sharp';
import { createConnection } from './database.js';
class PictureSyncer {
constructor() {
if (PictureSyncer.instance) {
return PictureSyncer.instance;
}
this.cacheBaseDir = process.env.CACHE_LOCATION || '.';
PictureSyncer.instance = this;
}
async syncImages(imageIds, groupName) {
const groupDir = path.join(this.cacheBaseDir, 'img', groupName);
// Ensure directory exists
await fs.mkdir(groupDir, { recursive: true });
// Get existing files
let existingFiles = [];
try {
existingFiles = await fs.readdir(groupDir);
} catch (err) {
// Directory might be empty or new
}
// Filter for image files (assuming we save as {id}.avif)
const existingIds = existingFiles
.filter(f => f.endsWith('.avif'))
.map(f => parseInt(f.replace('.avif', '')));
const validIds = new Set(imageIds.filter(id => id !== null && id !== undefined));
// 1. Delete obsolete images
const toDelete = existingIds.filter(id => !validIds.has(id));
for (const id of toDelete) {
const filePath = path.join(groupDir, `${id}.avif`);
await fs.unlink(filePath);
}
if (toDelete.length > 0) {
console.log(`🗑️ Deleted ${toDelete.length} obsolete images.`);
}
// 2. Download missing images
const toDownload = imageIds.filter(id => id !== null && id !== undefined && !existingIds.includes(id));
if (toDownload.length > 0) {
console.log(`📥 Downloading ${toDownload.length} new images for group '${groupName}'...`);
await this._downloadImages(toDownload, groupDir);
} else {
console.log(`✅ No new images to download for group '${groupName}'.`);
}
}
async _downloadImages(ids, dir) {
let pool;
try {
pool = await createConnection();
// Process in chunks to avoid huge queries
const chunkSize = 50;
for (let i = 0; i < ids.length; i += chunkSize) {
const chunk = ids.slice(i, i + chunkSize);
const list = chunk.join(',');
const result = await pool.request().query(`
SELECT kBild, bBild
FROM tBild
WHERE kBild IN (${list})
`);
for (const record of result.recordset) {
if (record.bBild) {
const filePath = path.join(dir, `${record.kBild}.avif`);
// Convert to AVIF using sharp
await sharp(record.bBild)
.avif({ quality: 80 })
.toFile(filePath);
}
}
const processed = Math.min(i + chunkSize, ids.length);
if (processed === ids.length) {
console.log(`✅ Processed ${processed}/${ids.length} images.`);
} else {
console.log(`⏳ Processed ${processed}/${ids.length} images...`);
}
}
} catch (err) {
console.error('❌ Error downloading images:', err);
} finally {
if (pool) {
await pool.close();
}
}
}
}
const instance = new PictureSyncer();
export default instance;