diff --git a/configuration/homeScreen-configuration.js b/configuration/homeScreen-configuration.js
index 07663f3..e18c915 100644
--- a/configuration/homeScreen-configuration.js
+++ b/configuration/homeScreen-configuration.js
@@ -155,6 +155,30 @@
return flattened;
}
+ const CONTINUE_WATCHING_SECTION_IDS = new Set([
+ 'continueWatching',
+ 'nextUp',
+ 'continueWatchingAndNextUp'
+ ]);
+
+ /**
+ * Clear MERGE_NEXT_UP-era hidden flags so CW / Next Up / combined are independent sections.
+ */
+ function normalizeContinueWatchingVisibility(config) {
+ if (!config) return;
+ const groupKeys = ['HOME_SECTION_GROUPS', 'SEASONAL_SECTION_GROUPS', 'DISCOVERY_SECTION_GROUPS', 'CUSTOM_SECTION_GROUPS'];
+ groupKeys.forEach((key) => {
+ (config[key] || []).forEach((group) => {
+ (group.sections || []).forEach((section) => {
+ if (section?.id && CONTINUE_WATCHING_SECTION_IDS.has(section.id)) {
+ section.hidden = false;
+ }
+ });
+ });
+ });
+ delete config.MERGE_NEXT_UP;
+ }
+
/**
* Collect discovery-enabled sections from custom groups, stamping pageNumber
* per group (1..N among discovery sections only, in group order).
@@ -927,10 +951,11 @@
CACHE: { ...defaults.CACHE, ...(existingConfig.CACHE || {}) },
SPOTLIGHT_SETTINGS: { ...defaults.SPOTLIGHT_SETTINGS, ...(existingConfig.SPOTLIGHT_SETTINGS || {}) },
HOME_SETTINGS: { ...defaults.HOME_SETTINGS, ...(existingConfig.HOME_SETTINGS || {}) },
- USER_HOME_SCREEN_SETTINGS: { ...defaults.USER_HOME_SCREEN_SETTINGS, ...(existingConfig.USER_HOME_SCREEN_SETTINGS || {}) },
- MERGE_NEXT_UP: existingConfig.MERGE_NEXT_UP ?? defaults.MERGE_NEXT_UP ?? false
+ USER_HOME_SCREEN_SETTINGS: { ...defaults.USER_HOME_SCREEN_SETTINGS, ...(existingConfig.USER_HOME_SCREEN_SETTINGS || {}) }
};
+ normalizeContinueWatchingVisibility(mergedConfig);
+
// Add ENABLED_NORMAL_SECTIONS and ENABLED_DISCOVERY_SECTIONS
// ENABLED_NORMAL_SECTIONS is all enabled sections from HOME_SECTION_GROUPS, SEASONAL_SECTION_GROUPS, and CUSTOM_SECTION_GROUPS that aren't discovery sections
// If they have a start/end date the current date must fall within that date range
@@ -1522,10 +1547,11 @@
USER_HOME_SCREEN_SETTINGS: {
...(savedHomeScreenConfig.USER_HOME_SCREEN_SETTINGS || {}),
...(config.USER_HOME_SCREEN_SETTINGS || {})
- },
- MERGE_NEXT_UP: config.MERGE_NEXT_UP !== undefined ? config.MERGE_NEXT_UP : (savedHomeScreenConfig.MERGE_NEXT_UP !== undefined ? savedHomeScreenConfig.MERGE_NEXT_UP : false)
+ }
};
+ normalizeContinueWatchingVisibility(mergedHomeScreenConfig);
+
// Empty values (= blank CustomPrefs segments) must not persist to JS Injector
omitEmptyPropsFromGroups(mergedHomeScreenConfig.HOME_SECTION_GROUPS);
omitEmptyPropsFromGroups(mergedHomeScreenConfig.SEASONAL_SECTION_GROUPS);
@@ -1760,33 +1786,9 @@
function buildSectionContentHTML(sectionType) {
// Always use currentConfig as source of truth
if (!currentConfig) return '
No configuration loaded.
';
-
- const mergeNextUp = currentConfig.MERGE_NEXT_UP || false;
-
- // Filter groups based on mergeNextUp setting
- const getFilteredGroups = (groups) => {
- if (mergeNextUp) {
- // Filter out groups containing nextUp or continueWatching sections when merged
- return groups.map(group => {
- const filteredSections = (group.sections || []).filter(s =>
- s.id !== 'nextUp' && s.id !== 'continueWatching'
- );
- return { ...group, sections: filteredSections };
- }).filter(group => group.sections.length > 0);
- } else {
- // Filter out continueWatchingAndNextUp section
- return groups.map(group => {
- const filteredSections = (group.sections || []).filter(s =>
- s.id !== 'continueWatchingAndNextUp'
- );
- return { ...group, sections: filteredSections };
- }).filter(group => group.sections.length > 0);
- }
- };
switch (sectionType) {
case 'home': {
- //const homeGroups = getFilteredGroups(currentConfig.HOME_SECTION_GROUPS || []);
const homeGroups = currentConfig.HOME_SECTION_GROUPS || [];
return `
@@ -2228,8 +2230,7 @@
CACHE: currentConfig?.CACHE,
SPOTLIGHT_SETTINGS: currentConfig?.SPOTLIGHT_SETTINGS,
HOME_SETTINGS: currentConfig?.HOME_SETTINGS,
- USER_HOME_SCREEN_SETTINGS: currentConfig?.USER_HOME_SCREEN_SETTINGS,
- MERGE_NEXT_UP: currentConfig?.MERGE_NEXT_UP
+ USER_HOME_SCREEN_SETTINGS: currentConfig?.USER_HOME_SCREEN_SETTINGS
};
}
@@ -2784,82 +2785,6 @@
// Attach initial section row listeners
attachSectionRowListeners(dialog);
-
- // Merge Next Up toggle - update currentConfig immediately and refresh sections tab
- // Use event delegation to prevent stacking listeners when switching tabs
- /* dialog.addEventListener('click', async (e) => {
- const btn = e.target.closest('#merge-next-up');
- if (!btn) return;
- currentConfig.MERGE_NEXT_UP = btn.checked;
-
- // Hide continue watching and next up sections if merge next up is enabled
- const continueWatchingGroup = currentConfig.HOME_SECTION_GROUPS.find(g => g.name === 'Continue Watching');
- if (continueWatchingGroup) {
- continueWatchingGroup.sections = continueWatchingGroup.sections.map(s => {
- if (s.id === 'continueWatchingAndNextUp') {
- s.enabled = currentConfig.MERGE_NEXT_UP;
- s.hidden = !currentConfig.MERGE_NEXT_UP;
- } else {
- s.enabled = !currentConfig.MERGE_NEXT_UP;
- s.hidden = currentConfig.MERGE_NEXT_UP;
- }
- return s;
- });
-
- // Update continueWatchingGroup in currentConfig
- currentConfig.HOME_SECTION_GROUPS = currentConfig.HOME_SECTION_GROUPS.map(g => {
- if (g.name === 'Continue Watching') {
- return continueWatchingGroup;
- }
- return g;
- });
- }
-
- await saveConfig(currentConfig);
- refreshMainModal();
- }); */
-
- /* const mergeNextUpCheckbox = dialog.querySelector('#merge-next-up');
- if (mergeNextUpCheckbox) {
- mergeNextUpCheckbox.addEventListener('change', async () => {
- currentConfig.MERGE_NEXT_UP = mergeNextUpCheckbox.checked;
-
- // Hide continue watching and next up sections if merge next up is enabled
- const continueWatchingGroup = currentConfig.HOME_SECTION_GROUPS.find(g => g.name === 'Continue Watching');
- if (continueWatchingGroup) {
- continueWatchingGroup.sections = continueWatchingGroup.sections.map(s => {
- if (s.id === 'continueWatchingAndNextUp') {
- s.enabled = currentConfig.MERGE_NEXT_UP;
- s.hidden = !currentConfig.MERGE_NEXT_UP;
- } else {
- s.enabled = !currentConfig.MERGE_NEXT_UP;
- s.hidden = currentConfig.MERGE_NEXT_UP;
- }
- return s;
- });
-
- // Update continueWatchingGroup in currentConfig
- currentConfig.HOME_SECTION_GROUPS = currentConfig.HOME_SECTION_GROUPS.map(g => {
- if (g.name === 'Continue Watching') {
- return continueWatchingGroup;
- }
- return g;
- });
- }
-
- await saveConfig(currentConfig);
- // Refresh sections tab if it's currently visible
- const sectionsTab = dialog.querySelector('#tab-sections');
- if (sectionsTab && sectionsTab.style.display !== 'none') {
- const contentContainer = dialog.querySelector('#section-content');
- if (contentContainer) {
- const activeNavBtn = dialog.querySelector('.section-type-nav-btn.active');
- const activeType = activeNavBtn ? activeNavBtn.dataset.sectionType : 'home';
- contentContainer.innerHTML = buildSectionContentHTML(activeType);
- }
- }
- });
- } */
// Group toggle-all buttons
// Use event delegation to prevent stacking listeners when switching tabs
@@ -3459,39 +3384,8 @@
const checkbox = document.getElementById(checkboxId);
if (checkbox) {
checkbox.checked = !checkbox.checked;
- // Trigger change event for any listeners (like merge-next-up)
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
updateToggleSliderUI(btn, checkbox.checked);
-
- if (checkboxId === 'merge-next-up') {
- currentConfig.MERGE_NEXT_UP = checkbox.checked;
-
- // Hide continue watching and next up sections if merge next up is enabled
- const continueWatchingGroup = currentConfig.HOME_SECTION_GROUPS.find(g => g.name === 'Continue Watching');
- if (continueWatchingGroup) {
- continueWatchingGroup.sections = continueWatchingGroup.sections.map(s => {
- if (s.id === 'continueWatchingAndNextUp') {
- s.enabled = currentConfig.MERGE_NEXT_UP;
- s.hidden = !currentConfig.MERGE_NEXT_UP;
- } else {
- s.enabled = !currentConfig.MERGE_NEXT_UP;
- s.hidden = currentConfig.MERGE_NEXT_UP;
- }
- return s;
- });
-
- // Update continueWatchingGroup in currentConfig
- currentConfig.HOME_SECTION_GROUPS = currentConfig.HOME_SECTION_GROUPS.map(g => {
- if (g.name === 'Continue Watching') {
- return continueWatchingGroup;
- }
- return g;
- });
- }
-
- await saveConfig(currentConfig);
- refreshMainModal();
- }
}
} else {
// Fallback: just toggle the visual state
diff --git a/configuration/homeScreen-migration.js b/configuration/homeScreen-migration.js
index 576efd7..033caec 100644
--- a/configuration/homeScreen-migration.js
+++ b/configuration/homeScreen-migration.js
@@ -49,7 +49,6 @@
DISCOVERY_SECTION_GROUPS: JSON.parse(JSON.stringify(defaults.DISCOVERY_SECTION_GROUPS || [])),
CUSTOM_SECTION_GROUPS: [],
REMOVE_CONFLICTING_SECTIONS: legacyHomeScreen.removeConflictingSections !== false,
- MERGE_NEXT_UP: false,
DISCOVERY_SETTINGS: JSON.parse(JSON.stringify(defaults.DISCOVERY_SETTINGS || {})),
SEASONAL_THEME_SETTINGS: JSON.parse(JSON.stringify(defaults.SEASONAL_THEME_SETTINGS || {}))
};
diff --git a/configuration/homeScreen-user-configuration.js b/configuration/homeScreen-user-configuration.js
index 28a8e40..7d5b1c6 100644
--- a/configuration/homeScreen-user-configuration.js
+++ b/configuration/homeScreen-user-configuration.js
@@ -1598,6 +1598,37 @@
}
}
+ /**
+ * Wait until admin homeScreen-configuration has registered getSections/getConfig.
+ * Injector loads deps in parallel, so prefs can open before that API exists.
+ */
+ function waitForHomeScreenSectionsApi(timeoutMs = 15000, intervalMs = 100) {
+ return new Promise((resolve) => {
+ if (typeof window.KefinHomeScreen?.getSections === 'function'
+ && typeof window.KefinHomeScreen?.getConfig === 'function') {
+ resolve(true);
+ return;
+ }
+ const start = Date.now();
+ const timer = setInterval(() => {
+ if (typeof window.KefinHomeScreen?.getSections === 'function'
+ && typeof window.KefinHomeScreen?.getConfig === 'function') {
+ clearInterval(timer);
+ resolve(true);
+ return;
+ }
+ if (Date.now() - start >= timeoutMs) {
+ clearInterval(timer);
+ resolve(false);
+ }
+ }, intervalMs);
+ });
+ }
+
+ function delay(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
/**
* Render user home sections editor
*/
@@ -1608,14 +1639,18 @@
}
try {
- // Check dependencies
- if (!window.KefinHomeScreen || !window.KefinHomeScreen.getSections) {
+ container.innerHTML = 'Loading home sections…
';
+
+ const apiReady = await waitForHomeScreenSectionsApi();
+ if (!apiReady) {
ERR('KefinHomeScreen.getSections not available');
+ container.innerHTML = 'Error loading editor. Please refresh the page.
';
return;
}
if (!window.KefinTweaksUI || !window.KefinTweaksUI.renderHomeSectionsOrderEditor) {
ERR('KefinTweaksUI.renderHomeSectionsOrderEditor not available');
+ container.innerHTML = 'Error loading editor. Please refresh the page.
';
return;
}
@@ -1624,7 +1659,7 @@
let allSections = (userConfig.sections || []).filter((s) => {
if (s.userConfigurable === false) return false;
if (s.type === 'discovery' || s.discoveryEnabled === true) return false;
- // Keep hidden sections only when currently enabled (e.g. merge CW+Next Up)
+ // Keep hidden sections only when currently enabled
if (s.hidden === true && s.enabled === false) return false;
return true;
});
@@ -1802,26 +1837,28 @@
}
window.KefinTweaksUtils.onViewPage(async (view, element, hash) => {
- // Check if we're on the mypreferenceshome page
LOG('Preferences home page detected');
- const form = document.querySelector('.libraryPage:not(.hide) .homeScreenSettingsContainer > form');
- if (form && form.firstChild) {
- // Check if editor already rendered
- if (form.querySelector('#kefin-user-home-sections-editor')) {
- return;
- }
-
- // Create container for editor
- const editorContainer = document.createElement('div');
- editorContainer.id = 'kefin-user-home-sections-editor';
-
- // Insert as last child of the form
- form.appendChild(editorContainer);
-
- // Render editor
- await renderUserHomeSectionsEditor(editorContainer);
+ let form = null;
+ for (let attempt = 0; attempt < 20; attempt++) {
+ form = document.querySelector('.libraryPage:not(.hide) .homeScreenSettingsContainer > form');
+ if (form && form.firstChild) break;
+ await delay(150);
}
+
+ if (!form || !form.firstChild) {
+ WARN('Home settings form not ready');
+ return;
+ }
+
+ if (form.querySelector('#kefin-user-home-sections-editor')) {
+ return;
+ }
+
+ const editorContainer = document.createElement('div');
+ editorContainer.id = 'kefin-user-home-sections-editor';
+ form.appendChild(editorContainer);
+ await renderUserHomeSectionsEditor(editorContainer);
}, {
pages: ['mypreferenceshome', 'userpreferences']
});
diff --git a/configuration/homeScreenAdvancedEditor.js b/configuration/homeScreenAdvancedEditor.js
index fec6977..d388c58 100644
--- a/configuration/homeScreenAdvancedEditor.js
+++ b/configuration/homeScreenAdvancedEditor.js
@@ -963,7 +963,9 @@
hideWatched: inferHideWatched(section),
hideName: section.hideName === true,
userEnabledByDefault: section.enabled !== false,
- userConfigurable: section.hidden !== true,
+ userConfigurable: section.userConfigurable !== undefined
+ ? section.userConfigurable !== false
+ : section.hidden !== true,
groupSelect: groupSelection.groupSelect,
groupNew: groupSelection.groupNew,
queries: (section.queries?.length ? section.queries : [createDefaultQuery()]).map(q => ({ ...q })),
diff --git a/scripts/homeScreen3.js b/scripts/homeScreen3.js
index 52d1c74..f096ef2 100644
--- a/scripts/homeScreen3.js
+++ b/scripts/homeScreen3.js
@@ -737,32 +737,6 @@
});
}
- // Handle MergeNextUp setting - check if continueWatchingAndNextUp should be enabled
- const mergeNextUp = await getMergeNextUpSetting();
- if (mergeNextUp) {
- // Find and disable nextUp and continueWatching sections when merged
- const nextUpIndex = merged.findIndex(s => s.id === 'nextUp');
- const continueWatchingIndex = merged.findIndex(s => s.id === 'continueWatching');
- if (nextUpIndex !== -1) {
- merged[nextUpIndex].enabled = false;
- }
- if (continueWatchingIndex !== -1) {
- merged[continueWatchingIndex].enabled = false;
- }
- // Enable the merged section
- const mergedIndex = merged.findIndex(s => s.id === 'continueWatchingAndNextUp');
- if (mergedIndex !== -1) {
- merged[mergedIndex].enabled = true;
- LOG('MergeNextUp enabled: Using continueWatchingAndNextUp section');
- }
- } else {
- // Disable merged section if merge is not enabled
- const mergedIndex = merged.findIndex(s => s.id === 'continueWatchingAndNextUp');
- if (mergedIndex !== -1) {
- merged[mergedIndex].enabled = false;
- }
- }
-
// Filter seasonal sections based on date
const filteredMerged = merged.filter(section => {
if (section.startDate && section.endDate) {
@@ -1135,7 +1109,6 @@
let performanceDuration;
const performanceTimes = [];
- const mergeNextUp = await getMergeNextUpSetting();
for (const sectionConfig of sortedSections) {
const loopStartTime = performance.now();
@@ -1158,19 +1131,6 @@
continue;
}
- // Check merge setting for continueWatchingAndNextUp
- if (sectionConfig.id === 'continueWatchingAndNextUp' && !mergeNextUp) {
- LOG(`Skipping continueWatchingAndNextUp: merge not enabled`);
- continue;
- }
-
- // Skip individual sections when merge is enabled
- if (mergeNextUp &&
- (sectionConfig.id === 'continueWatching' || sectionConfig.id === 'nextUp')) {
- LOG(`Skipping ${sectionConfig.id}: merge enabled, using continueWatchingAndNextUp`);
- continue;
- }
-
sectionPromises.push(loadSectionForRendering(sectionConfig));
const loopEndTime = performance.now();
@@ -1252,15 +1212,6 @@
return date.toLocaleDateString('en-US', options);
}
- async function getMergeNextUpSetting() {
- const config = await window.KefinHomeScreen.getConfig();
- const mergeNextUp = config.MERGE_NEXT_UP;
- if (mergeNextUp !== undefined) {
- return mergeNextUp;
- }
- return false;
- }
-
let _displayPreferencesPromise = null;
async function fetchDisplayPreferences() {
diff --git a/scripts/homeScreenConfig2.js b/scripts/homeScreenConfig2.js
index d647b28..bb18334 100644
--- a/scripts/homeScreenConfig2.js
+++ b/scripts/homeScreenConfig2.js
@@ -75,7 +75,6 @@
name: 'Continue Watching',
jellyfinId: 'resume',
enabled: false,
- hidden: false,
order: 10,
cardFormat: 'Thumb',
ttl: CACHE_CONFIG.FORCE_REFRESH_TTL,
@@ -99,7 +98,6 @@
name: 'Next Up',
jellyfinId: 'nextup',
enabled: false,
- hidden: false,
order: 11,
cardFormat: 'Thumb',
ttl: CACHE_CONFIG.FORCE_REFRESH_TTL,
@@ -122,9 +120,8 @@
id: 'continueWatchingAndNextUp',
name: 'Continue Watching and Next Up',
jellyfinId: 'resume',
- enabled: false, // Disabled by default - only used when merge setting is enabled
+ enabled: false,
order: 10,
- hidden: true,
cardFormat: 'Thumb',
ttl: CACHE_CONFIG.FORCE_REFRESH_TTL,
sortBy: 'DatePlayed', // Used to sort merged results from both queries
@@ -161,7 +158,6 @@
name: 'Continue Listening',
jellyfinId: 'resumeaudio',
enabled: false,
- hidden: false,
order: 10,
cardFormat: 'Square',
ttl: CACHE_CONFIG.FORCE_REFRESH_TTL,
@@ -185,7 +181,6 @@
name: 'Continue Reading',
jellyfinId: 'resumebook',
enabled: false,
- hidden: false,
order: 10,
cardFormat: 'Poster',
ttl: CACHE_CONFIG.FORCE_REFRESH_TTL,
@@ -1542,7 +1537,6 @@
DISCOVERY_SECTION_GROUPS: KEFINTWEAKS_DISCOVERY_SECTION_GROUPS,
CUSTOM_SECTION_GROUPS: [], // Empty by default - user-created/imported sections
REMOVE_CONFLICTING_SECTIONS: true,
- MERGE_NEXT_UP: false,
HOME_SETTINGS: HOME_SETTINGS,
USER_HOME_SCREEN_SETTINGS: USER_HOME_SCREEN_SETTINGS,
DISCOVERY_SETTINGS: DISCOVERY_SETTINGS,
@@ -1558,7 +1552,6 @@
DISCOVERY_SECTION_GROUPS: KEFINTWEAKS_DISCOVERY_SECTION_GROUPS, // modified by user configuration
CUSTOM_SECTION_GROUPS: [], // Empty by default - user-created/imported sections // modified by user configuration
REMOVE_CONFLICTING_SECTIONS: true,
- MERGE_NEXT_UP: false,
HOME_SETTINGS: HOME_SETTINGS,
DISCOVERY_SETTINGS: DISCOVERY_SETTINGS,
SEASONAL_THEME_SETTINGS: SEASONAL_THEME_SETTINGS,