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] 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