102 lines
3.5 KiB
JavaScript
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;
|