140 lines
3.3 KiB
JavaScript
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
|
|
}
|
|
};
|
|
}
|