From f92dd81c8f0ed42bbdee0867794255bf3e085400 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Wed, 9 Jul 2025 17:03:31 +0200 Subject: [PATCH] UI - Favicons - Try /favicon.ico if no other was specified in the document --- .../content_fetchers/res/favicon-fetcher.js | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/changedetectionio/content_fetchers/res/favicon-fetcher.js b/changedetectionio/content_fetchers/res/favicon-fetcher.js index a9c41fd2..f1a0fd8d 100644 --- a/changedetectionio/content_fetchers/res/favicon-fetcher.js +++ b/changedetectionio/content_fetchers/res/favicon-fetcher.js @@ -1,32 +1,42 @@ -async () => { - const links = Array.from(document.querySelectorAll('link[rel~="apple-touch-icon"], link[rel~="icon"]')); +(async () => { + const links = Array.from(document.querySelectorAll( + 'link[rel~="apple-touch-icon"], link[rel~="icon"]' + )); + const icons = links.map(link => { - const sizesStr = link.getAttribute('sizes'); - let size = 0; - if (sizesStr) { - const [w] = sizesStr.split('x').map(Number); - if (!isNaN(w)) size = w; - } else { - size = 16; - } - return { - size, - rel: link.getAttribute('rel'), - href: link.href - }; + const sizesStr = link.getAttribute('sizes'); + let size = 0; + if (sizesStr) { + const [w] = sizesStr.split('x').map(Number); + if (!isNaN(w)) size = w; + } else { + size = 16; + } + return { + size, + rel: link.getAttribute('rel'), + href: link.href + }; }); - if (icons.length === 0) return null; + // If no icons found, add fallback favicon.ico + if (icons.length === 0) { + icons.push({ + size: 16, + rel: 'icon', + href: '/favicon.ico' + }); + } + // sort preference icons.sort((a, b) => { - const isAppleA = /apple-touch-icon/.test(a.rel); - const isAppleB = /apple-touch-icon/.test(b.rel); - if (isAppleA && !isAppleB) return -1; - if (!isAppleA && isAppleB) return 1; - return b.size - a.size; + const isAppleA = /apple-touch-icon/.test(a.rel); + const isAppleB = /apple-touch-icon/.test(b.rel); + if (isAppleA && !isAppleB) return -1; + if (!isAppleA && isAppleB) return 1; + return b.size - a.size; }); - // Set a timeout value in ms const timeoutMs = 2000; for (const icon of icons) { @@ -42,13 +52,12 @@ async () => { clearTimeout(timeout); if (!resp.ok) { - // skip 404, 500, etc. continue; } const blob = await resp.blob(); - // Convert to base64 + // Convert blob to base64 const reader = new FileReader(); return await new Promise(resolve => { reader.onloadend = () => { @@ -61,10 +70,10 @@ async () => { }); } catch (e) { - // fetch error, timeout, or abort continue; } } + // nothing found return null; -} +})();