How to Add Favicons to Your Bookmarks

Many people store (and view) bookmarks in a DEVONthink database. Often these have the generic bookmark icon which makes it more difficult to visually identify the site they’re from. However, with a little automation, you can use the site’s favicon.

If you have existing bookmarks, you can use a batch process to select and update their thumbnails to a favicon.

  1. Select your database in the Navigate sidebar. Then create a new local smart group via Data > New > Smart Group, with a criterion Kind is Bookmark.
  2. Double-click the smart group in the item list to show all the matches.
  3. Select one or more bookmarks. If there are hundreds of them (or more), it’s best to select smaller batches, e.g., 50 or so, as this process has to make a query for the favicon for each bookmark.
  4. Choose Tools > Batch Process > Batch Process or press ⌃⌘B.
  5. Press the plus (+) button to create a new configuration.
  6. Set the action to: Apply Script > External > Add Favicons and press Apply.

If you’d like to use the favicon for future bookmarks, you can create a smart rule.

  1. Select your database in the Navigate sidebar. At the bottom of the sidebar, press the plus (+) button and choose New Smart Rule.
  2. The Search in popup will target the current database. You can change this to Databases but make sure you want this to affect all bookmarks added to all databases before you do.
  3. Add criteria: Kind is Bookmark and Item does not contain Thumbnail.
  4. Add the On Import event trigger.
  5. Set the Apply Script > External > Add Favicons action and press OK.

Add a bookmark to the database and the smart rule will attempt to change the thumbnail to the site’s favicon. But note: Not every site will return its favicon so this isn’t a 100% solution.

3 Likes

Thank you very much for sharing this, did not even know this script was part of the package :slight_smile:

QQ: is there something which can be done specifically about Youtube Favicons?

I tend to have a bunch of Youtube bookmarks, but those do not seem to work at all in my case.

P.S. I did read the caveat that this is not a 100% solution :slight_smile:

Nothing in the current version of the script. Perhaps it could be looked into at some point but it’s low priority at the moment.

‘Add Favicons’ Upgraded Script

Add Favicons (Upgraded).scpt.zip (2.7 KB)

Hello guys :waving_hand:t2:,

I’ve been using DEVONthink since early 2020. The excellent Add Favicon script by @cgrunenberg was one of my most-used scripts. I unfortunately find this script fails to download favicons for most of my bookmarks. Having browsed all the forum posts here on favicons, I noticed this shortcoming was a common issue. So, despite having no AppleScript experience, with the onset of sophisticated generative AI I sought to upgrade the script using Claude. The resulting script includes multiple new sources, and has been tested with one hundred bookmarks with all one hundred bookmarks succeeding. And @jooz, YouTube favicons are confirmed to work.

I’m sharing this script in case anybody else finds it useful!

Kind regards :grinning_face_with_smiling_eyes:,
Matthew

Overview Of The Script

Claude’s explanation of the new script

The original script has a few key weaknesses:

  1. Only tries 2 sources: the <link rel="icon"> tag and /favicon.ico — many sites use other paths

  2. No fallback chain: if the first attempt yields a bad/empty result, it gives up

  3. No Google Favicon API fallback: a highly reliable last resort that the script never uses

  4. Feed URL handling is incomplete: only converts feed:// but misses other edge cases

  5. Silent failures: the try block swallows all errors with no logging

Here’s what changed and why it’s more reliable:

6-stage fallback chain — the original tried 2 sources and gave up. This version works through six in sequence, stopping as soon as one succeeds:

  1. <link rel="icon"> in page HTML — same as before, but now properly resolves protocol-relative (//cdn.example.com/icon.png) and root-relative (/static/favicon.ico) paths, which the original script would have passed verbatim and failed silently

  2. /favicon.ico — now verified with a real HTTP status check before setting, so 404s don’t result in broken thumbnails

  3. /favicon.png — many modern sites skip .ico entirely in favour of .png

  4. /apple-touch-icon.png — high-res (typically 180×180), widely used, especially on Apple-centric sites

  5. Google Favicon API — extremely reliable CDN; uses a content-length check (>100 bytes) since Google returns 200 even for unknowns

  6. DuckDuckGo Favicon API — privacy-friendly fallback with its own large icon cache

Better URL handling — feed:// conversion was already there; the upgrade also ensures the base URL always has a trailing slash, preventing malformed paths like https://example.comfavicon.ico.

Failure summary — instead of silent errors, you get a notification when everything succeeds, or a dialog listing any records where all 6 stages failed, so you know exactly what to investigate.

View the script’s code
– Download Favicons (Upgraded)
– Original by Christian Grunenberg, Jun 02 2020.
– Upgraded with multi-stage fallback chain for higher reliability.

– Fallback order per record:
–   1. Parse  /  from the page HTML
–   2. Try /favicon.ico at the root of the domain
–   3. Try /favicon.png at the root of the domain
–   4. Try /apple-touch-icon.png (often high-res, widely supported)
–   5. Google Favicon API (https://www.google.com/s2/favicons?domain=…&sz=64)
–   6. DuckDuckGo Favicon API (https://icons.duckduckgo.com/ip3/….ico)

– Any step that returns a non-empty image sets the thumbnail and skips the rest.

on performSmartRule(theRecords)
tell application id “DNtp”
if (count of theRecords) is 0 then return

	set failedRecords to {}
	
	show progress indicator "Downloading Favicons…" steps (count of theRecords) with cancel button
	
	repeat with theRecord in theRecords
		step progress indicator ((name of theRecord) as string)
		
		set didSet to false
		
		try
			-- ── Normalise the URL ──────────────────────────────────────────
			set theURL to URL of theRecord
			if theURL is missing value or theURL is "" then error "No URL"
			
			-- Convert feed:// → https://
			if theURL begins with "feed://" then
				set theURL to "https" & (characters 5 thru -1 of theURL) as string
			end if
			
			-- Strip query strings and fragments for cleaner base URL parsing
			-- (leave theURL intact for HTML download; build theBaseURL separately)
			if theURL begins with "https://" or theURL begins with "http://" then
				
				-- ── Build base URL (scheme + host + trailing slash) ───────
				set slashes to 0
				set theBaseURL to theURL
				repeat with i from 1 to length of theURL
					if character i of theURL is "/" then
						set slashes to slashes + 1
						if slashes > 2 then
							set theBaseURL to (characters 1 thru i of theURL) as string
							exit repeat
						end if
					end if
				end repeat
				-- Ensure trailing slash
				if theBaseURL does not end with "/" then set theBaseURL to theBaseURL & "/"
				
				-- For feed records, fetch the site root rather than the feed URL
				set fetchURL to theURL
				if type of theRecord is feed then set fetchURL to theBaseURL
				
				-- ── Stage 1: Parse <link rel="icon"> from page HTML ───────
				if not didSet then
					try
						set theHTML to download markup from fetchURL
						set theFavicon to get favicon of theHTML
						if theFavicon is not missing value and theFavicon is not "" then
							-- Resolve relative paths
							if theFavicon begins with "//" then
								-- Protocol-relative
								if theURL begins with "https" then
									set theFavicon to "https:" & theFavicon
								else
									set theFavicon to "http:" & theFavicon
								end if
							else if theFavicon begins with "/" then
								-- Root-relative: prepend scheme + host (strip trailing slash then re-add path)
								set theHost to characters 1 thru -2 of theBaseURL as string -- remove trailing slash
								set theFavicon to theHost & theFavicon
							end if
							set thumbnail of theRecord to theFavicon
							set didSet to true
						end if
					end try
				end if
				
				-- ── Stage 2: /favicon.ico ─────────────────────────────────
				if not didSet then
					try
						set candidateURL to theBaseURL & "favicon.ico"
						set testResult to do shell script "curl -s -o /dev/null -w '%{http_code}' --max-time 5 --location " & quoted form of candidateURL
						if testResult is "200" then
							set thumbnail of theRecord to candidateURL
							set didSet to true
						end if
					end try
				end if
				
				-- ── Stage 3: /favicon.png ─────────────────────────────────
				if not didSet then
					try
						set candidateURL to theBaseURL & "favicon.png"
						set testResult to do shell script "curl -s -o /dev/null -w '%{http_code}' --max-time 5 --location " & quoted form of candidateURL
						if testResult is "200" then
							set thumbnail of theRecord to candidateURL
							set didSet to true
						end if
					end try
				end if
				
				-- ── Stage 4: /apple-touch-icon.png ────────────────────────
				if not didSet then
					try
						set candidateURL to theBaseURL & "apple-touch-icon.png"
						set testResult to do shell script "curl -s -o /dev/null -w '%{http_code}' --max-time 5 --location " & quoted form of candidateURL
						if testResult is "200" then
							set thumbnail of theRecord to candidateURL
							set didSet to true
						end if
					end try
				end if
				
				-- ── Stage 5: Google Favicon API ───────────────────────────
				if not didSet then
					try
						-- Extract bare domain (strip scheme and trailing slash)
						set theDomain to theBaseURL
						if theDomain begins with "https://" then set theDomain to (characters 9 thru -1 of theDomain) as string
						if theDomain begins with "http://" then set theDomain to (characters 8 thru -1 of theDomain) as string
						if theDomain ends with "/" then set theDomain to (characters 1 thru -2 of theDomain) as string
						
						set googleURL to "https://www.google.com/s2/favicons?domain=" & theDomain & "&sz=64"
						-- Google always returns 200 even for unknowns; check content length > 100 bytes
						set contentLength to do shell script "curl -s -o /dev/null -w '%{size_download}' --max-time 5 " & quoted form of googleURL
						if (contentLength as integer) > 100 then
							set thumbnail of theRecord to googleURL
							set didSet to true
						end if
					end try
				end if
				
				-- ── Stage 6: DuckDuckGo Favicon API ──────────────────────
				if not didSet then
					try
						if theDomain is not "" then
							set ddgURL to "https://icons.duckduckgo.com/ip3/" & theDomain & ".ico"
							set contentLength to do shell script "curl -s -o /dev/null -w '%{size_download}' --max-time 5 --location " & quoted form of ddgURL
							if (contentLength as integer) > 100 then
								set thumbnail of theRecord to ddgURL
								set didSet to true
							end if
						end if
					end try
				end if
				
				-- ── Track failures for summary ────────────────────────────
				if not didSet then
					set end of failedRecords to (name of theRecord as string)
				end if
				
			end if -- http/https check
			
		on error errMsg
			-- Record had no URL or an unhandled error; add to failed list
			try
				set end of failedRecords to (name of theRecord as string)
			end try
		end try
		
		if cancelled progress then exit repeat
	end repeat
	
	hide progress indicator
	
	-- ── Summary notification ──────────────────────────────────────────────
	set totalCount to count of theRecords
	set failCount to count of failedRecords
	set successCount to totalCount - failCount
	
	if failCount is 0 then
		display notification "✓ Favicons set for all " & successCount & " record(s)." with title "Download Favicons"
	else
		set failList to ""
		repeat with n in failedRecords
			set failList to failList & "• " & n & return
		end repeat
		display dialog "Favicons downloaded: " & successCount & " of " & totalCount & return & return & "Could not find favicons for:" & return & failList buttons {"OK"} default button "OK" with title "Download Favicons"
	end if
	
end tell

end performSmartRule

How To Install

Installation instructions
  1. Replace the existing script at: ~/Library/Application Scripts/com.devon-technologies.think4/Menu/. You can safely delete the old script, or preserve it if you want.

  2. Change the Add Favicons smart rule at `Sidebar (Smart Rules) > Add Favicons` to use the new ‘Add Favicons (Upgrade)’ script.

1 Like