add disk-based metadata cache + refresh endpoint
This commit is contained in:
parent
8aa50dd5e2
commit
ad45d9a6b9
60
server.js
60
server.js
@ -4,8 +4,8 @@
|
|||||||
*
|
*
|
||||||
* Strategy:
|
* Strategy:
|
||||||
* - SSH (server-side find+cat) for all NAS access — metadata and thumbnails
|
* - SSH (server-side find+cat) for all NAS access — metadata and thumbnails
|
||||||
* - Disk-based thumbnail cache at CACHE_DIR (survives container restarts)
|
* - Disk-based cache at CACHE_DIR for both metadata and thumbnails (survives container restarts)
|
||||||
* - In-memory library metadata cache (reset on restart, cheap to rebuild)
|
* - In-memory library metadata cache (hot path after disk cache is warm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@ -95,13 +95,16 @@ function listLibraries() {
|
|||||||
.map(e => e.replace('.library', ''));
|
.map(e => e.replace('.library', ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- In-memory library metadata cache ---
|
// --- In-memory library metadata cache (populated from disk cache on hit) ---
|
||||||
let libraryCache = {};
|
let libraryCache = {};
|
||||||
|
|
||||||
// --- Thumbnail: disk cache path helper ---
|
// --- Disk cache path helpers ---
|
||||||
function thumbCachePath(library, imageId) {
|
function thumbCachePath(library, imageId) {
|
||||||
return path.join(CACHE_DIR, `${library}__${imageId}.png`);
|
return path.join(CACHE_DIR, `${library}__${imageId}.png`);
|
||||||
}
|
}
|
||||||
|
function metaCachePath(libName) {
|
||||||
|
return path.join(CACHE_DIR, `meta__${libName}.json`);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Routes ---
|
// --- Routes ---
|
||||||
|
|
||||||
@ -113,15 +116,56 @@ app.get('/api/libraries', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/api/library/:name', (req, res) => {
|
app.get('/api/library/:name', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const name = req.params.name + '.library';
|
const name = req.params.name + '.library';
|
||||||
console.log(`Loading library: ${name} …`);
|
|
||||||
|
// 1. In-memory cache (fastest — already loaded this session)
|
||||||
|
if (libraryCache[name]) {
|
||||||
|
const data = libraryCache[name];
|
||||||
|
return res.json({ folders: data.meta.folders, imageCount: data.images.length, images: data.images, cached: 'memory' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Disk cache (fast — survives restarts)
|
||||||
|
const diskPath = metaCachePath(name);
|
||||||
|
try {
|
||||||
|
const raw = await fsp.readFile(diskPath, 'utf8');
|
||||||
|
const data = JSON.parse(raw);
|
||||||
|
libraryCache[name] = data; // also warm the in-memory cache
|
||||||
|
console.log(`Library ${name} loaded from disk cache (${data.images.length} images)`);
|
||||||
|
return res.json({ folders: data.meta.folders, imageCount: data.images.length, images: data.images, cached: 'disk' });
|
||||||
|
} catch { /* cache miss — fall through to SSH */ }
|
||||||
|
|
||||||
|
// 3. SSH fetch from NAS (slow — first time only)
|
||||||
|
console.log(`Loading library: ${name} from NAS via SSH…`);
|
||||||
const t0 = Date.now();
|
const t0 = Date.now();
|
||||||
const data = loadLibrary(name);
|
const data = loadLibrary(name);
|
||||||
libraryCache[name] = data;
|
libraryCache[name] = data;
|
||||||
console.log(`Loaded ${data.images.length} images in ${Date.now() - t0}ms`);
|
console.log(`Loaded ${data.images.length} images in ${Date.now() - t0}ms — writing to disk cache`);
|
||||||
res.json({ folders: data.meta.folders, imageCount: data.images.length, images: data.images });
|
|
||||||
|
// Write to disk cache (fire-and-forget)
|
||||||
|
fsp.writeFile(diskPath, JSON.stringify(data)).catch(err =>
|
||||||
|
console.warn(`Failed to write metadata cache for ${name}:`, err.message)
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ folders: data.meta.folders, imageCount: data.images.length, images: data.images, cached: 'miss' });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
res.status(500).json({ error: e.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Force a fresh SSH fetch and update the disk cache (call this when library contents change)
|
||||||
|
app.post('/api/library/:name/refresh', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const name = req.params.name + '.library';
|
||||||
|
console.log(`Refreshing library: ${name} …`);
|
||||||
|
const t0 = Date.now();
|
||||||
|
const data = loadLibrary(name);
|
||||||
|
libraryCache[name] = data;
|
||||||
|
await fsp.writeFile(metaCachePath(name), JSON.stringify(data));
|
||||||
|
console.log(`Refreshed ${data.images.length} images in ${Date.now() - t0}ms`);
|
||||||
|
res.json({ ok: true, imageCount: data.images.length, ms: Date.now() - t0 });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
res.status(500).json({ error: e.message });
|
res.status(500).json({ error: e.message });
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user