Improve support for Client Cache query types

This commit is contained in:
HighImKevin committed 2026-10-01 06:48:17 -04:00
1 parent 7abf79bbe5
commit 3229ba4123
6 files changed
+295 -99

No files matched your search

+129 -1
View File
@@ -297,6 +297,133 @@
}
}
const IDLE_HOLD_MS = 15000;
const BOOTSTRAP_FALLBACK_MS = 30000;
const SESSION_READY_FALLBACK_MS = 30000;
function hasMouseIdle() {
return typeof document !== 'undefined'
&& document.body
&& document.body.classList.contains('mouseIdle');
}
/**
* After login (+ home painted or session-ready fallback), fire onReady once when
* the first of these happens: 15s continuous body.mouseIdle, document.hidden,
* or 30s fallback. Background tabs are allowed (and preferred).
*
* @param {{ waitForLogin: () => Promise<boolean>, onReady: () => void }} opts
*/
function scheduleIdleBootstrap(opts) {
const waitForLogin = opts?.waitForLogin;
const onReady = opts?.onReady;
if (typeof onReady !== 'function') return;
let fired = false;
let idleTimer = null;
let fallbackTimer = null;
let sessionReadyTimer = null;
let mo = null;
const cleanup = () => {
if (idleTimer) {
clearTimeout(idleTimer);
idleTimer = null;
}
if (fallbackTimer) {
clearTimeout(fallbackTimer);
fallbackTimer = null;
}
if (sessionReadyTimer) {
clearTimeout(sessionReadyTimer);
sessionReadyTimer = null;
}
if (mo) {
mo.disconnect();
mo = null;
}
document.removeEventListener('visibilitychange', onVisibility);
document.removeEventListener('kefinTweaksHomePainted', onHomePainted);
};
const fire = () => {
if (fired) return;
fired = true;
cleanup();
try {
onReady();
} catch (e) {
console.error('[KefinTweaks LibraryCacheUtils] scheduleIdleBootstrap onReady failed:', e);
}
};
const syncIdleHold = () => {
if (fired) return;
if (hasMouseIdle()) {
if (!idleTimer) {
idleTimer = setTimeout(() => {
idleTimer = null;
if (hasMouseIdle()) fire();
}, IDLE_HOLD_MS);
}
} else if (idleTimer) {
clearTimeout(idleTimer);
idleTimer = null;
}
};
const onVisibility = () => {
if (document.hidden) fire();
};
const armGates = () => {
if (fired) return;
if (document.hidden) {
fire();
return;
}
fallbackTimer = setTimeout(fire, BOOTSTRAP_FALLBACK_MS);
document.addEventListener('visibilitychange', onVisibility);
if (typeof MutationObserver !== 'undefined' && document.body) {
mo = new MutationObserver(syncIdleHold);
mo.observe(document.body, { attributes: true, attributeFilter: ['class'] });
}
syncIdleHold();
};
const onHomePainted = () => {
document.removeEventListener('kefinTweaksHomePainted', onHomePainted);
if (sessionReadyTimer) {
clearTimeout(sessionReadyTimer);
sessionReadyTimer = null;
}
armGates();
};
Promise.resolve()
.then(() => (typeof waitForLogin === 'function' ? waitForLogin() : true))
.then((ok) => {
if (fired) return;
if (!ok) {
fire();
return;
}
document.addEventListener('kefinTweaksHomePainted', onHomePainted);
sessionReadyTimer = setTimeout(() => {
sessionReadyTimer = null;
document.removeEventListener('kefinTweaksHomePainted', onHomePainted);
armGates();
}, SESSION_READY_FALLBACK_MS);
})
.catch((e) => {
console.error('[KefinTweaks LibraryCacheUtils] scheduleIdleBootstrap login wait failed:', e);
fire();
});
}
window.LibraryCacheUtils = {
stripMovieForCache,
stripSeriesForCache,
@@ -310,6 +437,7 @@
upsertById,
removeByIds,
isVideoPlaybackRoute,
waitWhileCacheNetworkPaused
waitWhileCacheNetworkPaused,
scheduleIdleBootstrap
};
})();
+11 -21
View File
@@ -448,6 +448,7 @@
});
}
/** Defer crawl until idle/hidden/fallback after home has a chance to paint. */
let bootstrapStarted = false;
let bootstrapScheduled = false;
@@ -462,30 +463,19 @@
}
}
/** Defer crawl until home has painted (or 3s after login) so home getQuery is not starved. */
function scheduleBootstrap() {
if (bootstrapScheduled) return;
bootstrapScheduled = true;
const start = () => { bootstrap(); };
const onPainted = () => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
};
document.addEventListener('kefinTweaksHomePainted', onPainted);
waitForLoginReady().then((ok) => {
if (!ok) {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
return;
}
setTimeout(() => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
}, 3000);
});
const utils = window.LibraryCacheUtils;
if (utils?.scheduleIdleBootstrap) {
utils.scheduleIdleBootstrap({
waitForLogin: waitForLoginReady,
onReady: () => { bootstrap(); }
});
return;
}
// Fallback if LibraryCacheUtils not loaded yet
waitForLoginReady().then(() => bootstrap());
}
window.MoviesCache = {
+16 -31
View File
@@ -7,7 +7,7 @@
const WARN = (...args) => console.warn('[KefinTweaks PeopleCache]', ...args);
const ERR = (...args) => console.error('[KefinTweaks PeopleCache]', ...args);
const CACHE_NAME = 'library_top_people_v4';
const CACHE_NAME = 'library_top_people_v3';
const DEFAULT_MIN = 10;
const DEFAULT_PER_TYPE_MAX = 100;
const EPISODE_CHUNK_SIZE = 500;
@@ -337,7 +337,7 @@
const params = new URLSearchParams({
IncludeItemTypes: 'Episode',
Recursive: 'true',
Fields: 'People,ImageBlurHashes',
Fields: 'People',
ExcludeLocationTypes: 'Virtual',
EnableTotalRecordCount: 'false',
SortBy: 'DateCreated',
@@ -460,7 +460,7 @@
setEpisodeCrawl(startIndex, newestAt, oldestApplied);
await persistPeople({ prune: false });
}
} else {
} else {
LOG('Episode people sync: full crawl…');
startIndex = 0;
newestAt = null;
@@ -747,7 +747,7 @@
try {
const cache = window.IndexedDBCache;
const userId = window.ApiClient.getCurrentUserId();
if (cache && await cache.isCacheValid(CACHE_NAME, userId)) {
if (cache) {
const cached = await cache.get(CACHE_NAME, userId);
if (cached?.isComplete && Array.isArray(cached.peopleData)) {
const currentMode = shouldLoadPeopleEpisodeData();
@@ -791,7 +791,7 @@
// Resume partial, incremental watermark, or start full — never wipe on missing watermark alone
scheduleEpisodePeopleSync();
}
return;
return;
}
}
}
@@ -906,12 +906,7 @@
if (personType === 'Director') list = filterRoleList('Director', itemType);
else if (personType === 'Writer') list = filterRoleList('Writer', itemType);
else list = filterRoleList('Actor', itemType);
return list.slice(0, limit).map((p) => {
const out = { Id: p.Id, Name: p.Name, Type: 'Person' };
if (p.PrimaryImageTag) out.PrimaryImageTag = p.PrimaryImageTag;
if (p.ImageBlurHashes) out.ImageBlurHashes = p.ImageBlurHashes;
return out;
});
return list.slice(0, limit).map((p) => ({ Id: p.Id, Name: p.Name, Type: 'Person' }));
}
async function waitForLoginReady(maxWaitMs = 60000) {
@@ -926,6 +921,7 @@
return window.userHelper.waitForLogin(maxWaitMs);
}
/** Defer crawl until idle/hidden/fallback after home has a chance to paint. */
let bootstrapStarted = false;
let bootstrapScheduled = false;
@@ -945,26 +941,15 @@
function scheduleBootstrap() {
if (bootstrapScheduled) return;
bootstrapScheduled = true;
const start = () => { bootstrap(); };
const onPainted = () => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
};
document.addEventListener('kefinTweaksHomePainted', onPainted);
waitForLoginReady().then((ok) => {
if (!ok) {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
return;
}
setTimeout(() => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
}, 3000);
});
const utils = window.LibraryCacheUtils;
if (utils?.scheduleIdleBootstrap) {
utils.scheduleIdleBootstrap({
waitForLogin: waitForLoginReady,
onReady: () => { bootstrap(); }
});
return;
}
waitForLoginReady().then(() => bootstrap());
}
window.PeopleCache = {
+10 -20
View File
@@ -316,6 +316,7 @@
});
}
/** Defer crawl until idle/hidden/fallback after home has a chance to paint. */
let bootstrapStarted = false;
let bootstrapScheduled = false;
@@ -333,26 +334,15 @@
function scheduleBootstrap() {
if (bootstrapScheduled) return;
bootstrapScheduled = true;
const start = () => { bootstrap(); };
const onPainted = () => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
};
document.addEventListener('kefinTweaksHomePainted', onPainted);
waitForLoginReady().then((ok) => {
if (!ok) {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
return;
}
setTimeout(() => {
document.removeEventListener('kefinTweaksHomePainted', onPainted);
start();
}, 3000);
});
const utils = window.LibraryCacheUtils;
if (utils?.scheduleIdleBootstrap) {
utils.scheduleIdleBootstrap({
waitForLogin: waitForLoginReady,
onReady: () => { bootstrap(); }
});
return;
}
waitForLoginReady().then(() => bootstrap());
}
window.SeriesCache = {