From 5481a6aa437aa782563d59c8d3b9c6d1f760730c Mon Sep 17 00:00:00 2001 From: DB p Date: Thu, 20 Mar 2025 05:04:19 +0900 Subject: [PATCH 01/19] Add local favicon load --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 + .../Flow.Launcher.Infrastructure.csproj | 1 + .../Flow.Launcher.Plugin.csproj | 1 + Flow.Launcher.Test/Flow.Launcher.Test.csproj | 1 + Flow.Launcher/Flow.Launcher.csproj | 1 + .../ChromiumBookmarkLoader.cs | 144 +++++++++++- .../FirefoxBookmarkLoader.cs | 211 +++++++++++++----- ...low.Launcher.Plugin.BrowserBookmark.csproj | 3 + .../Main.cs | 8 +- .../Models/Bookmark.cs | 1 + .../Flow.Launcher.Plugin.Calculator.csproj | 1 + .../Flow.Launcher.Plugin.Explorer.csproj | 1 + ...low.Launcher.Plugin.PluginIndicator.csproj | 4 + ...Flow.Launcher.Plugin.PluginsManager.csproj | 4 + .../Flow.Launcher.Plugin.ProcessKiller.csproj | 1 + .../Flow.Launcher.Plugin.Program.csproj | 1 + .../Flow.Launcher.Plugin.Shell.csproj | 1 + .../Flow.Launcher.Plugin.Sys.csproj | 1 + .../Flow.Launcher.Plugin.Url.csproj | 4 + .../Flow.Launcher.Plugin.WebSearch.csproj | 4 + ...low.Launcher.Plugin.WindowsSettings.csproj | 3 + 21 files changed, 341 insertions(+), 56 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index e9f199d00dd..d10a0313d96 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -57,6 +57,7 @@ + diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index b91da711480..ae94afdabe1 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -67,6 +67,7 @@ + diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 1472813b8a5..027fac7eb45 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -77,6 +77,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 0241a374e41..98f25a5f497 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -55,6 +55,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 1e305d3d9cf..0a792ef72ab 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -104,6 +104,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 48acf61090b..acf863bf06e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -3,11 +3,24 @@ using System.IO; using System.Text.Json; using Flow.Launcher.Infrastructure.Logger; +using System; +using System.Data.SQLite; +using SkiaSharp; namespace Flow.Launcher.Plugin.BrowserBookmark; public abstract class ChromiumBookmarkLoader : IBookmarkLoader { + private readonly string _faviconCacheDir; + + protected ChromiumBookmarkLoader() + { + _faviconCacheDir = Path.Combine( + Path.GetDirectoryName(typeof(ChromiumBookmarkLoader).Assembly.Location), + "FaviconCache"); + Directory.CreateDirectory(_faviconCacheDir); + } + public abstract List GetBookmarks(); protected List LoadBookmarks(string browserDataPath, string name) @@ -22,10 +35,30 @@ protected List LoadBookmarks(string browserDataPath, string name) if (!File.Exists(bookmarkPath)) continue; - Main.RegisterBookmarkFile(bookmarkPath); + // Register bookmark file monitoring (direct call to Main.RegisterBookmarkFile) + try + { + if (File.Exists(bookmarkPath)) + { + //Main.RegisterBookmarkFile(bookmarkPath); + } + } + catch (Exception ex) + { + Log.Exception($"Failed to register bookmark file monitoring: {bookmarkPath}", ex); + } var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})"); - bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source)); + var profileBookmarks = LoadBookmarksFromFile(bookmarkPath, source); + + // Load favicons after loading bookmarks + var faviconDbPath = Path.Combine(profile, "Favicons"); + if (File.Exists(faviconDbPath)) + { + LoadFaviconsFromDb(faviconDbPath, profileBookmarks); + } + + bookmarks.AddRange(profileBookmarks); } return bookmarks; @@ -52,8 +85,7 @@ private void EnumerateRoot(JsonElement rootElement, ICollection bookma if (folder.Value.ValueKind != JsonValueKind.Object) continue; - // Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details. - // If various exceptions start to build up here consider splitting this Loader into multiple separate ones. + // Fix for Opera. It stores bookmarks slightly different than chrome. if (folder.Name == "custom_root") EnumerateRoot(folder.Value, bookmarks, source); else @@ -91,4 +123,108 @@ private void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks) + { + try + { + // Use a copy to avoid lock issues with the original file + var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.db"); + + try + { + File.Copy(dbPath, tempDbPath, true); + } + catch (Exception ex) + { + Log.Exception($"Failed to copy favicon DB: {dbPath}", ex); + return; + } + + try + { + using var connection = new SQLiteConnection($"Data Source={tempDbPath};Version=3;Read Only=True;"); + connection.Open(); + + foreach (var bookmark in bookmarks) + { + try + { + var url = bookmark.Url; + if (string.IsNullOrEmpty(url)) continue; + + // Extract domain from URL + if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri)) + continue; + + var domain = uri.Host; + + using var cmd = connection.CreateCommand(); + cmd.CommandText = @" + SELECT f.id, b.image_data + FROM favicons f + JOIN favicon_bitmaps b ON f.id = b.icon_id + JOIN icon_mapping m ON f.id = m.icon_id + WHERE m.page_url LIKE @url + ORDER BY b.width DESC + LIMIT 1"; + + cmd.Parameters.AddWithValue("@url", $"%{domain}%"); + + using var reader = cmd.ExecuteReader(); + if (reader.Read() && !reader.IsDBNull(1)) + { + var iconId = reader.GetInt64(0).ToString(); + var imageData = (byte[])reader["image_data"]; + + if (imageData != null && imageData.Length > 0) + { + var faviconPath = Path.Combine(_faviconCacheDir, $"{domain}_{iconId}.png"); + if (!File.Exists(faviconPath)) + { + SaveBitmapData(imageData, faviconPath); + } + bookmark.FaviconPath = faviconPath; + } + } + } + catch (Exception ex) + { + Log.Exception($"Failed to extract bookmark favicon: {bookmark.Url}", ex); + } + } + } + catch (Exception ex) + { + Log.Exception($"Failed to connect to SQLite: {tempDbPath}", ex); + } + + // Delete temporary file + try { File.Delete(tempDbPath); } catch { /* Ignore */ } + } + catch (Exception ex) + { + Log.Exception($"Failed to load favicon DB: {dbPath}", ex); + } + } + + private void SaveBitmapData(byte[] imageData, string outputPath) + { + try + { + using var ms = new MemoryStream(imageData); + using var bitmap = SKBitmap.Decode(ms); + if (bitmap != null) + { + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + using var fs = File.OpenWrite(outputPath); + data.SaveTo(fs); + } + } + catch (Exception ex) + { + Log.Exception($"Failed to save image: {outputPath}", ex); + } + } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 35ad32fb3d4..84c3437071f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -4,13 +4,26 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Flow.Launcher.Infrastructure.Logger; +using SkiaSharp; namespace Flow.Launcher.Plugin.BrowserBookmark; public abstract class FirefoxBookmarkLoaderBase : IBookmarkLoader { + private readonly string _faviconCacheDir; + + protected FirefoxBookmarkLoaderBase() + { + _faviconCacheDir = Path.Combine( + Path.GetDirectoryName(typeof(FirefoxBookmarkLoaderBase).Assembly.Location), + "FaviconCache"); + Directory.CreateDirectory(_faviconCacheDir); + } + public abstract List GetBookmarks(); + // Updated query - removed favicon_id column private const string QueryAllBookmarks = """ SELECT moz_places.url, moz_bookmarks.title FROM moz_places @@ -20,35 +33,160 @@ INNER JOIN moz_bookmarks ON ( ORDER BY moz_places.visit_count DESC """; - private const string DbPathFormat = "Data Source ={0}"; + private const string DbPathFormat = "Data Source={0}"; - protected static List GetBookmarksFromPath(string placesPath) + protected List GetBookmarksFromPath(string placesPath) { - // Return empty list if the places.sqlite file cannot be found + // Variable to store bookmark list + var bookmarks = new List(); + + // Return empty list if places.sqlite file doesn't exist if (string.IsNullOrEmpty(placesPath) || !File.Exists(placesPath)) - return new List(); - - Main.RegisterBookmarkFile(placesPath); - - // create the connection string and init the connection - string dbPath = string.Format(DbPathFormat, placesPath); - 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(); - - // return results in List format - return reader - .Select( - x => new Bookmark( - x["title"] is DBNull ? string.Empty : x["title"].ToString(), - x["url"].ToString() + return bookmarks; + + try + { + // Try to register file monitoring + try + { + Main.RegisterBookmarkFile(placesPath); + } + catch (Exception ex) + { + Log.Exception($"Failed to register Firefox bookmark file monitoring: {placesPath}", ex); + } + + // Use a copy to avoid lock issues with the original file + var tempDbPath = Path.Combine(_faviconCacheDir, $"tempplaces_{Guid.NewGuid()}.sqlite"); + File.Copy(placesPath, tempDbPath, true); + + // Connect to database and execute query + string dbPath = string.Format(DbPathFormat, tempDbPath); + using var dbConnection = new SqliteConnection(dbPath); + dbConnection.Open(); + var reader = new SqliteCommand(QueryAllBookmarks, dbConnection).ExecuteReader(); + + // Create bookmark list + bookmarks = reader + .Select( + x => new Bookmark( + x["title"] is DBNull ? string.Empty : x["title"].ToString(), + x["url"].ToString(), + "Firefox" + ) ) - ) - .ToList(); + .ToList(); + + // Path to favicon database + var faviconDbPath = Path.Combine(Path.GetDirectoryName(placesPath), "favicons.sqlite"); + if (File.Exists(faviconDbPath)) + { + LoadFaviconsFromDb(faviconDbPath, bookmarks); + } + + // Delete temporary file + try { File.Delete(tempDbPath); } catch { /* Ignore */ } + } + catch (Exception ex) + { + Log.Exception($"Failed to load Firefox bookmarks: {placesPath}", ex); + } + + return bookmarks; } -} + private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks) + { + try + { + // Use a copy to avoid lock issues with the original file + var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); + File.Copy(faviconDbPath, tempDbPath, true); + + 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) + { + try + { + if (string.IsNullOrEmpty(bookmark.Url)) + continue; + + // Extract domain from URL + if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) + continue; + + var domain = uri.Host; + + // Query for latest Firefox version favicon structure + using var cmd = connection.CreateCommand(); + cmd.CommandText = @" + SELECT i.data + FROM moz_icons i + JOIN moz_icons_to_pages ip ON i.id = ip.icon_id + JOIN moz_pages_w_icons p ON ip.page_id = p.id + WHERE p.page_url LIKE @url + AND i.data IS NOT NULL + ORDER BY i.width DESC -- Select largest icon available + LIMIT 1"; + + cmd.Parameters.AddWithValue("@url", $"%{domain}%"); + + using var reader = cmd.ExecuteReader(); + if (reader.Read() && !reader.IsDBNull(0)) + { + var imageData = (byte[])reader["data"]; + + if (imageData != null && imageData.Length > 0) + { + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); + + if (!File.Exists(faviconPath)) + { + SaveBitmapData(imageData, faviconPath); + } + bookmark.FaviconPath = faviconPath; + } + } + } + catch (Exception ex) + { + Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex); + } + } + + // Delete temporary file + try { File.Delete(tempDbPath); } catch { /* Ignore */ } + } + catch (Exception ex) + { + Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex); + } + } + + private void SaveBitmapData(byte[] imageData, string outputPath) + { + try + { + using var ms = new MemoryStream(imageData); + using var bitmap = SKBitmap.Decode(ms); + if (bitmap != null) + { + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + using var fs = File.OpenWrite(outputPath); + data.SaveTo(fs); + } + } + catch (Exception ex) + { + Log.Exception($"Failed to save image: {outputPath}", ex); + } + } +} public class FirefoxBookmarkLoader : FirefoxBookmarkLoaderBase { @@ -77,33 +215,6 @@ private string PlacesPath using var sReader = new StreamReader(profileIni); var ini = sReader.ReadToEnd(); - /* - Current profiles.ini structure example as of Firefox version 69.0.1 - - [Install736426B0AF4A39CB] - Default=Profiles/7789f565.default-release <== this is the default profile this plugin will get the bookmarks from. When opened Firefox will load the default profile - Locked=1 - - [Profile2] - Name=newblahprofile - IsRelative=0 - Path=C:\t6h2yuq8.newblahprofile <== Note this is a custom location path for the profile user can set, we need to cater for this in code. - - [Profile1] - Name=default - IsRelative=1 - Path=Profiles/cydum7q4.default - Default=1 - - [Profile0] - Name=default-release - IsRelative=1 - Path=Profiles/7789f565.default-release - - [General] - StartWithLastProfile=1 - Version=2 - */ var lines = ini.Split("\r\n").ToList(); var defaultProfileFolderNameRaw = lines.FirstOrDefault(x => x.Contains("Default=") && x != "Default=1") ?? string.Empty; diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index b4e42fbcdc3..4ac1e68b6b7 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -96,6 +96,9 @@ + + + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index a48d70f2d46..9d9b9e50547 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -68,7 +68,9 @@ public List Query(Query query) { Title = c.Name, SubTitle = c.Url, - IcoPath = @"Images\bookmark.png", + IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) + ? c.FaviconPath + : @"Images\bookmark.png", Score = BookmarkLoader.MatchProgram(c, param).Score, Action = _ => { @@ -90,7 +92,9 @@ public List Query(Query query) { Title = c.Name, SubTitle = c.Url, - IcoPath = @"Images\bookmark.png", + IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) + ? c.FaviconPath + : @"Images\bookmark.png", Score = 5, Action = _ => { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs index c738da389c5..caab16b65e8 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Models/Bookmark.cs @@ -18,4 +18,5 @@ public virtual bool Equals(Bookmark other) } public List CustomBrowsers { get; set; } = new(); + public string FaviconPath { get; set; } = string.Empty; } diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 1b985acf9b1..0bac1040e2a 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -63,6 +63,7 @@ + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 54921702730..5b326b524dc 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -46,6 +46,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index 21d964c1126..bb23eeed8ba 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -56,5 +56,9 @@ PreserveNewest + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index b438305d627..f3f5f268b21 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -37,4 +37,8 @@ PreserveNewest + + + + diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index 4e216b7b26a..628e349c4b8 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -55,6 +55,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index 99c1a12e9b3..ff538828a1a 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -69,6 +69,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index 8f443214bba..deef219446d 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -60,6 +60,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index 266c2417008..f59d596c87b 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -64,5 +64,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index 6d338733e31..ec0d3c1caf4 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -56,4 +56,8 @@ + + + + diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 55d69d5260c..5500096cd4a 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -56,4 +56,8 @@ + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj index 73fcd9f83eb..51266575342 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj @@ -68,5 +68,8 @@ + + + From 1f6d7713cdbcc122d698502b13574d0f3d1d8d95 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 09:48:32 +0800 Subject: [PATCH 02/19] Remove useless NuGet package & Downgrade System.Drawing.Common package --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 - .../Flow.Launcher.Infrastructure.csproj | 1 - .../Flow.Launcher.Plugin.csproj | 1 - Flow.Launcher.Test/Flow.Launcher.Test.csproj | 1 - Flow.Launcher/Flow.Launcher.csproj | 1 - ...low.Launcher.Plugin.BrowserBookmark.csproj | 36 ++----------------- .../Flow.Launcher.Plugin.Calculator.csproj | 1 - .../Flow.Launcher.Plugin.Explorer.csproj | 1 - ...low.Launcher.Plugin.PluginIndicator.csproj | 4 --- ...Flow.Launcher.Plugin.PluginsManager.csproj | 4 --- .../Flow.Launcher.Plugin.ProcessKiller.csproj | 1 - .../Flow.Launcher.Plugin.Program.csproj | 1 - .../Flow.Launcher.Plugin.Shell.csproj | 1 - .../Flow.Launcher.Plugin.Sys.csproj | 1 - .../Flow.Launcher.Plugin.Url.csproj | 4 --- .../Flow.Launcher.Plugin.WebSearch.csproj | 4 --- ...low.Launcher.Plugin.WindowsSettings.csproj | 3 -- 17 files changed, 3 insertions(+), 63 deletions(-) diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index d10a0313d96..e9f199d00dd 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -57,7 +57,6 @@ - diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj index ae94afdabe1..b91da711480 100644 --- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj +++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj @@ -67,7 +67,6 @@ - diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj index 027fac7eb45..1472813b8a5 100644 --- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj +++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj @@ -77,7 +77,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Flow.Launcher.Test/Flow.Launcher.Test.csproj b/Flow.Launcher.Test/Flow.Launcher.Test.csproj index 98f25a5f497..0241a374e41 100644 --- a/Flow.Launcher.Test/Flow.Launcher.Test.csproj +++ b/Flow.Launcher.Test/Flow.Launcher.Test.csproj @@ -55,7 +55,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj index 0a792ef72ab..1e305d3d9cf 100644 --- a/Flow.Launcher/Flow.Launcher.csproj +++ b/Flow.Launcher/Flow.Launcher.csproj @@ -104,7 +104,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 4ac1e68b6b7..09026200c1a 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -37,41 +37,11 @@ - + - + @@ -98,7 +68,7 @@ - + diff --git a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj index 0bac1040e2a..1b985acf9b1 100644 --- a/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj +++ b/Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj @@ -63,7 +63,6 @@ - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj index 5b326b524dc..54921702730 100644 --- a/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj +++ b/Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj @@ -46,7 +46,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj index bb23eeed8ba..21d964c1126 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj @@ -56,9 +56,5 @@ PreserveNewest - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj index f3f5f268b21..b438305d627 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -37,8 +37,4 @@ PreserveNewest - - - - diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj index 628e349c4b8..4e216b7b26a 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj @@ -55,7 +55,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj index ff538828a1a..99c1a12e9b3 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj +++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj @@ -69,7 +69,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj index deef219446d..8f443214bba 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj +++ b/Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj @@ -60,7 +60,6 @@ - diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index f59d596c87b..266c2417008 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -64,6 +64,5 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj index ec0d3c1caf4..6d338733e31 100644 --- a/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj +++ b/Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj @@ -56,8 +56,4 @@ - - - - diff --git a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj index 5500096cd4a..55d69d5260c 100644 --- a/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj +++ b/Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj @@ -56,8 +56,4 @@ - - - - \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj index 51266575342..73fcd9f83eb 100644 --- a/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj +++ b/Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj @@ -68,8 +68,5 @@ - - - From f6740bce59a8098117d53dbd183dad0010dfebaf Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 09:53:00 +0800 Subject: [PATCH 03/19] Revert style --- ...low.Launcher.Plugin.BrowserBookmark.csproj | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 09026200c1a..16f3f475d05 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -37,11 +37,41 @@ - + - + From cab0cb8e5a2ffd55ea4839430405b8f78828e438 Mon Sep 17 00:00:00 2001 From: DB p Date: Thu, 20 Mar 2025 14:40:59 +0900 Subject: [PATCH 04/19] Add SVG favicon support --- .../ChromiumBookmarkLoader.cs | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index acf863bf06e..12b00375ab6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -161,13 +161,13 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) using var cmd = connection.CreateCommand(); cmd.CommandText = @" - SELECT f.id, b.image_data - FROM favicons f - JOIN favicon_bitmaps b ON f.id = b.icon_id - JOIN icon_mapping m ON f.id = m.icon_id - WHERE m.page_url LIKE @url - ORDER BY b.width DESC - LIMIT 1"; + SELECT f.id, b.image_data + FROM favicons f + JOIN favicon_bitmaps b ON f.id = b.icon_id + JOIN icon_mapping m ON f.id = m.icon_id + WHERE m.page_url LIKE @url + ORDER BY b.width DESC + LIMIT 1"; cmd.Parameters.AddWithValue("@url", $"%{domain}%"); @@ -212,14 +212,29 @@ private void SaveBitmapData(byte[] imageData, string outputPath) { try { - using var ms = new MemoryStream(imageData); - using var bitmap = SKBitmap.Decode(ms); - if (bitmap != null) + // SVG 파일 시그니처 확인 (SVG XML 헤더 또는 특수 마커 검사) + bool isSvg = IsSvgData(imageData); + + if (isSvg) { - using var image = SKImage.FromBitmap(bitmap); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - using var fs = File.OpenWrite(outputPath); - data.SaveTo(fs); + // SVG 데이터는 있는 그대로 저장 (.svg 확장자 사용) + string svgOutputPath = Path.ChangeExtension(outputPath, ".svg"); + File.WriteAllBytes(svgOutputPath, imageData); + // 원래 경로 대신 SVG 경로를 반환하도록 outputPath 변수를 업데이트 + File.Copy(svgOutputPath, outputPath, true); + } + else + { + // 기존 비트맵 처리 코드 + using var ms = new MemoryStream(imageData); + using var bitmap = SKBitmap.Decode(ms); + if (bitmap != null) + { + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + using var fs = File.OpenWrite(outputPath); + data.SaveTo(fs); + } } } catch (Exception ex) @@ -227,4 +242,18 @@ private void SaveBitmapData(byte[] imageData, string outputPath) Log.Exception($"Failed to save image: {outputPath}", ex); } } + + private bool IsSvgData(byte[] data) + { + if (data == null || data.Length < 5) + return false; + + // SVG 파일 시그니처 확인 + // ASCII로 시작하는 SVG XML 헤더 확인 + string header = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(data.Length, 200)).ToLower(); + + return header.Contains(" Date: Thu, 20 Mar 2025 15:58:30 +0900 Subject: [PATCH 05/19] Added SVG for firefox --- .../FirefoxBookmarkLoader.cs | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 84c3437071f..e638fa825bc 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -167,18 +167,47 @@ ORDER BY i.width DESC -- Select largest icon available } } + private bool IsSvgData(byte[] data) + { + if (data == null || data.Length < 5) + return false; + + // SVG 파일 시그니처 확인 + // ASCII로 시작하는 SVG XML 헤더 확인 + string header = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(data.Length, 200)).ToLower(); + + return header.Contains(" Date: Thu, 20 Mar 2025 13:16:46 +0600 Subject: [PATCH 06/19] Revert "Add SVG favicon support" This reverts commit cab0cb8e5a2ffd55ea4839430405b8f78828e438. --- .../ChromiumBookmarkLoader.cs | 57 +++++-------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 12b00375ab6..acf863bf06e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -161,13 +161,13 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) using var cmd = connection.CreateCommand(); cmd.CommandText = @" - SELECT f.id, b.image_data - FROM favicons f - JOIN favicon_bitmaps b ON f.id = b.icon_id - JOIN icon_mapping m ON f.id = m.icon_id - WHERE m.page_url LIKE @url - ORDER BY b.width DESC - LIMIT 1"; + SELECT f.id, b.image_data + FROM favicons f + JOIN favicon_bitmaps b ON f.id = b.icon_id + JOIN icon_mapping m ON f.id = m.icon_id + WHERE m.page_url LIKE @url + ORDER BY b.width DESC + LIMIT 1"; cmd.Parameters.AddWithValue("@url", $"%{domain}%"); @@ -212,29 +212,14 @@ private void SaveBitmapData(byte[] imageData, string outputPath) { try { - // SVG 파일 시그니처 확인 (SVG XML 헤더 또는 특수 마커 검사) - bool isSvg = IsSvgData(imageData); - - if (isSvg) + using var ms = new MemoryStream(imageData); + using var bitmap = SKBitmap.Decode(ms); + if (bitmap != null) { - // SVG 데이터는 있는 그대로 저장 (.svg 확장자 사용) - string svgOutputPath = Path.ChangeExtension(outputPath, ".svg"); - File.WriteAllBytes(svgOutputPath, imageData); - // 원래 경로 대신 SVG 경로를 반환하도록 outputPath 변수를 업데이트 - File.Copy(svgOutputPath, outputPath, true); - } - else - { - // 기존 비트맵 처리 코드 - using var ms = new MemoryStream(imageData); - using var bitmap = SKBitmap.Decode(ms); - if (bitmap != null) - { - using var image = SKImage.FromBitmap(bitmap); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - using var fs = File.OpenWrite(outputPath); - data.SaveTo(fs); - } + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + using var fs = File.OpenWrite(outputPath); + data.SaveTo(fs); } } catch (Exception ex) @@ -242,18 +227,4 @@ private void SaveBitmapData(byte[] imageData, string outputPath) Log.Exception($"Failed to save image: {outputPath}", ex); } } - - private bool IsSvgData(byte[] data) - { - if (data == null || data.Length < 5) - return false; - - // SVG 파일 시그니처 확인 - // ASCII로 시작하는 SVG XML 헤더 확인 - string header = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(data.Length, 200)).ToLower(); - - return header.Contains(" Date: Thu, 20 Mar 2025 15:35:16 +0800 Subject: [PATCH 07/19] Adjust code format --- .../Flow.Launcher.Plugin.BrowserBookmark/Main.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 9d9b9e50547..3acf94bf6ed 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -58,7 +58,6 @@ public List Query(Query query) // Should top results be returned? (true if no search parameters have been passed) var topResults = string.IsNullOrEmpty(param); - if (!topResults) { // Since we mixed chrome and firefox bookmarks, we should order them again @@ -68,9 +67,9 @@ public List Query(Query query) { Title = c.Name, SubTitle = c.Url, - IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) - ? c.FaviconPath - : @"Images\bookmark.png", + IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) + ? c.FaviconPath + : @"Images\bookmark.png", Score = BookmarkLoader.MatchProgram(c, param).Score, Action = _ => { @@ -92,9 +91,9 @@ public List Query(Query query) { Title = c.Name, SubTitle = c.Url, - IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) - ? c.FaviconPath - : @"Images\bookmark.png", + IcoPath = !string.IsNullOrEmpty(c.FaviconPath) && File.Exists(c.FaviconPath) + ? c.FaviconPath + : @"Images\bookmark.png", Score = 5, Action = _ => { @@ -108,7 +107,6 @@ public List Query(Query query) } } - private static Channel _refreshQueue = Channel.CreateBounded(1); private static SemaphoreSlim _fileMonitorSemaphore = new(1, 1); From d2c185ee9205cb0ba5f0361395fbd995ff67697b Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 20 Mar 2025 14:03:08 +0600 Subject: [PATCH 08/19] Remove unnecessary dependencies --- .../ChromiumBookmarkLoader.cs | 15 ++------- .../FirefoxBookmarkLoader.cs | 33 +++---------------- ...low.Launcher.Plugin.BrowserBookmark.csproj | 3 -- 3 files changed, 8 insertions(+), 43 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index acf863bf06e..1198444bbc4 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -4,8 +4,7 @@ using System.Text.Json; using Flow.Launcher.Infrastructure.Logger; using System; -using System.Data.SQLite; -using SkiaSharp; +using Microsoft.Data.Sqlite; namespace Flow.Launcher.Plugin.BrowserBookmark; @@ -143,7 +142,7 @@ private void LoadFaviconsFromDb(string dbPath, List bookmarks) try { - using var connection = new SQLiteConnection($"Data Source={tempDbPath};Version=3;Read Only=True;"); + using var connection = new SqliteConnection($"Data Source={tempDbPath}"); connection.Open(); foreach (var bookmark in bookmarks) @@ -212,15 +211,7 @@ private void SaveBitmapData(byte[] imageData, string outputPath) { try { - using var ms = new MemoryStream(imageData); - using var bitmap = SKBitmap.Decode(ms); - if (bitmap != null) - { - using var image = SKImage.FromBitmap(bitmap); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - using var fs = File.OpenWrite(outputPath); - data.SaveTo(fs); - } + File.WriteAllBytes(outputPath, imageData); } catch (Exception ex) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index e638fa825bc..134524b6e8e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using Flow.Launcher.Infrastructure.Logger; -using SkiaSharp; namespace Flow.Launcher.Plugin.BrowserBookmark; @@ -142,7 +141,8 @@ ORDER BY i.width DESC -- Select largest icon available if (imageData != null && imageData.Length > 0) { - var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); + var ext = IsSvgData(imageData) ? "svg" : "png"; + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.{ext}"); if (!File.Exists(faviconPath)) { @@ -171,12 +171,12 @@ private bool IsSvgData(byte[] data) { if (data == null || data.Length < 5) return false; - + // SVG 파일 시그니처 확인 // ASCII로 시작하는 SVG XML 헤더 확인 string header = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(data.Length, 200)).ToLower(); - return header.Contains(" - - - From 977a8f8c47f608ed7ba23f240610fd32e664a92c Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 16:03:41 +0800 Subject: [PATCH 09/19] Log exception when deleting failed --- .../ChromiumBookmarkLoader.cs | 9 ++++++++- .../FirefoxBookmarkLoader.cs | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index acf863bf06e..d1551c598c6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -200,7 +200,14 @@ ORDER BY b.width DESC } // Delete temporary file - try { File.Delete(tempDbPath); } catch { /* Ignore */ } + try + { + File.Delete(tempDbPath); + } + catch (Exception ex) + { + Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex); + } } catch (Exception ex) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index e638fa825bc..59fca63cec0 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -85,7 +85,14 @@ protected List GetBookmarksFromPath(string placesPath) } // Delete temporary file - try { File.Delete(tempDbPath); } catch { /* Ignore */ } + try + { + File.Delete(tempDbPath); + } + catch (Exception ex) + { + Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex); + } } catch (Exception ex) { @@ -159,7 +166,14 @@ ORDER BY i.width DESC -- Select largest icon available } // Delete temporary file - try { File.Delete(tempDbPath); } catch { /* Ignore */ } + try + { + File.Delete(tempDbPath); + } + catch (Exception ex) + { + Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex); + } } catch (Exception ex) { From a87211bfb494a26dfdd414522c622f1cd04a89c5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 16:11:22 +0800 Subject: [PATCH 10/19] Fix sqlite file lock issue & Add static --- .../ChromiumBookmarkLoader.cs | 10 ++++++---- .../FirefoxBookmarkLoader.cs | 10 +++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 83f794f5da6..7aabc4190dc 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -63,7 +63,7 @@ protected List LoadBookmarks(string browserDataPath, string name) return bookmarks; } - protected List LoadBookmarksFromFile(string path, string source) + protected static List LoadBookmarksFromFile(string path, string source) { var bookmarks = new List(); @@ -77,7 +77,7 @@ protected List LoadBookmarksFromFile(string path, string source) return bookmarks; } - private void EnumerateRoot(JsonElement rootElement, ICollection bookmarks, string source) + private static void EnumerateRoot(JsonElement rootElement, ICollection bookmarks, string source) { foreach (var folder in rootElement.EnumerateObject()) { @@ -92,7 +92,7 @@ private void EnumerateRoot(JsonElement rootElement, ICollection bookma } } - private void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks, + private static void EnumerateFolderBookmark(JsonElement folderElement, ICollection bookmarks, string source) { if (!folderElement.TryGetProperty("children", out var childrenElement)) @@ -201,6 +201,8 @@ ORDER BY b.width DESC // Delete temporary file try { + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) @@ -214,7 +216,7 @@ ORDER BY b.width DESC } } - private void SaveBitmapData(byte[] imageData, string outputPath) + private static void SaveBitmapData(byte[] imageData, string outputPath) { try { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index df2fc028c8c..187b772245e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -86,6 +86,8 @@ protected List GetBookmarksFromPath(string placesPath) // Delete temporary file try { + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) @@ -168,6 +170,8 @@ ORDER BY i.width DESC -- Select largest icon available // Delete temporary file try { + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) @@ -181,7 +185,7 @@ ORDER BY i.width DESC -- Select largest icon available } } - private bool IsSvgData(byte[] data) + private static bool IsSvgData(byte[] data) { if (data == null || data.Length < 5) return false; @@ -195,7 +199,7 @@ private bool IsSvgData(byte[] data) header.Contains("image/svg+xml"); } - private void SaveBitmapData(byte[] imageData, string outputPath) + private static void SaveBitmapData(byte[] imageData, string outputPath) { try { @@ -221,7 +225,7 @@ public override List GetBookmarks() /// /// Path to places.sqlite /// - private string PlacesPath + private static string PlacesPath { get { From 2b08ad65a0424c60b74072afe0a874a2b8a8588f Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 16:21:15 +0800 Subject: [PATCH 11/19] Only clear pool for one connection --- .../ChromiumBookmarkLoader.cs | 5 +++-- .../FirefoxBookmarkLoader.cs | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 7aabc4190dc..8fb04d2522c 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -192,6 +192,9 @@ ORDER BY b.width DESC Log.Exception($"Failed to extract bookmark favicon: {bookmark.Url}", ex); } } + + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); } catch (Exception ex) { @@ -201,8 +204,6 @@ ORDER BY b.width DESC // Delete temporary file try { - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 187b772245e..1b018921591 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -83,11 +83,12 @@ protected List GetBookmarksFromPath(string placesPath) LoadFaviconsFromDb(faviconDbPath, bookmarks); } + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(dbConnection); + // Delete temporary file try { - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) @@ -165,13 +166,14 @@ ORDER BY i.width DESC -- Select largest icon available { Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex); } + + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); } // Delete temporary file try { - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearAllPools(); File.Delete(tempDbPath); } catch (Exception ex) From 0c162b1cbe59eeca0c0bd53bc3cefd21dbc7a166 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 16:24:41 +0800 Subject: [PATCH 12/19] Fix call position --- .../FirefoxBookmarkLoader.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 1b018921591..da7780acbb5 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -166,11 +166,11 @@ ORDER BY i.width DESC -- Select largest icon available { Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex); } - - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearPool(connection); } + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); + // Delete temporary file try { From 92ef2c76c7d48f4aa3092b323ddffcb33abf7fd6 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 20 Mar 2025 14:43:38 +0600 Subject: [PATCH 13/19] Reduce nesting --- .../ChromiumBookmarkLoader.cs | 26 ++++++++--------- .../FirefoxBookmarkLoader.cs | 29 ++++++++++--------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index 8fb04d2522c..aed13c858d9 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -171,21 +171,21 @@ ORDER BY b.width DESC cmd.Parameters.AddWithValue("@url", $"%{domain}%"); using var reader = cmd.ExecuteReader(); - if (reader.Read() && !reader.IsDBNull(1)) + if (!reader.Read() || reader.IsDBNull(1)) + continue; + + var iconId = reader.GetInt64(0).ToString(); + var imageData = (byte[])reader["image_data"]; + + if (imageData is not { Length: > 0 }) + continue; + + var faviconPath = Path.Combine(_faviconCacheDir, $"{domain}_{iconId}.png"); + if (!File.Exists(faviconPath)) { - var iconId = reader.GetInt64(0).ToString(); - var imageData = (byte[])reader["image_data"]; - - if (imageData != null && imageData.Length > 0) - { - var faviconPath = Path.Combine(_faviconCacheDir, $"{domain}_{iconId}.png"); - if (!File.Exists(faviconPath)) - { - SaveBitmapData(imageData, faviconPath); - } - bookmark.FaviconPath = faviconPath; - } + SaveBitmapData(imageData, faviconPath); } + bookmark.FaviconPath = faviconPath; } catch (Exception ex) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index da7780acbb5..7851214e98f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -145,22 +145,23 @@ ORDER BY i.width DESC -- Select largest icon available cmd.Parameters.AddWithValue("@url", $"%{domain}%"); using var reader = cmd.ExecuteReader(); - if (reader.Read() && !reader.IsDBNull(0)) + if (!reader.Read() || reader.IsDBNull(0)) + continue; + + var imageData = (byte[])reader["data"]; + + if (imageData is not { Length: > 0 }) + continue; + + var ext = IsSvgData(imageData) ? "svg" : "png"; + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.{ext}"); + + if (!File.Exists(faviconPath)) { - var imageData = (byte[])reader["data"]; - - if (imageData != null && imageData.Length > 0) - { - var ext = IsSvgData(imageData) ? "svg" : "png"; - var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.{ext}"); - - if (!File.Exists(faviconPath)) - { - SaveBitmapData(imageData, faviconPath); - } - bookmark.FaviconPath = faviconPath; - } + SaveBitmapData(imageData, faviconPath); } + + bookmark.FaviconPath = faviconPath; } catch (Exception ex) { From a837e8e197d4566cd720dde6d75930372b0c041d Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 20 Mar 2025 14:47:50 +0600 Subject: [PATCH 14/19] Close bookmarks db connection before trying to delete the file --- .../ChromiumBookmarkLoader.cs | 1 + .../FirefoxBookmarkLoader.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs index aed13c858d9..d9c4e91d020 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs @@ -195,6 +195,7 @@ ORDER BY b.width DESC // https://github.com/dotnet/efcore/issues/26580 SqliteConnection.ClearPool(connection); + connection.Close(); } catch (Exception ex) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index 7851214e98f..bf076e0eaf5 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -85,6 +85,7 @@ protected List GetBookmarksFromPath(string placesPath) // https://github.com/dotnet/efcore/issues/26580 SqliteConnection.ClearPool(dbConnection); + dbConnection.Close(); // Delete temporary file try @@ -171,6 +172,7 @@ ORDER BY i.width DESC -- Select largest icon available // https://github.com/dotnet/efcore/issues/26580 SqliteConnection.ClearPool(connection); + connection.Close(); // Delete temporary file try From 443f17dcef2dfbfa79ec0764ad8482cad2ab3828 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Thu, 20 Mar 2025 16:54:14 +0800 Subject: [PATCH 15/19] Code quality --- .../Main.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 3acf94bf6ed..3ae7cfa1d49 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -17,7 +17,7 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex { private static PluginInitContext _context; - private static List _cachedBookmarks = new List(); + private static List _cachedBookmarks = new(); private static Settings _settings; @@ -107,9 +107,9 @@ public List Query(Query query) } } - private static Channel _refreshQueue = Channel.CreateBounded(1); + private static readonly Channel _refreshQueue = Channel.CreateBounded(1); - private static SemaphoreSlim _fileMonitorSemaphore = new(1, 1); + private static readonly SemaphoreSlim _fileMonitorSemaphore = new(1, 1); private static async Task MonitorRefreshQueueAsync() { @@ -143,12 +143,13 @@ internal static void RegisterBookmarkFile(string path) return; } - var watcher = new FileSystemWatcher(directory!); - watcher.Filter = Path.GetFileName(path); - - watcher.NotifyFilter = NotifyFilters.FileName | - NotifyFilters.LastWrite | - NotifyFilters.Size; + var watcher = new FileSystemWatcher(directory!) + { + Filter = Path.GetFileName(path), + NotifyFilter = NotifyFilters.FileName | + NotifyFilters.LastWrite | + NotifyFilters.Size + }; watcher.Changed += static (_, _) => { @@ -197,7 +198,7 @@ public List LoadContextMenus(Result selectedResult) { return new List() { - new Result + new() { Title = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_title"), SubTitle = _context.API.GetTranslation("flowlauncher_plugin_browserbookmark_copyurl_subtitle"), From dfc9d5b84f91bd7daae082beb93582e4f1aad262 Mon Sep 17 00:00:00 2001 From: DB p Date: Thu, 20 Mar 2025 19:11:15 +0900 Subject: [PATCH 16/19] - Add SVG convert for firefox - Add remove cache UI --- .../FirefoxBookmarkLoader.cs | 154 +++++++++++------- ...low.Launcher.Plugin.BrowserBookmark.csproj | 1 + .../Main.cs | 10 ++ .../Views/SettingsControl.xaml | 4 + .../Views/SettingsControl.xaml.cs | 81 ++++++++- 5 files changed, 186 insertions(+), 64 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs index bf076e0eaf5..76940768359 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/FirefoxBookmarkLoader.cs @@ -106,89 +106,131 @@ protected List GetBookmarksFromPath(string placesPath) } private void LoadFaviconsFromDb(string faviconDbPath, List bookmarks) +{ + try { - try - { - // Use a copy to avoid lock issues with the original file - var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); - File.Copy(faviconDbPath, tempDbPath, true); + // Use a copy to avoid lock issues with the original file + var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite"); + File.Copy(faviconDbPath, tempDbPath, true); - string dbPath = string.Format(DbPathFormat, tempDbPath); - using var connection = new SqliteConnection(dbPath); - connection.Open(); + 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) + // Get favicons based on bookmark URLs + foreach (var bookmark in bookmarks) + { + try { - try - { - if (string.IsNullOrEmpty(bookmark.Url)) - continue; + if (string.IsNullOrEmpty(bookmark.Url)) + continue; - // Extract domain from URL - if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) - continue; + // Extract domain from URL + if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri)) + continue; - var domain = uri.Host; + var domain = uri.Host; - // Query for latest Firefox version favicon structure - using var cmd = connection.CreateCommand(); - cmd.CommandText = @" - SELECT i.data - FROM moz_icons i - JOIN moz_icons_to_pages ip ON i.id = ip.icon_id - JOIN moz_pages_w_icons p ON ip.page_id = p.id - WHERE p.page_url LIKE @url - AND i.data IS NOT NULL - ORDER BY i.width DESC -- Select largest icon available - LIMIT 1"; + // Query for latest Firefox version favicon structure + using var cmd = connection.CreateCommand(); + cmd.CommandText = @" + SELECT i.data + FROM moz_icons i + JOIN moz_icons_to_pages ip ON i.id = ip.icon_id + JOIN moz_pages_w_icons p ON ip.page_id = p.id + WHERE p.page_url LIKE @url + AND i.data IS NOT NULL + ORDER BY i.width DESC -- Select largest icon available + LIMIT 1"; - cmd.Parameters.AddWithValue("@url", $"%{domain}%"); + cmd.Parameters.AddWithValue("@url", $"%{domain}%"); - using var reader = cmd.ExecuteReader(); - if (!reader.Read() || reader.IsDBNull(0)) - continue; + using var reader = cmd.ExecuteReader(); + if (!reader.Read() || reader.IsDBNull(0)) + continue; - var imageData = (byte[])reader["data"]; + var imageData = (byte[])reader["data"]; - if (imageData is not { Length: > 0 }) - continue; + if (imageData is not { Length: > 0 }) + continue; - var ext = IsSvgData(imageData) ? "svg" : "png"; - var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.{ext}"); + var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png"); - if (!File.Exists(faviconPath)) + if (!File.Exists(faviconPath)) + { + if (IsSvgData(imageData)) + { + // SVG를 PNG로 변환 + var pngData = ConvertSvgToPng(imageData); + if (pngData != null) + { + SaveBitmapData(pngData, faviconPath); + } + else + { + // 변환 실패 시 빈 문자열 설정 (기본 아이콘 사용) + bookmark.FaviconPath = string.Empty; + continue; + } + } + else { + // PNG는 그대로 저장 SaveBitmapData(imageData, faviconPath); } - - bookmark.FaviconPath = faviconPath; } - catch (Exception ex) - { - Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex); - } - } - // https://github.com/dotnet/efcore/issues/26580 - SqliteConnection.ClearPool(connection); - connection.Close(); - - // Delete temporary file - try - { - File.Delete(tempDbPath); + bookmark.FaviconPath = faviconPath; } catch (Exception ex) { - Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex); + Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex); } } + + // https://github.com/dotnet/efcore/issues/26580 + SqliteConnection.ClearPool(connection); + connection.Close(); + + // Delete temporary file + try + { + File.Delete(tempDbPath); + } catch (Exception ex) { - Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex); + Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex); } } + catch (Exception ex) + { + Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex); + } +} + +private byte[] ConvertSvgToPng(byte[] svgData) +{ + try + { + using var memoryStream = new MemoryStream(); + // 메모리에 SVG 데이터 로드 + using (var image = new ImageMagick.MagickImage(svgData)) + { + // 적절한 크기로 리사이징 (32x32가 일반적인 파비콘 크기) + image.Resize(32, 32); + // PNG 형식으로 변환하여 메모리 스트림에 저장 + image.Format = ImageMagick.MagickFormat.Png; + image.Write(memoryStream); + } + + return memoryStream.ToArray(); + } + catch (Exception ex) + { + Log.Exception("SVG to PNG conversion failed", ex); + return null; + } +} private static bool IsSvgData(byte[] data) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index b4e42fbcdc3..5b54ef0265f 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -95,6 +95,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 3acf94bf6ed..3cef07fff34 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -22,6 +22,16 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex private static Settings _settings; private static bool _initialized = false; + + public static PluginInitContext GetContext() + { + return _context; + } + + public static string GetPluginDirectory() + { + return _context?.CurrentPluginMetadata?.PluginDirectory; + } public void Init(PluginInitContext context) { diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml index 014deff03b6..f1bb242eaac 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml @@ -31,6 +31,10 @@ Margin="0,0,15,0" Click="Others_Click" Content="{DynamicResource flowlauncher_plugin_browserbookmark_others}" /> +