Files
embycovers/homescreen_editor/lib/server/emby-user-cache.js
T
2026-06-08 21:58:16 +12:00

140 lines
3.3 KiB
JavaScript

import { existsSync, mkdirSync } from 'fs';
import { dirname, resolve } from 'path';
import { DatabaseSync } from 'node:sqlite';
function getCacheDbPath() {
return process.env.EMBY_USER_CACHE_DB_PATH || resolve('.cache', 'emby-users.db');
}
function ensureCacheDir() {
const dir = dirname(getCacheDbPath());
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
function openCacheDb() {
ensureCacheDir();
const db = new DatabaseSync(getCacheDbPath());
db.exec(`
CREATE TABLE IF NOT EXISTS EmbyUsers (
embyGuid TEXT PRIMARY KEY,
name TEXT NOT NULL,
fetchedAt TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS CacheMeta (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
`);
return db;
}
function normalizeGuid(value) {
return typeof value === 'string' ? value.replace(/-/g, '').trim().toLowerCase() : '';
}
export function normalizeEmbyUsers(users) {
if (!Array.isArray(users)) return [];
return users
.map((user) => ({
embyGuid: normalizeGuid(user?.embyGuid ?? user?.Id),
name: String(user?.name ?? user?.Name ?? '').trim()
}))
.filter((user) => user.embyGuid && user.name);
}
export function writeCachedEmbyUsers(users) {
const normalizedUsers = normalizeEmbyUsers(users);
const fetchedAt = new Date().toISOString();
const db = openCacheDb();
try {
const clearStmt = db.prepare('DELETE FROM EmbyUsers');
const insertStmt = db.prepare(
'INSERT INTO EmbyUsers (embyGuid, name, fetchedAt) VALUES (?, ?, ?)'
);
const metaStmt = db.prepare(
'INSERT INTO CacheMeta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value'
);
db.exec('BEGIN');
try {
clearStmt.run();
for (const user of normalizedUsers) {
insertStmt.run(user.embyGuid, user.name, fetchedAt);
}
metaStmt.run('lastSyncedAt', fetchedAt);
db.exec('COMMIT');
} catch (error) {
db.exec('ROLLBACK');
throw error;
}
} finally {
db.close();
}
return { users: normalizedUsers, lastSyncedAt: fetchedAt };
}
export function readCachedEmbyUsers() {
if (!existsSync(getCacheDbPath())) {
return { users: [], lastSyncedAt: null };
}
const db = openCacheDb();
try {
const users = db
.prepare('SELECT embyGuid, name, fetchedAt FROM EmbyUsers ORDER BY lower(name), embyGuid')
.all()
.map((row) => ({
embyGuid: normalizeGuid(row.embyGuid),
name: row.name
}));
const meta = db.prepare("SELECT value FROM CacheMeta WHERE key = 'lastSyncedAt'").get();
return {
users,
lastSyncedAt: meta?.value || users[0]?.fetchedAt || null
};
} finally {
db.close();
}
}
export function getCachedEmbyUserMap() {
return new Map(readCachedEmbyUsers().users.map((user) => [user.embyGuid, user]));
}
export function applyCachedEmbyNames(users) {
const cached = readCachedEmbyUsers();
const lookup = new Map(cached.users.map((user) => [user.embyGuid, user]));
let matchedCount = 0;
const hydratedUsers = (users || []).map((user) => {
const embyGuid = normalizeGuid(user?.embyGuid);
const cachedUser = lookup.get(embyGuid);
if (!cachedUser) {
return user;
}
matchedCount++;
return {
...user,
dbName: user.dbName || user.name,
embyName: cachedUser.name,
name: cachedUser.name
};
});
return {
users: hydratedUsers,
cache: {
matchedCount,
totalCachedUsers: lookup.size,
lastSyncedAt: cached.lastSyncedAt
}
};
}