diff --git a/server/focal-point.ts b/server/focal-point.ts index 4915f86..a4b7b41 100644 --- a/server/focal-point.ts +++ b/server/focal-point.ts @@ -26,6 +26,42 @@ function loadDetected() { const cache = new Map(); +// Trajni predpomnilnik LLM zarisov. Lezi na /opt/folxtv-uploads (bind mount), +// zato prezivi deploy in restart — brez tega je vsak deploy pomenil ponovno +// analizo vseh naslovnih slik na gpt-4o-mini. +const CACHE_PATH = path.join(process.cwd(), "client/public/uploads/focal_cache.json"); +let cacheLoaded = false; +let cacheDirty = false; + +function loadCache() { + if (cacheLoaded) return; + cacheLoaded = true; + try { + const raw = JSON.parse(fs.readFileSync(CACHE_PATH, "utf-8")) as Record; + for (const [k, v] of Object.entries(raw)) { + if (v && typeof v.x === "number" && typeof v.y === "number") cache.set(k, v); + } + console.log("[focal-point] iz predpomnilnika nalozenih:", cache.size); + } catch { + console.log("[focal-point] predpomnilnika se ni, zacenjam prazen"); + } +} + +function saveCache() { + if (!cacheDirty) return; + try { + const obj: Record = {}; + for (const [k, v] of cache.entries()) obj[k] = v; + fs.mkdirSync(path.dirname(CACHE_PATH), { recursive: true }); + fs.writeFileSync(CACHE_PATH + ".tmp", JSON.stringify(obj)); + fs.renameSync(CACHE_PATH + ".tmp", CACHE_PATH); + cacheDirty = false; + console.log("[focal-point] predpomnilnik shranjen, vnosov:", cache.size); + } catch (e) { + console.log("[focal-point] predpomnilnika ni bilo mogoce shraniti:", (e as Error).message); + } +} + const manualOverrides: Record = { "/uploads/70-jahre-oberkrainer.webp": { x: 50, y: 55 }, }; @@ -46,6 +82,7 @@ Return ONLY a JSON object like {"x":42,"y":38} with no other text.`; export async function analyzeFocalPoint(imagePath: string): Promise<{ x: number; y: number }> { const originalPath = imagePath; + loadCache(); if (manualOverrides[originalPath]) { cache.set(originalPath, manualOverrides[originalPath]); return manualOverrides[originalPath]; @@ -104,6 +141,7 @@ export async function analyzeFocalPoint(imagePath: string): Promise<{ x: number; y: Math.max(0, Math.min(100, Number(parsed.y) || 30)), }; cache.set(originalPath, point); + cacheDirty = true; return point; } } catch (e) { @@ -120,10 +158,12 @@ export async function analyzeAllArticleImages(articles: Array<{ coverImage: stri if (!article.coverImage) continue; await analyzeFocalPoint(article.coverImage); } + saveCache(); console.log("[focal-point] Analyzed", cache.size, "images"); } export function getCachedFocalPoints(): Record { + loadCache(); loadDetected(); const result: Record = {}; for (const [key, value] of cache.entries()) {