From 773393ef3e159b2470d30d7a63c8e9ecfb3d6cc5 Mon Sep 17 00:00:00 2001 From: HighImKevin Date: Thu, 10 Sep 2026 09:20:29 -0400 Subject: [PATCH] Fix Continue Watching and Next Up not appearing correctly --- configuration/homeScreen-configuration.js | 168 ++++-------------- configuration/homeScreen-migration.js | 1 - .../homeScreen-user-configuration.js | 77 +++++--- configuration/homeScreenAdvancedEditor.js | 4 +- scripts/homeScreen3.js | 49 ----- scripts/homeScreenConfig2.js | 9 +- 6 files changed, 92 insertions(+), 216 deletions(-) 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,