Fix not syncing native Jellyfin home sections when using the "Update" function from KefinTweaks Home Screen configuration

This commit is contained in:
HighImKevin committed 2026-10-06 22:25:51 -04:00
1 parent 3c28d21b9e
commit b25f572ef4
2 files changed
+122 -33

No files matched your search

+30
View File
@@ -3889,6 +3889,20 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
});
}
function getServerEnabledSectionsForUpdatePreview() {
const config = currentConfig || getConfig();
if (!config) return [];
const inSeason = (s) => (s.startDate && s.endDate ? isInSeasonalPeriod(s.startDate, s.endDate) : true);
const enabledFrom = (groups) => flattenSectionGroups(groups || []).filter(
(s) => s.enabled === true && s.discoveryEnabled !== true && inSeason(s)
);
return [
...enabledFrom(config.HOME_SECTION_GROUPS),
...enabledFrom(config.SEASONAL_SECTION_GROUPS),
...enabledFrom(config.CUSTOM_SECTION_GROUPS)
];
}
function confirmAndUpdateAllUsers() {
if (!window.ModalSystem) {
ERR('ModalSystem not available for update confirmation');
@@ -3898,11 +3912,27 @@ window.KefinTweaksConfig = ${JSON.stringify(configToBackup, null, 2)};`;
window.ModalSystem.close(UPDATE_ALL_CONFIRM_MODAL_ID);
}
// Reflect current order UI before previewing native slots
try {
applyOrderListFromForm(mainModalInstance?.dialogContent);
} catch (_) { /* preview best-effort */ }
const serverEnabled = getServerEnabledSectionsForUpdatePreview();
const slots = window.KefinUserHomeScreenConfig?.buildHomesectionSlotsFromKefin?.(serverEnabled) || [];
const pairedSlotPreview = slots.filter((id) => id && id !== 'none').join(', ') || '(none)';
const content = document.createElement('div');
content.innerHTML = `
<div class="listItemBodyText" style="line-height: 1.5;">
WARNING: This will forcefully update the KefinTweaks Home Screen user preferences for all accounts on your server. Any changes those users have made to customize their home screen will be lost.
</div>
<div class="listItemBodyText" style="line-height: 1.5; margin-top: 0.75em;">
Users with Pair Home Sections on will get these native Jellyfin slots:
<code style="display: block; margin-top: 0.35em; word-break: break-word;">${pairedSlotPreview}</code>
</div>
<div class="listItemBodyText" style="line-height: 1.5; margin-top: 0.75em;">
Users with Pair Home Sections off get all native slots set to <code>none</code>.
</div>
`;
const footer = document.createElement('div');
footer.style.display = 'flex';
+92 -33
View File
@@ -862,10 +862,28 @@
}
/**
* Clear all section overrides for every user (pins preserved).
* Resolve admin-enabled server catalog sections used to pack native homesectionN on Update.
*/
function getServerEnabledSectionsForNativeSync() {
const config = window.KefinHomeScreen?.getConfig?.();
if (Array.isArray(config?.ENABLED_NORMAL_SECTIONS)) {
return config.ENABLED_NORMAL_SECTIONS.filter((s) => s?.enabled === true);
}
const sectionsApi = window.KefinHomeScreen?.getSections;
if (typeof sectionsApi === 'function') {
const { enabledHomeSections = [] } = sectionsApi() || {};
return (enabledHomeSections || []).filter((s) => s?.enabled === true);
}
return [];
}
/**
* Clear all section overrides for every user (pins preserved) and rewrite homesection0–9
* from the server-enabled catalog (honors each user's pairNativeHomeSections).
* @param {{ onProgress?: Function }} [options]
*/
async function updateUserHomeScreenConfiguration(options = {}) {
const serverEnabledSections = getServerEnabledSectionsForNativeSync();
const users = await listServerUsers();
const total = users.length;
let done = 0;
@@ -876,14 +894,23 @@
const userName = user.Name || user.name || userId;
try {
const prefs = await window.userHelper.getUserDisplayPreferencesForUser(userId);
const homeScreen = parseKefinTweaksHomeScreen(prefs?.CustomPrefs);
if ((homeScreen.sections || []).length > 0) {
homeScreen.sections = [];
if (!prefs.CustomPrefs) prefs.CustomPrefs = {};
const homeScreen = parseKefinTweaksHomeScreen(prefs.CustomPrefs);
const hadOverrides = (homeScreen.sections || []).length > 0;
homeScreen.sections = [];
const jellyfinSync = syncNativeHomeSectionsFromKefin(
serverEnabledSections,
prefs.CustomPrefs,
homeScreen
);
if (hadOverrides || jellyfinSync.changed) {
await saveHomeScreenForUser(userId, homeScreen, prefs);
}
} catch (e) {
failed += 1;
WARN(`Failed clearing homeScreen sections for ${userName}:`, e);
WARN(`Failed updating homeScreen / native sections for ${userName}:`, e);
}
done += 1;
options.onProgress?.({ done, total, failed, userName });
@@ -894,19 +921,55 @@
}
/**
* Resolve Jellyfin pairing id for a catalog section.
* Prefer section.jellyfinId; fall back to map / recently-added-* pattern.
* All Jellyfin pairing ids for a catalog section (jellyfinId + jellyfinId2).
* Falls back to map / recently-added-* when neither is set.
* @returns {string[]} lowercased unique non-empty ids (excludes 'none')
*/
function getSectionJellyfinIds(section) {
if (!section) return [];
const ids = [];
const push = (value) => {
if (value == null || value === '') return;
const normalized = String(value).toLowerCase();
if (!normalized || normalized === 'none') return;
if (!ids.includes(normalized)) ids.push(normalized);
};
push(section.jellyfinId);
push(section.jellyfinId2);
if (!ids.length) {
const id = section.id || '';
if (id.startsWith('recently-added-')) {
push('latestmedia');
} else {
for (const [jellyfinId, kefinId] of Object.entries(JELLYFIN_HOME_SECTIONS_MAP)) {
if (jellyfinId === 'latestmedia' && id.startsWith('recently-added-')) {
push('latestmedia');
break;
}
if (kefinId === id) {
push(jellyfinId);
break;
}
}
}
}
return ids;
}
function sectionMatchesNativeType(section, nativeType) {
const type = String(nativeType || '').toLowerCase();
if (!type || type === 'none') return false;
return getSectionJellyfinIds(section).includes(type);
}
/**
* Primary Jellyfin pairing id for a catalog section (first of getSectionJellyfinIds).
*/
function resolveSectionJellyfinId(section) {
if (!section) return null;
if (section.jellyfinId) return String(section.jellyfinId).toLowerCase();
const id = section.id || '';
if (id.startsWith('recently-added-')) return 'latestmedia';
for (const [jellyfinId, kefinId] of Object.entries(JELLYFIN_HOME_SECTIONS_MAP)) {
if (jellyfinId === 'latestmedia' && id.startsWith('recently-added-')) return 'latestmedia';
if (kefinId === id) return String(jellyfinId).toLowerCase();
}
return null;
const ids = getSectionJellyfinIds(section);
return ids[0] || null;
}
/**
@@ -918,15 +981,10 @@
const collected = [];
(sections || []).forEach((section) => {
if (section.enabled !== true) return;
const jellyfinId = resolveSectionJellyfinId(section);
if (!jellyfinId || jellyfinId === 'none') return;
const order = section.order || 0;
collected.push({ jellyfinId, order });
if (section.id === 'continueWatchingAndNextUp') {
collected.push({ jellyfinId: 'nextup', order });
}
getSectionJellyfinIds(section).forEach((jellyfinId) => {
collected.push({ jellyfinId, order });
});
});
collected.sort((a, b) => (a.order || 0) - (b.order || 0));
@@ -975,26 +1033,21 @@
/**
* Candidate Kefin sections for a native homesection type (inbound enable sync).
* resume → continueWatching only; livetv/latestmedia → all matches.
* Matches jellyfinId or jellyfinId2 (e.g. resume/nextup both hit continueWatchingAndNextUp).
* latestmedia also includes recently-added-* rows.
*/
function resolveKefinCandidatesForNative(nativeType, catalogSections) {
const type = String(nativeType || '').toLowerCase();
if (!type || type === 'none') return [];
const catalog = catalogSections || [];
if (type === 'resume') {
return catalog.filter((s) => s?.id === 'continueWatching');
}
if (type === 'latestmedia') {
return catalog.filter((s) => {
const id = s?.id || '';
return id.startsWith('recently-added-') || resolveSectionJellyfinId(s) === 'latestmedia';
return id.startsWith('recently-added-') || sectionMatchesNativeType(s, 'latestmedia');
});
}
if (type === 'livetv') {
return catalog.filter((s) => resolveSectionJellyfinId(s) === 'livetv');
}
return catalog.filter((s) => resolveSectionJellyfinId(s) === type);
return catalog.filter((s) => sectionMatchesNativeType(s, type));
}
const RECENTLY_ADDED_PREFIX = 'recently-added-';
@@ -1267,9 +1320,13 @@
uniqueNative.forEach((nativeType) => {
const candidates = resolveKefinCandidatesForNative(nativeType, catalog);
if (!candidates.length) return;
// Covered if any candidate is already on (includes continueWatchingAndNextUp)
if (candidates.some(sectionIsEnabled)) return;
candidates.forEach((section) => {
// Never auto-enable the combined Continue Watching + Next Up row
if (section.id === 'continueWatchingAndNextUp') return;
const storedId = getStoredSectionPrefId(section);
const existing = prefMap.get(getPrefDedupeKey({ id: section.id }))
|| prefMap.get(getPrefDedupeKey({ id: storedId }))
@@ -2761,6 +2818,8 @@
resolvePairNativeHomeSections,
getServerPairNativeHomeSectionsDefault,
buildHomesectionSlotsFromKefin,
getSectionJellyfinIds,
sectionMatchesNativeType,
resolveSectionJellyfinId,
resolveHomesectionValue,
DEFAULT_NATIVE_HOME_SECTIONS,