Improve movement flow layout/animation and fix Byzantine hall doors, styles, and preload.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
23e1210e86
commit
8111cd0163
+26
-11
@@ -160,17 +160,9 @@ async function syncPaintingFromDisk(row) {
|
||||
}
|
||||
|
||||
/** Fast preload: link local files only, no external API calls; regenerate missing thumbs from full files. */
|
||||
async function preloadArtistImagesLocal(artistId) {
|
||||
const rows = 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.artist_id = $1`,
|
||||
[artistId]
|
||||
);
|
||||
|
||||
async function preloadPaintingImageRows(rows) {
|
||||
let linked = 0;
|
||||
for (const row of rows.rows) {
|
||||
for (const row of rows) {
|
||||
const hasFull = localFileExists(row.image_path);
|
||||
const hasThumb = localFileExists(row.thumbnail_path) && isPaintingThumbRel(row.thumbnail_path);
|
||||
if (hasFull && hasThumb) {
|
||||
@@ -188,7 +180,29 @@ async function preloadArtistImagesLocal(artistId) {
|
||||
}
|
||||
}
|
||||
|
||||
return { fetched: linked, total: rows.rows.length };
|
||||
return { fetched: linked, total: rows.length };
|
||||
}
|
||||
|
||||
async function preloadArtistImagesLocal(artistId) {
|
||||
const rows = 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.artist_id = $1`,
|
||||
[artistId]
|
||||
);
|
||||
return preloadPaintingImageRows(rows.rows);
|
||||
}
|
||||
|
||||
async function preloadMovementImagesLocal(movementId) {
|
||||
const rows = 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 a.movement_id = $1`,
|
||||
[movementId]
|
||||
);
|
||||
return preloadPaintingImageRows(rows.rows);
|
||||
}
|
||||
|
||||
async function ensurePaintingImages(paintingId, size = 'thumb') {
|
||||
@@ -613,6 +627,7 @@ async function replaceArtistPortraitFromUrl(artistId, imageUrl, context = {}) {
|
||||
module.exports = {
|
||||
ensurePaintingImages,
|
||||
preloadArtistImagesLocal,
|
||||
preloadMovementImagesLocal,
|
||||
replacePaintingImageFromUrl,
|
||||
replaceArtistPortraitFromUrl,
|
||||
clearPaintingImage,
|
||||
|
||||
+23
-3
@@ -12,7 +12,7 @@ const { requirePermission } = require('./middleware/auth');
|
||||
const { logCuratorAction } = require('./audit-log');
|
||||
const authRoutes = require('./routes/auth');
|
||||
const usersRoutes = require('./routes/users');
|
||||
const { ensurePaintingImages, preloadArtistImagesLocal, replacePaintingImageFromUrl, replaceArtistPortraitFromUrl, clearPaintingImage, deletePainting, clearArtistPortrait, replacePaintingImageFromBuffer, replaceArtistPortraitFromBuffer, enrichPaintingRow, enrichArtistRow, IMAGE_DIR } = require('./image-service');
|
||||
const { ensurePaintingImages, preloadArtistImagesLocal, preloadMovementImagesLocal, replacePaintingImageFromUrl, replaceArtistPortraitFromUrl, clearPaintingImage, deletePainting, clearArtistPortrait, replacePaintingImageFromBuffer, replaceArtistPortraitFromBuffer, enrichPaintingRow, enrichArtistRow, IMAGE_DIR } = require('./image-service');
|
||||
const { getVersionInfo } = require('./version-info');
|
||||
const { searchCatalog } = require('./search-service');
|
||||
const {
|
||||
@@ -90,10 +90,18 @@ async function fetchTimelineErasAndMovements(startYear, endYear) {
|
||||
[startYear, endYear]
|
||||
),
|
||||
pool.query(
|
||||
`SELECT DISTINCT m.*, e.name as era_name
|
||||
`SELECT DISTINCT m.*, e.name as era_name,
|
||||
COALESCE(ic.link_count, 0)::int AS influence_link_count
|
||||
FROM art_movements m
|
||||
LEFT JOIN historical_eras e ON m.era_id = e.id
|
||||
INNER JOIN artists a ON a.movement_id = m.id
|
||||
LEFT JOIN (
|
||||
SELECT a2.movement_id, COUNT(pis.id)::int AS link_count
|
||||
FROM painting_influence_sources pis
|
||||
JOIN paintings p ON p.id = pis.painting_id
|
||||
JOIN artists a2 ON a2.id = p.artist_id
|
||||
GROUP BY a2.movement_id
|
||||
) ic ON ic.movement_id = m.id
|
||||
WHERE m.end_year >= $1 AND m.start_year <= $2
|
||||
AND (a.death_year IS NULL OR a.death_year >= $1)
|
||||
AND (a.birth_year IS NULL OR a.birth_year <= $2)
|
||||
@@ -126,7 +134,8 @@ async function catalogBootstrapEtag() {
|
||||
(SELECT COUNT(*)::int FROM artists) AS artist_count,
|
||||
(SELECT COUNT(*)::int FROM art_movements) AS movement_count,
|
||||
(SELECT COUNT(*)::int FROM historical_eras) AS era_count,
|
||||
(SELECT COUNT(*)::int FROM paintings) AS painting_count
|
||||
(SELECT COUNT(*)::int FROM paintings) AS painting_count,
|
||||
(SELECT COUNT(*)::int FROM painting_influence_sources) AS influence_count
|
||||
`);
|
||||
return result.rows[0];
|
||||
}
|
||||
@@ -942,6 +951,17 @@ app.post('/api/artists/:id/preload-images', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/movements/:id/preload-images', async (req, res) => {
|
||||
try {
|
||||
const movementId = parseInt(req.params.id, 10);
|
||||
const result = await preloadMovementImagesLocal(movementId);
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
console.error('Movement preload error:', err.message);
|
||||
res.status(500).json({ error: 'Preload failed' });
|
||||
}
|
||||
});
|
||||
|
||||
// Developer debug: Google Images first result for image audit
|
||||
app.get('/api/paintings/:id/debug-image-search/more', requirePermission('images'), async (req, res) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user