feat: Enhance image processing in data-fetching and update SEO meta tags for product images; add Telegram assistant link in ChatAssistant component with localization support

This commit is contained in:
sebseb7
2026-03-26 11:56:07 +01:00
parent c5dce64ac9
commit 1897ceb7c5
47 changed files with 372 additions and 244 deletions

View File

@@ -186,80 +186,98 @@ const saveProductImages = async (socket, products, categoryName, outputDir) => {
.filter((id) => id);
if (imageIds.length > 0) {
// Process first image for each product
// Process first image for each product — store AVIF + JPEG (e.g. for Twitter / social)
const bildId = parseInt(imageIds[0]);
const estimatedFilename = `prod${bildId}.avif`; // We'll generate a filename based on the ID
const avifFilename = `prod${bildId}.avif`;
const jpegFilename = `prod${bildId}.jpg`;
const avifPath = path.join(assetsPath, avifFilename);
const jpegPath = path.join(assetsPath, jpegFilename);
const imagePath = path.join(assetsPath, estimatedFilename);
// Skip if image already exists
if (fs.existsSync(imagePath)) {
if (fs.existsSync(avifPath) && fs.existsSync(jpegPath)) {
imagesSkipped++;
continue;
}
const writeAvifAndJpegFromBuffer = async (buf) => {
if (!fs.existsSync(avifPath)) {
await sharp(buf).avif().toFile(avifPath);
}
if (!fs.existsSync(jpegPath)) {
await sharp(buf)
.jpeg({ quality: 85, mozjpeg: true })
.toFile(jpegPath);
}
};
try {
const imageBuffer = await fetchProductImage(socket, bildId);
// If overlay exists, apply it to the image
if (false && fs.existsSync(overlayPath)) {
try {
// Get image dimensions to center the overlay
const baseImage = sharp(Buffer.from(imageBuffer));
const baseMetadata = await baseImage.metadata();
const overlaySize = Math.min(baseMetadata.width, baseMetadata.height) * 0.4;
// Resize overlay to 20% of base image size and get its buffer
const resizedOverlayBuffer = await sharp(overlayPath)
.resize({
width: Math.round(overlaySize),
height: Math.round(overlaySize),
fit: 'contain', // Keep full overlay visible
background: { r: 0, g: 0, b: 0, alpha: 0 } // Transparent background instead of black bars
})
.toBuffer();
// Calculate center position for the resized overlay
const centerX = Math.floor((baseMetadata.width - overlaySize) / 2);
const centerY = Math.floor((baseMetadata.height - overlaySize) / 2);
const processedImageBuffer = await baseImage
.composite([
{
input: resizedOverlayBuffer,
top: centerY,
left: centerX,
blend: "multiply", // Darkens the image, visible on all backgrounds
opacity: 0.3,
},
])
.avif() // Ensure output is AVIF
.toBuffer();
fs.writeFileSync(imagePath, processedImageBuffer);
console.log(
` ✅ Applied centered inverted sh.avif overlay to ${estimatedFilename}`
);
} catch (overlayError) {
console.log(
` ⚠️ Failed to apply overlay to ${estimatedFilename}: ${overlayError.message}`
);
// Fallback: save without overlay
fs.writeFileSync(imagePath, Buffer.from(imageBuffer));
}
if (fs.existsSync(avifPath) && !fs.existsSync(jpegPath)) {
await sharp(avifPath)
.jpeg({ quality: 85, mozjpeg: true })
.toFile(jpegPath);
} else if (!fs.existsSync(avifPath) && fs.existsSync(jpegPath)) {
await sharp(jpegPath).avif().toFile(avifPath);
} else {
// Save without overlay if overlay file doesn't exist
fs.writeFileSync(imagePath, Buffer.from(imageBuffer));
const imageBuffer = await fetchProductImage(socket, bildId);
const buf = Buffer.from(imageBuffer);
// If overlay exists, apply it to the image
if (false && fs.existsSync(overlayPath)) {
try {
const baseImage = sharp(buf);
const baseMetadata = await baseImage.metadata();
const overlaySize =
Math.min(baseMetadata.width, baseMetadata.height) * 0.4;
const resizedOverlayBuffer = await sharp(overlayPath)
.resize({
width: Math.round(overlaySize),
height: Math.round(overlaySize),
fit: "contain",
background: { r: 0, g: 0, b: 0, alpha: 0 },
})
.toBuffer();
const centerX = Math.floor(
(baseMetadata.width - overlaySize) / 2
);
const centerY = Math.floor(
(baseMetadata.height - overlaySize) / 2
);
const processedImageBuffer = await baseImage
.composite([
{
input: resizedOverlayBuffer,
top: centerY,
left: centerX,
blend: "multiply",
opacity: 0.3,
},
])
.toBuffer();
await writeAvifAndJpegFromBuffer(processedImageBuffer);
console.log(
` ✅ Applied overlay → ${avifFilename} + ${jpegFilename}`
);
} catch (overlayError) {
console.log(
` ⚠️ Failed to apply overlay to prod${bildId}: ${overlayError.message}`
);
await writeAvifAndJpegFromBuffer(buf);
}
} else {
await writeAvifAndJpegFromBuffer(buf);
}
}
imagesSaved++;
// Small delay to avoid overwhelming server
await new Promise((resolve) => setTimeout(resolve, 50));
} catch (error) {
console.log(
` ⚠️ Failed to fetch image ${estimatedFilename} (ID: ${bildId}): ${error.message}`
` ⚠️ Failed to fetch/save prod${bildId} (${avifFilename} / ${jpegFilename}): ${error.message}`
);
}
}