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 e5f3d541e..861779a3d 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 86532d275..44993aec9 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 30424f4e8..fef60eea4 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 155069495..c08a508d3 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 c08a508d3..91ade206b 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 e859976bd..b8874c94a 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -43,16 +43,20 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 75f26d322..5cbd92586 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -41,30 +41,33 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
)
.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 5cbd92586..e090d8c14 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -88,6 +88,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
}
}
+ // 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 e090d8c14..06ddba7af 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -101,7 +101,10 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
// 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 b8874c94a..16f0a0798 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -55,7 +55,10 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 06ddba7af..d387cd7b5 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -84,7 +84,10 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 16f0a0798..a20baccb5 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -132,6 +132,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
// 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 d387cd7b5..11fc688e7 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -60,7 +60,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 a20baccb5..ddb50235c 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 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
{
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 11fc688e7..5d6854aa5 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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
// 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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
{
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
{
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 ddb50235c..8a23cc8fd 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -134,7 +134,6 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
// 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 5d6854aa5..62b71da4c 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -122,7 +122,6 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
// 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 62b71da4c..cd2f40d58 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -60,8 +60,7 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 861779a3d..44118c099 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 44118c099..a133d2402 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 8a23cc8fd..cbcbf70ea 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -161,7 +161,7 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 cbcbf70ea..e102e43b6 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs
@@ -52,12 +52,12 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 @@ public abstract class ChromiumBookmarkLoader : IBookmarkLoader
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 cd2f40d58..492a76c7b 100644
--- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
+++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs
@@ -78,12 +78,12 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
.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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 @@ public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader
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 a133d2402..22830e7c8 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 44993aec9..a0041e0d6 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 fef60eea4..0767ee980 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