Add influence rework, image checkup, debug mode, and fetched paintings.
Support artist and movement influence links with web discovery, a developer checkup table with gallery/detail thumbnails, and debug image search with fix-it workflow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
0ece1195fa
commit
bf7db9b25e
+74
-2
@@ -1,12 +1,31 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pool = require('./db');
|
||||
const { savePaintingImages } = require('../scripts/image-fetcher');
|
||||
const {
|
||||
savePaintingImages,
|
||||
downloadImageToFile,
|
||||
generateThumbnailFromFull,
|
||||
pickExt,
|
||||
} = require('../scripts/image-fetcher');
|
||||
|
||||
const IMAGE_DIR = path.resolve(process.env.IMAGE_DIR || './data/images');
|
||||
const FETCH_TIMEOUT_MS = 15000;
|
||||
const inflight = new Map();
|
||||
|
||||
function safePaintingBase(artistName, title) {
|
||||
return `${artistName}_${title}`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||
}
|
||||
|
||||
function unlinkIfExists(absPath) {
|
||||
if (absPath && fs.existsSync(absPath)) {
|
||||
try {
|
||||
fs.unlinkSync(absPath);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function withTimeout(promise, ms) {
|
||||
return Promise.race([
|
||||
promise,
|
||||
@@ -135,4 +154,57 @@ async function ensurePaintingImages(paintingId, size = 'thumb') {
|
||||
return promise;
|
||||
}
|
||||
|
||||
module.exports = { ensurePaintingImages, preloadArtistImagesLocal, IMAGE_DIR };
|
||||
async function replacePaintingImageFromUrl(paintingId, imageUrl) {
|
||||
const result = await pool.query(
|
||||
`SELECT p.id, p.title, p.image_path, p.thumbnail_path, a.name AS artist_name
|
||||
FROM paintings p
|
||||
JOIN artists a ON a.id = p.artist_id
|
||||
WHERE p.id = $1`,
|
||||
[paintingId]
|
||||
);
|
||||
if (result.rows.length === 0) {
|
||||
throw new Error('Painting not found');
|
||||
}
|
||||
|
||||
const row = result.rows[0];
|
||||
const safeBase = safePaintingBase(row.artist_name, row.title);
|
||||
const paintingsDir = path.join(IMAGE_DIR, 'paintings');
|
||||
const thumbsDir = path.join(IMAGE_DIR, 'paintings', 'thumbs');
|
||||
if (!fs.existsSync(paintingsDir)) fs.mkdirSync(paintingsDir, { recursive: true });
|
||||
if (!fs.existsSync(thumbsDir)) fs.mkdirSync(thumbsDir, { recursive: true });
|
||||
|
||||
const fullExt = pickExt(imageUrl);
|
||||
const fullDest = path.join(paintingsDir, safeBase + fullExt);
|
||||
const thumbDest = path.join(thumbsDir, safeBase + '_thumb.jpg');
|
||||
|
||||
unlinkIfExists(row.image_path ? path.join(IMAGE_DIR, row.image_path) : null);
|
||||
unlinkIfExists(row.thumbnail_path ? path.join(IMAGE_DIR, row.thumbnail_path) : null);
|
||||
unlinkIfExists(fullDest);
|
||||
unlinkIfExists(thumbDest);
|
||||
|
||||
await downloadImageToFile(imageUrl, fullDest, { force: true });
|
||||
|
||||
let thumbnailPath = null;
|
||||
try {
|
||||
await generateThumbnailFromFull(fullDest, thumbDest);
|
||||
thumbnailPath = path.join('paintings', 'thumbs', safeBase + '_thumb.jpg').replace(/\\/g, '/');
|
||||
} catch {
|
||||
thumbnailPath = path.join('paintings', safeBase + fullExt).replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
const imagePath = path.join('paintings', safeBase + fullExt).replace(/\\/g, '/');
|
||||
|
||||
await pool.query(
|
||||
`UPDATE paintings SET image_path = $1, thumbnail_path = $2 WHERE id = $3`,
|
||||
[imagePath, thumbnailPath, paintingId]
|
||||
);
|
||||
|
||||
return { imagePath, thumbnailPath };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ensurePaintingImages,
|
||||
preloadArtistImagesLocal,
|
||||
replacePaintingImageFromUrl,
|
||||
IMAGE_DIR,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user