From 2f8e5b91b0666a366e012b56ba614caaaa6be892 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:17:38 +0800 Subject: [PATCH 01/17] Add new setting to enable favorite icons --- .../Languages/en.xaml | 2 ++ .../Models/Settings.cs | 2 ++ .../Views/SettingsControl.xaml | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index e5f3d541e11..861779a3d96 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -27,4 +27,6 @@ Browser Engine If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. + Load favorite icons (It may cost much time) + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs index 86532d27598..44993aec94d 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs @@ -8,6 +8,8 @@ public class Settings : BaseModel public string BrowserPath { get; set; } + public bool EnableFavoriteIcons { get; set; } = false; + public bool LoadChromeBookmark { get; set; } = true; public bool LoadFirefoxBookmark { get; set; } = true; public bool LoadEdgeBookmark { get; set; } = true; diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml index 30424f4e892..fef60eea4d1 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml @@ -12,6 +12,7 @@ + + \ No newline at end of file From fd8222ba8075a2412bebe2d40a443dca9378cfb5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:17:51 +0800 Subject: [PATCH 02/17] Organize usings --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 155069495bc..c08a508d33d 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Channels; +using System.Threading.Tasks; +using System.Threading; using System.Windows.Controls; using Flow.Launcher.Plugin.BrowserBookmark.Commands; using Flow.Launcher.Plugin.BrowserBookmark.Models; using Flow.Launcher.Plugin.BrowserBookmark.Views; -using System.IO; -using System.Threading.Channels; -using System.Threading.Tasks; -using System.Threading; using Flow.Launcher.Plugin.SharedCommands; namespace Flow.Launcher.Plugin.BrowserBookmark; From cef3e8e4bb8ecdd0da22f91178382421d298ee2f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:18:01 +0800 Subject: [PATCH 03/17] Make settings internal --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index c08a508d33d..91ade206b67 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -21,9 +21,9 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex internal static PluginInitContext _context; - private static List _cachedBookmarks = new(); + internal static Settings _settings; - private static Settings _settings; + private static List _cachedBookmarks = new(); private static bool _initialized = false; From f982d80ffe66bb6ab3b0e03fda14896173f204e6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:21:57 +0800 Subject: [PATCH 04/17] Support enable favorite icons for chromiun bookmarks --- .../ChromiumBookmarkLoader.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index e859976bdc2..b8874c94add 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -43,16 +43,20 @@ protected List LoadBookmarks(string browserDataPath, string name) catch (Exception ex) { Main._context.API.LogException(ClassName, $"Failed to register bookmark file monitoring: {bookmarkPath}", ex); + continue; } var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})"); var profileBookmarks = LoadBookmarksFromFile(bookmarkPath, source); // Load favicons after loading bookmarks - var faviconDbPath = Path.Combine(profile, "Favicons"); - if (File.Exists(faviconDbPath)) + if (Main._settings.EnableFavoriteIcons) { - LoadFaviconsFromDb(faviconDbPath, profileBookmarks); + var faviconDbPath = Path.Combine(profile, "Favicons"); + if (File.Exists(faviconDbPath)) + { + LoadFaviconsFromDb(faviconDbPath, profileBookmarks); + } } bookmarks.AddRange(profileBookmarks); From b68b299cd49271013e5f31f3bed4203076da9437 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:26:32 +0800 Subject: [PATCH 05/17] Support enable favorite icons for firefox bookmarks --- .../FirefoxBookmarkLoader.cs | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 75f26d32252..5cbd9258611 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -41,30 +41,33 @@ protected List GetBookmarksFromPath(string placesPath) if (string.IsNullOrEmpty(placesPath) || !File.Exists(placesPath)) return bookmarks; + // Try to register file monitoring + try + { + Main.RegisterBookmarkFile(placesPath); + } + catch (Exception ex) + { + Main._context.API.LogException(ClassName, $"Failed to register Firefox bookmark file monitoring: {placesPath}", ex); + return bookmarks; + } + var tempDbPath = Path.Combine(_faviconCacheDir, $"tempplaces_{Guid.NewGuid()}.sqlite"); try { - // Try to register file monitoring - try - { - Main.RegisterBookmarkFile(placesPath); - } - catch (Exception ex) - { - Main._context.API.LogException(ClassName, $"Failed to register Firefox bookmark file monitoring: {placesPath}", ex); - } - // Use a copy to avoid lock issues with the original file File.Copy(placesPath, tempDbPath, true); - // Connect to database and execute query + // Create the connection string and init the connection string dbPath = string.Format(DbPathFormat, tempDbPath); using var dbConnection = new SqliteConnection(dbPath); + + // Open connection to the database file and execute the query dbConnection.Open(); var reader = new SqliteCommand(QueryAllBookmarks, dbConnection).ExecuteReader(); - // Create bookmark list + // Get results in List format bookmarks = reader .Select( x => new Bookmark( @@ -75,12 +78,16 @@ protected List GetBookmarksFromPath(string placesPath) ) .ToList(); - // Path to favicon database - var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); - if (File.Exists(faviconDbPath)) + // Load favicons after loading bookmarks + if (Main._settings.EnableFavoriteIcons) { - LoadFaviconsFromDb(faviconDbPath, bookmarks); + var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); + if (File.Exists(faviconDbPath)) + { + LoadFaviconsFromDb(faviconDbPath, bookmarks); + } } + // https://github.com/dotnet/efcore/issues/26580 SqliteConnection.ClearPool(dbConnection); dbConnection.Close(); From 6d5b95c6168ffb07fd2ffa08686eaef368ecd189 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:31:10 +0800 Subject: [PATCH 06/17] Improve code comments --- .../FirefoxBookmarkLoader.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 5cbd9258611..e090d8c1456 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -88,6 +88,7 @@ protected List GetBookmarksFromPath(string placesPath) } } + // Close the connection so that we can delete the temporary file // https://github.com/dotnet/efcore/issues/26580 SqliteConnection.ClearPool(dbConnection); dbConnection.Close(); From a761ec880bd8de87c36771e7a2fab72f4b591bcf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Wed, 4 Jun 2025 23:31:18 +0800 Subject: [PATCH 07/17] Check file exists --- .../FirefoxBookmarkLoader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index e090d8c1456..06ddba7af8e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -101,7 +101,10 @@ protected List GetBookmarksFromPath(string placesPath) // Delete temporary file try { - File.Delete(tempDbPath); + if (File.Exists(tempDbPath)) + { + File.Delete(tempDbPath); + } } catch (Exception ex) { From 72bd4c202a194667308d3d6adb778bf9b42640b6 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 00:27:03 +0800 Subject: [PATCH 08/17] Info favorite icons cost --- .../ChromiumBookmarkLoader.cs | 5 ++++- .../FirefoxBookmarkLoader.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index b8874c94add..16f0a0798a1 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -55,7 +55,10 @@ protected List LoadBookmarks(string browserDataPath, string name) var faviconDbPath = Path.Combine(profile, "Favicons"); if (File.Exists(faviconDbPath)) { - LoadFaviconsFromDb(faviconDbPath, profileBookmarks); + Main._context.API.StopwatchLogInfo(ClassName, $"Load {profileBookmarks.Count} favorite icons cost", () => + { + LoadFaviconsFromDb(faviconDbPath, profileBookmarks); + }); } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 06ddba7af8e..d387cd7b561 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -84,7 +84,10 @@ protected List GetBookmarksFromPath(string placesPath) var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); if (File.Exists(faviconDbPath)) { - LoadFaviconsFromDb(faviconDbPath, bookmarks); + Main._context.API.StopwatchLogInfo(ClassName, $"Load {bookmarks.Count} favorite icons cost", () => + { + LoadFaviconsFromDb(faviconDbPath, bookmarks); + }); } } From 0ec38faf1deb008ece5ae41868bf5347488381c5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 01:12:12 +0800 Subject: [PATCH 09/17] Add code comments & Improve code quality --- .../ChromiumBookmarkLoader.cs | 1 + .../FirefoxBookmarkLoader.cs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 16f0a0798a1..a20baccb50d 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -132,6 +132,7 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) // Use a copy to avoid lock issues with the original file var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.db"); + // Use a copy to avoid lock issues with the original file try { File.Copy(dbPath, tempDbPath, true); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index d387cd7b561..11fc688e73c 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -60,7 +60,7 @@ protected List GetBookmarksFromPath(string placesPath) File.Copy(placesPath, tempDbPath, true); // Create the connection string and init the connection - string dbPath = string.Format(DbPathFormat, tempDbPath); + var dbPath = string.Format($"Data Source={tempDbPath};Mode=ReadOnly"); using var dbConnection = new SqliteConnection(dbPath); // Open connection to the database file and execute the query @@ -119,8 +119,10 @@ protected List GetBookmarksFromPath(string placesPath) private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks) { + // Use a copy to avoid lock issues with the original file var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); + // Use a copy to avoid lock issues with the original file try { // Use a copy to avoid lock issues with the original file From d6f40ec488c4fd1134e3510cee52623b741fad04 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 01:12:46 +0800 Subject: [PATCH 10/17] Use concurrent way to load favorite icons for chromium --- .../ChromiumBookmarkLoader.cs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index a20baccb50d..ddb50235cc0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Text.Json; -using System; +using System.Threading.Tasks; using Flow.Launcher.Plugin.BrowserBookmark.Models; using Microsoft.Data.Sqlite; @@ -156,19 +158,24 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) try { - using var connection = new SqliteConnection($"Data Source={tempDbPath}"); - connection.Open(); + // Since some bookmarks may have same favorite icon id, we need to record them to avoid duplicates + var savedPaths = new ConcurrentDictionary(); - foreach (var bookmark in bookmarks) + // Get favicons based on bookmarks concurrently + Parallel.ForEach(bookmarks.ToArray(), bookmark => { + // Use read-only connection to avoid locking issues + var connection = new SqliteConnection($"Data Source={tempDbPath};Mode=ReadOnly"); + connection.Open(); + try { var url = bookmark.Url; - if (string.IsNullOrEmpty(url)) continue; + if (string.IsNullOrEmpty(url)) return; // Extract domain from URL if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) - continue; + return; var domain = uri.Host; @@ -186,16 +193,21 @@ ORDER BY b.width DESC using var reader = cmd.ExecuteReader(); if (!reader.Read() || reader.IsDBNull(1)) - continue; + return; var iconId = reader.GetInt64(0).ToString(); var imageData = (byte[])reader["image_data"]; if (imageData is not { Length: > 0 }) - continue; + return; var faviconPath = Path.Combine(_faviconCacheDir, $"chromium_{domain}_{iconId}.png"); - SaveBitmapData(imageData, faviconPath); + + // Filter out duplicate favorite icons + if (savedPaths.TryAdd(faviconPath, true)) + { + SaveBitmapData(imageData, faviconPath); + } bookmark.FaviconPath = faviconPath; } @@ -203,11 +215,14 @@ ORDER BY b.width DESC { Main._context.API.LogException(ClassName, $"Failed to extract bookmark favicon: {bookmark.Url}", ex); } - } - - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearPool(connection); - connection.Close(); + finally + { + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); + connection.Close(); + connection.Dispose(); + } + }); } catch (Exception ex) { From 8bb2b50a4c11ebe71211e908c423c47f2185c1bc Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 01:13:48 +0800 Subject: [PATCH 11/17] Use concurrent way to load favorite icons for firefox --- .../FirefoxBookmarkLoader.cs | 79 ++++++++++++------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 11fc688e73c..5d6854aa575 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using Flow.Launcher.Plugin.BrowserBookmark.Models; using Microsoft.Data.Sqlite; @@ -30,8 +32,6 @@ INNER JOIN moz_bookmarks ON ( ORDER BY moz_places.visit_count DESC """; - private const string DbPathFormat = "Data Source={0}"; - protected List GetBookmarksFromPath(string placesPath) { // Variable to store bookmark list @@ -117,7 +117,7 @@ protected List GetBookmarksFromPath(string placesPath) return bookmarks; } - private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks) + private void LoadFaviconsFromDb(string dbPath, List bookmarks) { // Use a copy to avoid lock issues with the original file var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); @@ -125,28 +125,45 @@ private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks) // Use a copy to avoid lock issues with the original file try { - // Use a copy to avoid lock issues with the original file - File.Copy(faviconDbPath, tempDbPath, true); - - var defaultIconPath = Path.Combine( - Path.GetDirectoryName(typeof(FirefoxBookmarkLoaderBase).Assembly.Location), - "bookmark.png"); - - string dbPath = string.Format(DbPathFormat, tempDbPath); - using var connection = new SqliteConnection(dbPath); - connection.Open(); - - // Get favicons based on bookmark URLs - foreach (var bookmark in bookmarks) + File.Copy(dbPath, tempDbPath, true); + } + catch (Exception ex) + { + try { + if (File.Exists(tempDbPath)) + { + File.Delete(tempDbPath); + } + } + catch (Exception ex1) + { + Main._context.API.LogException(ClassName, $"Failed to delete temporary favicon DB: {tempDbPath}", ex1); + } + Main._context.API.LogException(ClassName, $"Failed to copy favicon DB: {dbPath}", ex); + return; + } + + try + { + // Since some bookmarks may have same favorite icon id, we need to record them to avoid duplicates + var savedPaths = new ConcurrentDictionary(); + + // Get favicons based on bookmarks concurrently + Parallel.ForEach(bookmarks, bookmark => + { + // Use read-only connection to avoid locking issues + var connection = new SqliteConnection($"Data Source={tempDbPath};Mode=ReadOnly"); + connection.Open(); + try { if (string.IsNullOrEmpty(bookmark.Url)) - continue; + return; // Extract domain from URL if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) - continue; + return; var domain = uri.Host; @@ -166,12 +183,12 @@ ORDER BY i.width DESC -- Select largest icon available using var reader = cmd.ExecuteReader(); if (!reader.Read() || reader.IsDBNull(0)) - continue; + return; var imageData = (byte[])reader["data"]; if (imageData is not { Length: > 0 }) - continue; + return; string faviconPath; if (IsSvgData(imageData)) @@ -182,7 +199,12 @@ ORDER BY i.width DESC -- Select largest icon available { faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); } - SaveBitmapData(imageData, faviconPath); + + // Filter out duplicate favorite icons + if (savedPaths.TryAdd(faviconPath, true)) + { + SaveBitmapData(imageData, faviconPath); + } bookmark.FaviconPath = faviconPath; } @@ -190,15 +212,18 @@ ORDER BY i.width DESC -- Select largest icon available { Main._context.API.LogException(ClassName, $"Failed to extract Firefox favicon: {bookmark.Url}", ex); } - } - - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearPool(connection); - connection.Close(); + finally + { + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); + connection.Close(); + connection.Dispose(); + } + }); } catch (Exception ex) { - Main._context.API.LogException(ClassName, $"Failed to load Firefox favicon DB: {faviconDbPath}", ex); + Main._context.API.LogException(ClassName, $"Failed to load Firefox favicon DB: {tempDbPath}", ex); } // Delete temporary file From f717376a1745db848a5f3427ca67dc53fb372dab Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 01:30:26 +0800 Subject: [PATCH 12/17] Remove dulplicated comments --- .../ChromiumBookmarkLoader.cs | 1 - .../FirefoxBookmarkLoader.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index ddb50235cc0..8a23cc8fd16 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -134,7 +134,6 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) // Use a copy to avoid lock issues with the original file var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.db"); - // Use a copy to avoid lock issues with the original file try { File.Copy(dbPath, tempDbPath, true); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 5d6854aa575..62b71da4cf9 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -122,7 +122,6 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) // Use a copy to avoid lock issues with the original file var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); - // Use a copy to avoid lock issues with the original file try { File.Copy(dbPath, tempDbPath, true); From 49a42dd1b45d107b14fbdec7a4f36bf2e9db35d1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 01:31:06 +0800 Subject: [PATCH 13/17] Improve code quality --- .../FirefoxBookmarkLoader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 62b71da4cf9..cd2f40d58d6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -60,8 +60,7 @@ protected List GetBookmarksFromPath(string placesPath) File.Copy(placesPath, tempDbPath, true); // Create the connection string and init the connection - var dbPath = string.Format($"Data Source={tempDbPath};Mode=ReadOnly"); - using var dbConnection = new SqliteConnection(dbPath); + using var dbConnection = new SqliteConnection($"Data Source={tempDbPath};Mode=ReadOnly"); // Open connection to the database file and execute the query dbConnection.Open(); From 76dc35433bfed913d460bfdfc92f83bf71d9d393 Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 5 Jun 2025 12:12:55 +1000 Subject: [PATCH 14/17] update wording --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index 861779a3d96..44118c0993e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -27,6 +27,6 @@ Browser Engine If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. - Load favorite icons (It may cost much time) + Load favorite icons (can be time consuming during startup) \ No newline at end of file From d87a7ce32beed41f45bb765a87965304f2df65ed Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Thu, 5 Jun 2025 12:15:47 +1000 Subject: [PATCH 15/17] use favicons instead of favourite icons --- Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index 44118c0993e..a133d240261 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -27,6 +27,6 @@ Browser Engine If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. - Load favorite icons (can be time consuming during startup) + Load favicons (can be time consuming during startup) \ No newline at end of file From 69dd0383115e52b2ffacc275756b4a353f47b1d2 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 10:16:02 +0800 Subject: [PATCH 16/17] No need to convert to array --- .../ChromiumBookmarkLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 8a23cc8fd16..cbcbf70eaf4 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -161,7 +161,7 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) var savedPaths = new ConcurrentDictionary(); // Get favicons based on bookmarks concurrently - Parallel.ForEach(bookmarks.ToArray(), bookmark => + Parallel.ForEach(bookmarks, bookmark => { // Use read-only connection to avoid locking issues var connection = new SqliteConnection($"Data Source={tempDbPath};Mode=ReadOnly"); From 94b8a66db73f547f660580d77471f3cb5854d1c9 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 5 Jun 2025 10:21:24 +0800 Subject: [PATCH 17/17] Rename to favicon --- .../ChromiumBookmarkLoader.cs | 8 ++++---- .../FirefoxBookmarkLoader.cs | 8 ++++---- .../Languages/en.xaml | 2 +- .../Models/Settings.cs | 2 +- .../Views/SettingsControl.xaml | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index cbcbf70eaf4..e102e43b67b 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -52,12 +52,12 @@ protected List LoadBookmarks(string browserDataPath, string name) var profileBookmarks = LoadBookmarksFromFile(bookmarkPath, source); // Load favicons after loading bookmarks - if (Main._settings.EnableFavoriteIcons) + if (Main._settings.EnableFavicons) { var faviconDbPath = Path.Combine(profile, "Favicons"); if (File.Exists(faviconDbPath)) { - Main._context.API.StopwatchLogInfo(ClassName, $"Load {profileBookmarks.Count} favorite icons cost", () => + Main._context.API.StopwatchLogInfo(ClassName, $"Load {profileBookmarks.Count} favicons cost", () => { LoadFaviconsFromDb(faviconDbPath, profileBookmarks); }); @@ -157,7 +157,7 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) try { - // Since some bookmarks may have same favorite icon id, we need to record them to avoid duplicates + // Since some bookmarks may have same favicon id, we need to record them to avoid duplicates var savedPaths = new ConcurrentDictionary(); // Get favicons based on bookmarks concurrently @@ -202,7 +202,7 @@ ORDER BY b.width DESC var faviconPath = Path.Combine(_faviconCacheDir, $"chromium_{domain}_{iconId}.png"); - // Filter out duplicate favorite icons + // Filter out duplicate favicons if (savedPaths.TryAdd(faviconPath, true)) { SaveBitmapData(imageData, faviconPath); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index cd2f40d58d6..492a76c7bc0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -78,12 +78,12 @@ protected List GetBookmarksFromPath(string placesPath) .ToList(); // Load favicons after loading bookmarks - if (Main._settings.EnableFavoriteIcons) + if (Main._settings.EnableFavicons) { var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); if (File.Exists(faviconDbPath)) { - Main._context.API.StopwatchLogInfo(ClassName, $"Load {bookmarks.Count} favorite icons cost", () => + Main._context.API.StopwatchLogInfo(ClassName, $"Load {bookmarks.Count} favicons cost", () => { LoadFaviconsFromDb(faviconDbPath, bookmarks); }); @@ -144,7 +144,7 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) try { - // Since some bookmarks may have same favorite icon id, we need to record them to avoid duplicates + // Since some bookmarks may have same favicon id, we need to record them to avoid duplicates var savedPaths = new ConcurrentDictionary(); // Get favicons based on bookmarks concurrently @@ -198,7 +198,7 @@ ORDER BY i.width DESC -- Select largest icon available faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); } - // Filter out duplicate favorite icons + // Filter out duplicate favicons if (savedPaths.TryAdd(faviconPath, true)) { SaveBitmapData(imageData, faviconPath); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index a133d240261..22830e7c880 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -27,6 +27,6 @@ Browser Engine If you are not using Chrome, Firefox or Edge, or you are using their portable version, you need to add bookmarks data directory and select correct browser engine to make this plugin work. For example: Brave's engine is Chromium; and its default bookmarks data location is: "%LOCALAPPDATA%\BraveSoftware\Brave-Browser\UserData". For Firefox engine, the bookmarks directory is the userdata folder contains the places.sqlite file. - Load favicons (can be time consuming during startup) + Load favicons (can be time consuming during startup) \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs index 44993aec94d..a0041e0d6a0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Settings.cs @@ -8,7 +8,7 @@ public class Settings : BaseModel public string BrowserPath { get; set; } - public bool EnableFavoriteIcons { get; set; } = false; + public bool EnableFavicons { get; set; } = false; public bool LoadChromeBookmark { get; set; } = true; public bool LoadFirefoxBookmark { get; set; } = true; diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml index fef60eea4d1..0767ee98069 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml @@ -97,7 +97,7 @@ Margin="{StaticResource SettingPanelItemTopBottomMargin}" HorizontalAlignment="Left" VerticalAlignment="Center" - Content="{DynamicResource flowlauncher_plugin_browserbookmark_enable_favorite_icons}" - IsChecked="{Binding Settings.EnableFavoriteIcons}" /> + Content="{DynamicResource flowlauncher_plugin_browserbookmark_enable_favicons}" + IsChecked="{Binding Settings.EnableFavicons}" /> \ No newline at end of file