mirror of
https://github.com/ranaldsgift/KefinTweaks.git
synced 2026-11-05 15:52:47 +01:00
Improve support for Client Cache query types
This commit is contained in:
1 parent
7abf79bbe5
commit
3229ba4123
6 files changed
+295
-99
No files matched your search
@@ -20,12 +20,33 @@
|
||||
{ value: 'Custom', label: 'Custom', path: null }
|
||||
];
|
||||
|
||||
const PEOPLE_CACHE_ITEM_TYPE_VALUES = [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'Movie', label: 'Movie' },
|
||||
{ value: 'Series', label: 'Series' },
|
||||
{ value: 'Episode', label: 'Episode' }
|
||||
];
|
||||
|
||||
const PEOPLE_CACHE_ARGS = [
|
||||
{
|
||||
key: 'ItemType',
|
||||
type: 'select',
|
||||
label: 'Item Type',
|
||||
possibleValues: PEOPLE_CACHE_ITEM_TYPE_VALUES
|
||||
},
|
||||
{ key: 'MinCount', type: 'number', label: 'Min Count' }
|
||||
];
|
||||
|
||||
const CACHE_SOURCE_OPTIONS = [
|
||||
{ value: 'MoviesCache.getImdbTop250Movies', label: 'IMDb Top 250 Movies' },
|
||||
{ value: 'StudiosCache.getPopularTVNetworks', label: 'Popular TV Networks' },
|
||||
{ value: 'PeopleCache.getTopActors', label: 'Top Actors' },
|
||||
{ value: 'PeopleCache.getTopDirectors', label: 'Top Directors' },
|
||||
{ value: 'PeopleCache.getTopWriters', label: 'Top Writers' }
|
||||
{
|
||||
value: 'StudiosCache.getPopularTVNetworks',
|
||||
label: 'Popular TV Networks',
|
||||
args: [{ key: 'MinCount', type: 'number', label: 'Min Count' }]
|
||||
},
|
||||
{ value: 'PeopleCache.getTopActors', label: 'Top Actors', args: PEOPLE_CACHE_ARGS },
|
||||
{ value: 'PeopleCache.getTopDirectors', label: 'Top Directors', args: PEOPLE_CACHE_ARGS },
|
||||
{ value: 'PeopleCache.getTopWriters', label: 'Top Writers', args: PEOPLE_CACHE_ARGS }
|
||||
];
|
||||
|
||||
const DEFAULT_SECTION_QUERY_LIMIT = window.KefinHomeScreenEditorConstants?.DEFAULT_SECTION_QUERY_LIMIT ?? 16;
|
||||
@@ -1388,9 +1409,60 @@
|
||||
if (list) list.innerHTML = buildStaticItemsListHTML(items);
|
||||
}
|
||||
|
||||
function getCacheSourceOption(dataSource) {
|
||||
const value = dataSource || CACHE_SOURCE_OPTIONS[0]?.value;
|
||||
return CACHE_SOURCE_OPTIONS.find(o => o.value === value) || CACHE_SOURCE_OPTIONS[0] || null;
|
||||
}
|
||||
|
||||
function buildCacheArgsHTML(queryIndex, dataSource, dataSourceOptions) {
|
||||
const { buildSelect, buildTextInput } = getApi();
|
||||
const source = getCacheSourceOption(dataSource);
|
||||
const args = Array.isArray(source?.args) ? source.args : [];
|
||||
if (!args.length) return '';
|
||||
|
||||
const fields = args.map((arg) => {
|
||||
const id = `query-${queryIndex}-dsArg-${arg.key}`;
|
||||
const raw = dataSourceOptions?.[arg.key];
|
||||
const selected = raw != null ? String(raw) : (arg.default != null ? String(arg.default) : '');
|
||||
if (arg.type === 'select') {
|
||||
const options = Array.isArray(arg.possibleValues) ? arg.possibleValues : [];
|
||||
return buildSelect(id, options, selected, arg.label || arg.key);
|
||||
}
|
||||
if (arg.type === 'number') {
|
||||
return buildTextInput(id, selected, arg.label || arg.key, 'number', '', { step: 1 });
|
||||
}
|
||||
return buildTextInput(id, selected, arg.label || arg.key);
|
||||
}).join('');
|
||||
|
||||
return `<div class="hsae-cache-args hsae-field-grid hsae-field-grid-2" data-query-index="${queryIndex}">${fields}</div>`;
|
||||
}
|
||||
|
||||
function collectCacheArgsIntoDataSourceOptions(editor, queryIndex, dataSource, dataSourceOptions) {
|
||||
const source = getCacheSourceOption(dataSource);
|
||||
const args = Array.isArray(source?.args) ? source.args : [];
|
||||
args.forEach((arg) => {
|
||||
const el = editor.querySelector(`#query-${queryIndex}-dsArg-${arg.key}`);
|
||||
if (!el) return;
|
||||
const raw = el.value;
|
||||
if (raw === '' || raw == null) return;
|
||||
if (arg.type === 'number') {
|
||||
const n = parseFloat(raw);
|
||||
if (Number.isFinite(n)) dataSourceOptions[arg.key] = n;
|
||||
return;
|
||||
}
|
||||
dataSourceOptions[arg.key] = raw;
|
||||
});
|
||||
}
|
||||
|
||||
function buildQueryCard(query, index, canDelete, isExpanded = true) {
|
||||
const { buildSelect, buildTextInput, SORT_ORDERS, SORT_ORDER_DIRECTIONS } = getApi();
|
||||
const queryOptions = query.queryOptions || {};
|
||||
// Prefer dataSourceOptions; fall back to legacy ItemType/MinCount stored in queryOptions
|
||||
const dataSourceOptions = {
|
||||
...(query.queryOptions?.ItemType != null ? { ItemType: query.queryOptions.ItemType } : {}),
|
||||
...(query.queryOptions?.MinCount != null ? { MinCount: query.queryOptions.MinCount } : {}),
|
||||
...(query.dataSourceOptions || {})
|
||||
};
|
||||
const sourceType = inferQuerySourceType(query);
|
||||
const endpointValue = sourceType === 'static' ? 'Items' : inferEndpointValue(query);
|
||||
const customPathDisplay = endpointValue === 'Custom' && sourceType === 'jellyfin' ? 'block' : 'none';
|
||||
@@ -1398,8 +1470,10 @@
|
||||
const endpointDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
|
||||
const jellyfinDisplay = sourceType === 'jellyfin' ? 'block' : 'none';
|
||||
const staticDisplay = sourceType === 'static' ? 'block' : 'none';
|
||||
const sortExtrasDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
|
||||
const limitDisplay = sourceType === 'jellyfin' || sourceType === 'static' || sourceType === 'cache' ? 'block' : 'none';
|
||||
const searchDisplay = sourceType === 'jellyfin' || sourceType === 'static' ? 'block' : 'none';
|
||||
const staticItems = getQueryStaticItems(query);
|
||||
const selectedDataSource = query.dataSource || CACHE_SOURCE_OPTIONS[0]?.value;
|
||||
|
||||
const parentSelection = getQueryParentSelection(query);
|
||||
const includeTypes = Array.isArray(queryOptions.IncludeItemTypes)
|
||||
@@ -1434,12 +1508,15 @@
|
||||
${buildEndpointSelectHTML(index, endpointValue, sourceType === 'static')}
|
||||
</div>
|
||||
<div class="hsae-cache-wrap" data-query-index="${index}" style="display:${cacheDisplay};">
|
||||
${buildSelect(`query-${index}-dataSource`, CACHE_SOURCE_OPTIONS, query.dataSource || CACHE_SOURCE_OPTIONS[0].value, 'Cache Source')}
|
||||
${buildSelect(`query-${index}-dataSource`, CACHE_SOURCE_OPTIONS, selectedDataSource, 'Cache Source')}
|
||||
</div>
|
||||
<div id="query-${index}-path-container" class="hsae-custom-path hsae-query-jellyfin-only" style="display:${customPathDisplay};">
|
||||
${buildTextInput(`query-${index}-path`, query.path || '', 'Custom Endpoint Path', 'text', '/Shows/Upcoming, etc.')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hsae-cache-args-wrap" data-query-index="${index}" style="display:${cacheDisplay};" data-hsae-profiles="full">
|
||||
${sourceType === 'cache' ? buildCacheArgsHTML(index, selectedDataSource, dataSourceOptions) : ''}
|
||||
</div>
|
||||
|
||||
<div class="hsae-field-grid hsae-query-row-sort">
|
||||
<div class="hsae-query-sort-by" data-hsae-profiles="full normal dateCutoffs">
|
||||
@@ -1448,10 +1525,10 @@
|
||||
<div class="hsae-query-sort-order" data-hsae-profiles="full normal dateCutoffs">
|
||||
${buildSelect(`query-${index}-SortOrder`, SORT_ORDER_DIRECTIONS, queryOptions.SortOrder || '', 'Sort Order')}
|
||||
</div>
|
||||
<div class="hsae-query-limit hsae-query-sort-extras" data-hsae-profiles="full minimal postProcessing normal dateCutoffs" style="display:${sortExtrasDisplay};">
|
||||
<div class="hsae-query-limit" data-hsae-profiles="full minimal postProcessing normal dateCutoffs" style="display:${limitDisplay};">
|
||||
${buildTextInput(`query-${index}-Limit`, queryOptions.Limit ?? '', 'Limit', 'number', '', { min: 0, step: 1 })}
|
||||
</div>
|
||||
<div class="hsae-query-search hsae-query-sort-extras" data-hsae-profiles="full" style="display:${sortExtrasDisplay};">
|
||||
<div class="hsae-query-search" data-hsae-profiles="full" style="display:${searchDisplay};">
|
||||
${buildTextInput(`query-${index}-SearchTerm`, queryOptions.SearchTerm || '', 'Search Term')}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2369,17 +2446,36 @@
|
||||
}
|
||||
|
||||
if (sourceType === 'cache') {
|
||||
const dataSource = editor.querySelector(`#query-${queryIndex}-dataSource`)?.value;
|
||||
const dataSource = editor.querySelector(`#query-${queryIndex}-dataSource`)?.value
|
||||
|| CACHE_SOURCE_OPTIONS[0]?.value;
|
||||
if (dataSource) query.dataSource = dataSource;
|
||||
} else {
|
||||
const endpoint = editor.querySelector(`#query-${queryIndex}-endpoint`)?.value || 'Items';
|
||||
const endpointMeta = ENDPOINT_OPTIONS.find(o => o.value === endpoint);
|
||||
if (endpoint === 'Custom') {
|
||||
const path = editor.querySelector(`#query-${queryIndex}-path`)?.value?.trim();
|
||||
if (path) query.path = path;
|
||||
} else if (endpointMeta?.path) {
|
||||
query.path = endpointMeta.path;
|
||||
query.queryOptions = {};
|
||||
['SortBy', 'SortOrder'].forEach(key => {
|
||||
const value = editor.querySelector(`#query-${queryIndex}-${key}`)?.value;
|
||||
if (value) query.queryOptions[key] = value;
|
||||
});
|
||||
const limit = editor.querySelector(`#query-${queryIndex}-Limit`)?.value;
|
||||
if (limit !== '' && limit != null) {
|
||||
const limitN = parseInt(limit, 10);
|
||||
if (Number.isFinite(limitN) && limitN >= 0) {
|
||||
query.queryOptions.Limit = limitN;
|
||||
}
|
||||
}
|
||||
const dataSourceOptions = {};
|
||||
collectCacheArgsIntoDataSourceOptions(editor, queryIndex, dataSource, dataSourceOptions);
|
||||
if (Object.keys(dataSourceOptions).length) {
|
||||
query.dataSourceOptions = dataSourceOptions;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
const endpoint = editor.querySelector(`#query-${queryIndex}-endpoint`)?.value || 'Items';
|
||||
const endpointMeta = ENDPOINT_OPTIONS.find(o => o.value === endpoint);
|
||||
if (endpoint === 'Custom') {
|
||||
const path = editor.querySelector(`#query-${queryIndex}-path`)?.value?.trim();
|
||||
if (path) query.path = path;
|
||||
} else if (endpointMeta?.path) {
|
||||
query.path = endpointMeta.path;
|
||||
}
|
||||
|
||||
query.queryOptions = {};
|
||||
@@ -2709,12 +2805,15 @@
|
||||
|
||||
const sourceSelect = editor.querySelector(`#query-${queryIndex}-sourceType`);
|
||||
const endpointSelect = editor.querySelector(`#query-${queryIndex}-endpoint`);
|
||||
const dataSourceSelect = editor.querySelector(`#query-${queryIndex}-dataSource`);
|
||||
const pathContainer = editor.querySelector(`#query-${queryIndex}-path-container`);
|
||||
const endpointWrap = editor.querySelector('.hsae-jellyfin-endpoint-wrap');
|
||||
const cacheWrap = editor.querySelector('.hsae-cache-wrap');
|
||||
const cacheArgsWrap = editor.querySelector('.hsae-cache-args-wrap');
|
||||
const jellyfinOnlyBlocks = editor.querySelectorAll('.hsae-query-jellyfin-only');
|
||||
const staticOnlyBlocks = editor.querySelectorAll('.hsae-query-static-only');
|
||||
const sortExtrasBlocks = editor.querySelectorAll('.hsae-query-sort-extras');
|
||||
const limitBlock = editor.querySelector('.hsae-query-limit');
|
||||
const searchBlock = editor.querySelector('.hsae-query-search');
|
||||
|
||||
const updateSourceVisibility = () => {
|
||||
const sourceType = sourceSelect?.value || 'jellyfin';
|
||||
@@ -2724,15 +2823,21 @@
|
||||
if (sourceType === 'static') endpointSelect.value = 'Items';
|
||||
}
|
||||
if (cacheWrap) cacheWrap.style.display = sourceType === 'cache' ? 'block' : 'none';
|
||||
if (cacheArgsWrap) cacheArgsWrap.style.display = sourceType === 'cache' ? 'block' : 'none';
|
||||
jellyfinOnlyBlocks.forEach(block => {
|
||||
block.style.display = sourceType === 'jellyfin' ? '' : 'none';
|
||||
});
|
||||
staticOnlyBlocks.forEach(block => {
|
||||
block.style.display = sourceType === 'static' ? '' : 'none';
|
||||
});
|
||||
sortExtrasBlocks.forEach(block => {
|
||||
block.style.display = sourceType === 'jellyfin' || sourceType === 'static' ? '' : 'none';
|
||||
});
|
||||
if (limitBlock) {
|
||||
limitBlock.style.display = sourceType === 'jellyfin' || sourceType === 'static' || sourceType === 'cache'
|
||||
? ''
|
||||
: 'none';
|
||||
}
|
||||
if (searchBlock) {
|
||||
searchBlock.style.display = sourceType === 'jellyfin' || sourceType === 'static' ? '' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
const updateEndpointVisibility = () => {
|
||||
@@ -2743,6 +2848,9 @@
|
||||
sourceSelect?.addEventListener('change', () => {
|
||||
refreshEditorBody(modalInstance);
|
||||
});
|
||||
dataSourceSelect?.addEventListener('change', () => {
|
||||
refreshEditorBody(modalInstance);
|
||||
});
|
||||
endpointSelect?.addEventListener('change', updateEndpointVisibility);
|
||||
|
||||
updateSourceVisibility();
|
||||
|
||||
@@ -85,11 +85,6 @@
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
[data-modal-id="kefin-homescreen-section-create-flow"] .hsse-flow-body:has(.hsse-wizard) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[data-modal-id="kefin-homescreen-section-create-flow"] .hsse-subtitle {
|
||||
margin: 0 0 1.25em;
|
||||
font-size: 0.9375rem;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 = {
|
||||
|
||||
Reference in new issue
Block a user