Skip to content

Commit 5481a6a

Browse files
committed
Add local favicon load
1 parent e28a69c commit 5481a6a

File tree

21 files changed

+341
-56
lines changed

21 files changed

+341
-56
lines changed

Flow.Launcher.Core/Flow.Launcher.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<PackageReference Include="FSharp.Core" Version="9.0.201" />
5858
<PackageReference Include="Meziantou.Framework.Win32.Jobs" Version="3.4.0" />
5959
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
60+
<PackageReference Include="SkiaSharp" Version="3.116.1" />
6061
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
6162
<PackageReference Include="StreamJsonRpc" Version="2.21.10" />
6263
</ItemGroup>

Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
</PackageReference>
6868
<PackageReference Include="NLog" Version="4.7.10" />
6969
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
70+
<PackageReference Include="SkiaSharp" Version="3.116.1" />
7071
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
7172
<!--ToolGood.Words.Pinyin v3.0.2.6 results in high memory usage when search with pinyin is enabled-->
7273
<!--Bumping to it or higher needs to test and ensure this is no longer a problem-->

Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
7878
</PackageReference>
7979
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
80+
<PackageReference Include="SkiaSharp" Version="3.116.1" />
8081
</ItemGroup>
8182

8283
</Project>

Flow.Launcher.Test/Flow.Launcher.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
5656
</PackageReference>
5757
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
58+
<PackageReference Include="SkiaSharp" Version="3.116.1" />
5859
</ItemGroup>
5960

6061
</Project>

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
105105
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
106106
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
107+
<PackageReference Include="SkiaSharp" Version="3.116.1" />
107108
<PackageReference Include="TaskScheduler" Version="2.12.1" />
108109
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
109110
</ItemGroup>

Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromiumBookmarkLoader.cs

Lines changed: 140 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,24 @@
33
using System.IO;
44
using System.Text.Json;
55
using Flow.Launcher.Infrastructure.Logger;
6+
using System;
7+
using System.Data.SQLite;
8+
using SkiaSharp;
69

710
namespace Flow.Launcher.Plugin.BrowserBookmark;
811

912
public abstract class ChromiumBookmarkLoader : IBookmarkLoader
1013
{
14+
private readonly string _faviconCacheDir;
15+
16+
protected ChromiumBookmarkLoader()
17+
{
18+
_faviconCacheDir = Path.Combine(
19+
Path.GetDirectoryName(typeof(ChromiumBookmarkLoader).Assembly.Location),
20+
"FaviconCache");
21+
Directory.CreateDirectory(_faviconCacheDir);
22+
}
23+
1124
public abstract List<Bookmark> GetBookmarks();
1225

1326
protected List<Bookmark> LoadBookmarks(string browserDataPath, string name)
@@ -22,10 +35,30 @@ protected List<Bookmark> LoadBookmarks(string browserDataPath, string name)
2235
if (!File.Exists(bookmarkPath))
2336
continue;
2437

25-
Main.RegisterBookmarkFile(bookmarkPath);
38+
// Register bookmark file monitoring (direct call to Main.RegisterBookmarkFile)
39+
try
40+
{
41+
if (File.Exists(bookmarkPath))
42+
{
43+
//Main.RegisterBookmarkFile(bookmarkPath);
44+
}
45+
}
46+
catch (Exception ex)
47+
{
48+
Log.Exception($"Failed to register bookmark file monitoring: {bookmarkPath}", ex);
49+
}
2650

2751
var source = name + (Path.GetFileName(profile) == "Default" ? "" : $" ({Path.GetFileName(profile)})");
28-
bookmarks.AddRange(LoadBookmarksFromFile(bookmarkPath, source));
52+
var profileBookmarks = LoadBookmarksFromFile(bookmarkPath, source);
53+
54+
// Load favicons after loading bookmarks
55+
var faviconDbPath = Path.Combine(profile, "Favicons");
56+
if (File.Exists(faviconDbPath))
57+
{
58+
LoadFaviconsFromDb(faviconDbPath, profileBookmarks);
59+
}
60+
61+
bookmarks.AddRange(profileBookmarks);
2962
}
3063

3164
return bookmarks;
@@ -52,8 +85,7 @@ private void EnumerateRoot(JsonElement rootElement, ICollection<Bookmark> bookma
5285
if (folder.Value.ValueKind != JsonValueKind.Object)
5386
continue;
5487

55-
// Fix for Opera. It stores bookmarks slightly different than chrome. See PR and bug report for this change for details.
56-
// If various exceptions start to build up here consider splitting this Loader into multiple separate ones.
88+
// Fix for Opera. It stores bookmarks slightly different than chrome.
5789
if (folder.Name == "custom_root")
5890
EnumerateRoot(folder.Value, bookmarks, source);
5991
else
@@ -91,4 +123,108 @@ private void EnumerateFolderBookmark(JsonElement folderElement, ICollection<Book
91123
}
92124
}
93125
}
126+
127+
private void LoadFaviconsFromDb(string dbPath, List<Bookmark> bookmarks)
128+
{
129+
try
130+
{
131+
// Use a copy to avoid lock issues with the original file
132+
var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.db");
133+
134+
try
135+
{
136+
File.Copy(dbPath, tempDbPath, true);
137+
}
138+
catch (Exception ex)
139+
{
140+
Log.Exception($"Failed to copy favicon DB: {dbPath}", ex);
141+
return;
142+
}
143+
144+
try
145+
{
146+
using var connection = new SQLiteConnection($"Data Source={tempDbPath};Version=3;Read Only=True;");
147+
connection.Open();
148+
149+
foreach (var bookmark in bookmarks)
150+
{
151+
try
152+
{
153+
var url = bookmark.Url;
154+
if (string.IsNullOrEmpty(url)) continue;
155+
156+
// Extract domain from URL
157+
if (!Uri.TryCreate(url, UriKind.Absolute, out Uri uri))
158+
continue;
159+
160+
var domain = uri.Host;
161+
162+
using var cmd = connection.CreateCommand();
163+
cmd.CommandText = @"
164+
SELECT f.id, b.image_data
165+
FROM favicons f
166+
JOIN favicon_bitmaps b ON f.id = b.icon_id
167+
JOIN icon_mapping m ON f.id = m.icon_id
168+
WHERE m.page_url LIKE @url
169+
ORDER BY b.width DESC
170+
LIMIT 1";
171+
172+
cmd.Parameters.AddWithValue("@url", $"%{domain}%");
173+
174+
using var reader = cmd.ExecuteReader();
175+
if (reader.Read() && !reader.IsDBNull(1))
176+
{
177+
var iconId = reader.GetInt64(0).ToString();
178+
var imageData = (byte[])reader["image_data"];
179+
180+
if (imageData != null && imageData.Length > 0)
181+
{
182+
var faviconPath = Path.Combine(_faviconCacheDir, $"{domain}_{iconId}.png");
183+
if (!File.Exists(faviconPath))
184+
{
185+
SaveBitmapData(imageData, faviconPath);
186+
}
187+
bookmark.FaviconPath = faviconPath;
188+
}
189+
}
190+
}
191+
catch (Exception ex)
192+
{
193+
Log.Exception($"Failed to extract bookmark favicon: {bookmark.Url}", ex);
194+
}
195+
}
196+
}
197+
catch (Exception ex)
198+
{
199+
Log.Exception($"Failed to connect to SQLite: {tempDbPath}", ex);
200+
}
201+
202+
// Delete temporary file
203+
try { File.Delete(tempDbPath); } catch { /* Ignore */ }
204+
}
205+
catch (Exception ex)
206+
{
207+
Log.Exception($"Failed to load favicon DB: {dbPath}", ex);
208+
}
209+
}
210+
211+
private void SaveBitmapData(byte[] imageData, string outputPath)
212+
{
213+
try
214+
{
215+
using var ms = new MemoryStream(imageData);
216+
using var bitmap = SKBitmap.Decode(ms);
217+
if (bitmap != null)
218+
{
219+
using var image = SKImage.FromBitmap(bitmap);
220+
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
221+
using var fs = File.OpenWrite(outputPath);
222+
data.SaveTo(fs);
223+
}
224+
}
225+
catch (Exception ex)
226+
{
227+
Log.Exception($"Failed to save image: {outputPath}", ex);
228+
}
229+
}
94230
}

0 commit comments

Comments
 (0)