Fix issues preventing Watchlist page from rendering. Also add link location configuration.

This commit is contained in:
HighImKevin committed 2026-09-25 05:07:00 -04:00
1 parent 95c57843cf
commit e091f7a286
3 files changed
+100 -120

No files matched your search

+29 -7
View File
@@ -10,16 +10,21 @@
const DEFAULTS = {
sideMenu: true,
topNavigation: true,
userMenu: true
topNavigation: 'main',
userMenu: true,
order: 2
};
function normalizeConfig(raw) {
const cfg = raw && typeof raw === 'object' ? raw : {};
const orderRaw = parseInt(cfg.order, 10);
return {
sideMenu: cfg.sideMenu !== false,
topNavigation: cfg.topNavigation !== false && cfg.topNavigation !== 'none',
userMenu: cfg.userMenu !== false
topNavigation: typeof cfg.topNavigation === 'boolean'
? (cfg.topNavigation ? 'main' : 'none')
: cfg.topNavigation === undefined ? 'main' : cfg.topNavigation,
userMenu: cfg.userMenu !== false,
order: Number.isFinite(orderRaw) ? orderRaw : DEFAULTS.order
};
}
@@ -72,6 +77,7 @@
}
function buildConfigHTML(config) {
const order = Number.isFinite(config.order) ? config.order : DEFAULTS.order;
return `
<div style="max-width: 640px;">
${buildToggleCard(
@@ -82,7 +88,7 @@
)}
${buildToggleCard(
'wl-top-navigation',
config.topNavigation,
config.topNavigation !== false && config.topNavigation !== 'none',
'Top navigation',
'Show Watchlist in the main top navigation bar (or via Custom Tabs when available).'
)}
@@ -92,6 +98,18 @@
'User menu',
'Show a Watchlist link in the user settings menu.'
)}
<div class="listItem" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 0.75em; margin-bottom: 0.75em;">
<div class="listItemContent">
<div class="listItemBodyText" style="margin-bottom: 0.5em;">Menu order</div>
<div class="listItemBodyText secondary" style="margin-bottom: 0.75em; font-size: 0.9em;">
Lower values appear earlier in the side menu and top navigation.
</div>
<div class="hsae-field-wrap">
<input type="number" id="wl-order" class="fld emby-input"
min="0" step="1" value="${order}">
</div>
</div>
</div>
</div>
`;
}
@@ -100,10 +118,14 @@
const side = document.getElementById('wl-side-menu');
const top = document.getElementById('wl-top-navigation');
const user = document.getElementById('wl-user-menu');
const orderRaw = parseInt(document.getElementById('wl-order')?.value || String(DEFAULTS.order), 10);
return {
sideMenu: side ? side.checked : DEFAULTS.sideMenu,
topNavigation: top ? top.checked : DEFAULTS.topNavigation,
userMenu: user ? user.checked : DEFAULTS.userMenu
topNavigation: top
? (top.checked ? 'main' : 'none')
: DEFAULTS.topNavigation,
userMenu: user ? user.checked : DEFAULTS.userMenu,
order: Number.isFinite(orderRaw) ? orderRaw : DEFAULTS.order
};
}
-1
View File
@@ -9,7 +9,6 @@
/* Watchlist Tab Styling */
.sections.watchlist {
padding-bottom: 20px;
margin-top: 20px;
}
.sections.watchlist .watchlist-tabs {
+71 -112
View File
@@ -4880,81 +4880,33 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
await window.userHelper.waitForLogin();
const maxRetries = 10;
let retries = 0;
// Use WebSocket to listen for UserDataChanged messages instead of intercepting fetch
// This is more reliable and doesn't interfere with fetch requests
function setupWebSocketMonitoring() {
try {
// Grab the actual socket
const socket = (window.ApiClient && (window.ApiClient.webSocket || window.ApiClient._webSocket)) || null;
function startWatchlistUserDataListener() {
if (playbackMonitorInitialized) {
return;
}
if (!socket) {
if (retries >= maxRetries) {
ERR('Max retries reached, giving up on WebSocket monitoring');
return false;
if (window.websocketHelper?.listen) {
window.websocketHelper.listen('UserDataChanged', (msg) => {
const entries = msg?.Data?.UserDataList;
if (!Array.isArray(entries) || !entries.length) return;
for (const userData of entries) {
if (!userData?.ItemId) continue;
LOG(`Detected UserDataChanged for item: ${userData.ItemId}, Played: ${userData.Played}`);
handleItemWatchedStatusChange(userData.ItemId, userData.Played, userData.Likes).catch(err => {
ERR('Error handling UserDataChanged event:', err);
});
}
retries++;
setTimeout(() => {
if (!playbackMonitorInitialized) {
setupWebSocketMonitoring();
}
}, 1000);
return;
}
// Store original handler if it exists
const originalHandler = socket.onmessage;
// Hook into onmessage
socket.onmessage = function(event) {
try {
// Pass it through so Jellyfin still works normally
if (originalHandler) {
originalHandler.call(this, event);
}
const messageData = event.Data || event.data;
const data = typeof messageData === 'string' ? JSON.parse(messageData) : messageData;
// Check if this is a UserDataChanged message
if (data.MessageType === 'UserDataChanged' && data.Data && data.Data.UserDataList && data.Data.UserDataList.length > 0) {
const userData = data.Data.UserDataList[0];
if (userData.ItemId) {
LOG(`Detected UserDataChanged for item: ${userData.ItemId}, Played: ${userData.Played}`);
// Handle the watched status change (works for both played and unplayed)
handleItemWatchedStatusChange(userData.ItemId, userData.Played, userData.Likes).catch(err => {
ERR('Error handling UserDataChanged event:', err);
});
}
}
} catch (err) {
// Log parse errors but don't break the original handler
WARN('WebSocket message parse error:', err);
}
};
});
playbackMonitorInitialized = true;
LOG('Playback and watch status monitoring initialized via WebSocket');
return true;
} catch (err) {
ERR('Error setting up WebSocket monitoring:', err);
return false;
return;
}
setTimeout(startWatchlistUserDataListener, 1000);
}
//setupWebSocketMonitoring();
// Try to set up immediately
if (!setupWebSocketMonitoring()) {
// If WebSocket isn't available yet, retry after a short delay
// This can happen if the page loads before the WebSocket connection is established
setTimeout(() => {
if (!playbackMonitorInitialized) {
setupWebSocketMonitoring();
}
}, 1000);
}
startWatchlistUserDataListener();
}
let watchedItems = [];
@@ -7428,22 +7380,26 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
}
}
// Optional onViewPage backup (primary mount path is MutationObserver + addCustomPage)
if (window.KefinTweaksUtils && window.KefinTweaksUtils.onViewPage) {
window.KefinTweaksUtils.onViewPage(async (view, element, hash) => {
const watchlistPath = window.KefinTweaksUtils._watchlistUrl || '#/watchlist';
if (hash.includes(watchlistPath)) {
LOG('onViewPage: Watchlist');
renderWatchlist();
}
}, {
pages: []
});
LOG('Registered onViewPage handler for Watchlist');
} else {
WARN('KefinTweaksUtils.onViewPage not available');
async function addWatchlistPageHandler() {
// Primary init is addCustomPage onShow + kefin:custompage-show; keep onViewPage as backup for home-tab URLs
if (window.KefinTweaksUtils && window.KefinTweaksUtils.onViewPage) {
window.KefinTweaksUtils.onViewPage(async (view, element, hash) => {
const watchlistUrl = await window.KefinTweaksUtils.getWatchlistUrl();
if (watchlistUrl && hash.includes(watchlistUrl)) {
LOG('onViewPage: Watchlist (backup)');
renderWatchlist();
}
}, {
pages: []
});
LOG('Registered onViewPage handler for Watchlist');
} else {
WARN('KefinTweaksUtils.onViewPage not available');
}
}
let watchlistCustomPageShowBound = false;
async function addCustomWatchlistPage() {
if (!window.KefinTweaksUtils?.addCustomPage) {
WARN('addCustomPage not available yet');
@@ -7453,8 +7409,27 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
window.KefinTweaksUtils.addCustomPage(
watchlistUrl,
'Watchlist',
'<div class="sections watchlist"></div>'
'<div class="sections watchlist"></div>',
() => {
LOG('onShow: Watchlist');
renderWatchlist();
}
);
if (!watchlistCustomPageShowBound) {
watchlistCustomPageShowBound = true;
document.addEventListener('kefin:custompage-show', async (e) => {
const hrefPath = e.detail?.hrefPath;
if (!hrefPath) return;
const url = await window.KefinTweaksUtils.getWatchlistUrl();
const normalized = url && String(url).split('?')[0];
if (hrefPath === normalized || (url && hrefPath === url)) {
LOG('kefin:custompage-show: Watchlist');
renderWatchlist();
}
});
}
LOG('Registered custom page Watchlist');
return true;
}
@@ -7470,34 +7445,20 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
window.KefinTweaksConfig?.watchlist
) || {
sideMenu: window.KefinTweaksConfig?.watchlist?.sideMenu !== false,
topNavigation: window.KefinTweaksConfig?.watchlist?.topNavigation !== false
&& window.KefinTweaksConfig?.watchlist?.topNavigation !== 'none',
userMenu: window.KefinTweaksConfig?.watchlist?.userMenu !== false
topNavigation: window.KefinTweaksConfig?.watchlist?.topNavigation === undefined ? 'main' : window.KefinTweaksConfig?.watchlist?.topNavigation,
userMenu: window.KefinTweaksConfig?.watchlist?.userMenu !== false,
order: window.KefinTweaksConfig?.watchlist?.order !== undefined ? window.KefinTweaksConfig?.watchlist?.order : 2
};
const watchlistTabIndex = await getWatchlistTabIndex();
const watchlistUrl = await window.KefinTweaksUtils.getWatchlistUrl();
const options = {
sideMenu: watchlistCfg.sideMenu !== false,
userMenu: watchlistCfg.userMenu === true,
order: 2
topNavigation: watchlistCfg.topNavigation,
order: watchlistCfg.order
};
let watchlistUrl = '#/watchlist';
const isModernUI = window.KefinTweaksUtils.isModernUI();
const wantTopNav = watchlistCfg.topNavigation !== false && watchlistCfg.topNavigation !== 'none';
// If watchlist tab index is null or undefined, add to top navigation
if (wantTopNav && (watchlistTabIndex === null || watchlistTabIndex === undefined || isModernUI)) {
options.topNavigation = 'main';
} else if (!wantTopNav) {
options.topNavigation = 'none';
} else {
watchlistUrl = `#/home?tab=${watchlistTabIndex}`;
}
window.KefinTweaksUtils._watchlistUrl = watchlistUrl;
window.KefinTweaksUtils.addCustomMenuLink(
'Watchlist',
'bookmark',
@@ -7510,18 +7471,16 @@ In the Custom Tabs plugin, add a new tab with the following HTML content:
function waitForUtilsAndRegisterWatchlist() {
const ready = () => {
if (!window.KefinTweaksUtils?.addCustomPage || !window.KefinTweaksUtils?.addCustomMenuLink) {
if (!window.KefinTweaksUtils?.addCustomPage || !window.KefinTweaksUtils?.addCustomMenuLink || !window.KefinTweaksUtils?.onViewPage) {
return false;
}
const api = (typeof ApiClient !== 'undefined' && ApiClient) || window.ApiClient || null;
if (!api) return false;
const serverAddress = api._serverAddress || (typeof api.serverAddress === 'function' ? api.serverAddress() : null);
return !!serverAddress;
return true;
};
const run = () => {
addCustomWatchlistPage();
addCustomMenuLink();
const run = async () => {
await addCustomWatchlistPage();
await addCustomMenuLink();
await addWatchlistPageHandler();
};
if (ready()) {