Skip to content

Commit dfc9d5b

Browse files
committed
- Add SVG convert for firefox
- Add remove cache UI
1 parent a837e8e commit dfc9d5b

File tree

5 files changed

+186
-64
lines changed

5 files changed

+186
-64
lines changed

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

Lines changed: 98 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -106,89 +106,131 @@ protected List<Bookmark> GetBookmarksFromPath(string placesPath)
106106
}
107107

108108
private void LoadFaviconsFromDb(string faviconDbPath, List<Bookmark> bookmarks)
109+
{
110+
try
109111
{
110-
try
111-
{
112-
// Use a copy to avoid lock issues with the original file
113-
var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite");
114-
File.Copy(faviconDbPath, tempDbPath, true);
112+
// Use a copy to avoid lock issues with the original file
113+
var tempDbPath = Path.Combine(_faviconCacheDir, $"tempfavicons_{Guid.NewGuid()}.sqlite");
114+
File.Copy(faviconDbPath, tempDbPath, true);
115115

116-
string dbPath = string.Format(DbPathFormat, tempDbPath);
117-
using var connection = new SqliteConnection(dbPath);
118-
connection.Open();
116+
string dbPath = string.Format(DbPathFormat, tempDbPath);
117+
using var connection = new SqliteConnection(dbPath);
118+
connection.Open();
119119

120-
// Get favicons based on bookmark URLs
121-
foreach (var bookmark in bookmarks)
120+
// Get favicons based on bookmark URLs
121+
foreach (var bookmark in bookmarks)
122+
{
123+
try
122124
{
123-
try
124-
{
125-
if (string.IsNullOrEmpty(bookmark.Url))
126-
continue;
125+
if (string.IsNullOrEmpty(bookmark.Url))
126+
continue;
127127

128-
// Extract domain from URL
129-
if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri))
130-
continue;
128+
// Extract domain from URL
129+
if (!Uri.TryCreate(bookmark.Url, UriKind.Absolute, out Uri uri))
130+
continue;
131131

132-
var domain = uri.Host;
132+
var domain = uri.Host;
133133

134-
// Query for latest Firefox version favicon structure
135-
using var cmd = connection.CreateCommand();
136-
cmd.CommandText = @"
137-
SELECT i.data
138-
FROM moz_icons i
139-
JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
140-
JOIN moz_pages_w_icons p ON ip.page_id = p.id
141-
WHERE p.page_url LIKE @url
142-
AND i.data IS NOT NULL
143-
ORDER BY i.width DESC -- Select largest icon available
144-
LIMIT 1";
134+
// Query for latest Firefox version favicon structure
135+
using var cmd = connection.CreateCommand();
136+
cmd.CommandText = @"
137+
SELECT i.data
138+
FROM moz_icons i
139+
JOIN moz_icons_to_pages ip ON i.id = ip.icon_id
140+
JOIN moz_pages_w_icons p ON ip.page_id = p.id
141+
WHERE p.page_url LIKE @url
142+
AND i.data IS NOT NULL
143+
ORDER BY i.width DESC -- Select largest icon available
144+
LIMIT 1";
145145

146-
cmd.Parameters.AddWithValue("@url", $"%{domain}%");
146+
cmd.Parameters.AddWithValue("@url", $"%{domain}%");
147147

148-
using var reader = cmd.ExecuteReader();
149-
if (!reader.Read() || reader.IsDBNull(0))
150-
continue;
148+
using var reader = cmd.ExecuteReader();
149+
if (!reader.Read() || reader.IsDBNull(0))
150+
continue;
151151

152-
var imageData = (byte[])reader["data"];
152+
var imageData = (byte[])reader["data"];
153153

154-
if (imageData is not { Length: > 0 })
155-
continue;
154+
if (imageData is not { Length: > 0 })
155+
continue;
156156

157-
var ext = IsSvgData(imageData) ? "svg" : "png";
158-
var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.{ext}");
157+
var faviconPath = Path.Combine(_faviconCacheDir, $"firefox_{domain}.png");
159158

160-
if (!File.Exists(faviconPath))
159+
if (!File.Exists(faviconPath))
160+
{
161+
if (IsSvgData(imageData))
162+
{
163+
// SVG를 PNG로 변환
164+
var pngData = ConvertSvgToPng(imageData);
165+
if (pngData != null)
166+
{
167+
SaveBitmapData(pngData, faviconPath);
168+
}
169+
else
170+
{
171+
// 변환 실패 시 빈 문자열 설정 (기본 아이콘 사용)
172+
bookmark.FaviconPath = string.Empty;
173+
continue;
174+
}
175+
}
176+
else
161177
{
178+
// PNG는 그대로 저장
162179
SaveBitmapData(imageData, faviconPath);
163180
}
164-
165-
bookmark.FaviconPath = faviconPath;
166181
}
167-
catch (Exception ex)
168-
{
169-
Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex);
170-
}
171-
}
172182

173-
// https://github.com/dotnet/efcore/issues/26580
174-
SqliteConnection.ClearPool(connection);
175-
connection.Close();
176-
177-
// Delete temporary file
178-
try
179-
{
180-
File.Delete(tempDbPath);
183+
bookmark.FaviconPath = faviconPath;
181184
}
182185
catch (Exception ex)
183186
{
184-
Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex);
187+
Log.Exception($"Failed to extract Firefox favicon: {bookmark.Url}", ex);
185188
}
186189
}
190+
191+
// https://github.com/dotnet/efcore/issues/26580
192+
SqliteConnection.ClearPool(connection);
193+
connection.Close();
194+
195+
// Delete temporary file
196+
try
197+
{
198+
File.Delete(tempDbPath);
199+
}
187200
catch (Exception ex)
188201
{
189-
Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex);
202+
Log.Exception($"Failed to delete temporary favicon DB: {tempDbPath}", ex);
190203
}
191204
}
205+
catch (Exception ex)
206+
{
207+
Log.Exception($"Failed to load Firefox favicon DB: {faviconDbPath}", ex);
208+
}
209+
}
210+
211+
private byte[] ConvertSvgToPng(byte[] svgData)
212+
{
213+
try
214+
{
215+
using var memoryStream = new MemoryStream();
216+
// 메모리에 SVG 데이터 로드
217+
using (var image = new ImageMagick.MagickImage(svgData))
218+
{
219+
// 적절한 크기로 리사이징 (32x32가 일반적인 파비콘 크기)
220+
image.Resize(32, 32);
221+
// PNG 형식으로 변환하여 메모리 스트림에 저장
222+
image.Format = ImageMagick.MagickFormat.Png;
223+
image.Write(memoryStream);
224+
}
225+
226+
return memoryStream.ToArray();
227+
}
228+
catch (Exception ex)
229+
{
230+
Log.Exception("SVG to PNG conversion failed", ex);
231+
return null;
232+
}
233+
}
192234

193235
private static bool IsSvgData(byte[] data)
194236
{

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
</ItemGroup>
9696

9797
<ItemGroup>
98+
<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.5.0" />
9899
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.3" />
99100
</ItemGroup>
100101

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex
2222
private static Settings _settings;
2323

2424
private static bool _initialized = false;
25+
26+
public static PluginInitContext GetContext()
27+
{
28+
return _context;
29+
}
30+
31+
public static string GetPluginDirectory()
32+
{
33+
return _context?.CurrentPluginMetadata?.PluginDirectory;
34+
}
2535

2636
public void Init(PluginInitContext context)
2737
{

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
Margin="0,0,15,0"
3232
Click="Others_Click"
3333
Content="{DynamicResource flowlauncher_plugin_browserbookmark_others}" />
34+
<Button
35+
Margin="0,0,15,0"
36+
Click="RemoveFaviconCache_Click"
37+
Content="Remove Favicon Cache" />
3438
</StackPanel>
3539
<StackPanel Name="CustomBrowsersList" Visibility="Collapsed">
3640
<ListView

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/SettingsControl.xaml.cs

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,27 @@
33
using System.Windows.Input;
44
using System.ComponentModel;
55
using System.Threading.Tasks;
6+
using System;
7+
using System.IO;
8+
using System.Windows;
9+
using System.Windows.Input;
10+
using System.ComponentModel;
11+
using System.Threading.Tasks;
12+
using Flow.Launcher.Plugin.BrowserBookmark.Models;
613

714
namespace Flow.Launcher.Plugin.BrowserBookmark.Views;
815

916
public partial class SettingsControl : INotifyPropertyChanged
1017
{
1118
public Settings Settings { get; }
12-
19+
private readonly PluginInitContext _context;
20+
public SettingsControl(Settings settings)
21+
{
22+
// Settings 객체를 직접 받도록 수정
23+
Settings = settings;
24+
InitializeComponent();
25+
DataContext = this;
26+
}
1327
public CustomBrowser SelectedCustomBrowser { get; set; }
1428

1529
public bool LoadChromeBookmark
@@ -51,13 +65,6 @@ public bool OpenInNewBrowserWindow
5165
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OpenInNewBrowserWindow)));
5266
}
5367
}
54-
55-
public SettingsControl(Settings settings)
56-
{
57-
Settings = settings;
58-
InitializeComponent();
59-
}
60-
6168
public event PropertyChangedEventHandler PropertyChanged;
6269

6370
private void NewCustomBrowser(object sender, RoutedEventArgs e)
@@ -116,4 +123,62 @@ private void EditSelectedCustomBrowser()
116123
_ = Task.Run(() => Main.ReloadAllBookmarks());
117124
}
118125
}
126+
private void RemoveFaviconCache_Click(object sender, RoutedEventArgs e)
127+
{
128+
try
129+
{
130+
// 플러그인 디렉토리 경로 가져오기
131+
var pluginDir = Main.GetPluginDirectory();
132+
if (string.IsNullOrEmpty(pluginDir))
133+
{
134+
MessageBox.Show("플러그인 디렉토리를 찾을 수 없습니다.", "오류", MessageBoxButton.OK, MessageBoxImage.Error);
135+
return;
136+
}
137+
138+
// 파비콘 캐시 디렉토리 경로 지정
139+
string cacheDir = Path.Combine(pluginDir, "Images", "Favicons");
140+
141+
// 디렉토리 존재 확인 및 생성
142+
if (!Directory.Exists(cacheDir))
143+
{
144+
MessageBox.Show("파비콘 캐시 디렉토리가 존재하지 않습니다. 캐시가 비어있거나 아직 생성되지 않았을 수 있습니다.", "알림", MessageBoxButton.OK, MessageBoxImage.Information);
145+
return;
146+
}
147+
148+
// 파일 수 확인
149+
var files = Directory.GetFiles(cacheDir);
150+
if (files.Length == 0)
151+
{
152+
MessageBox.Show("파비콘 캐시가 이미 비어 있습니다.", "알림", MessageBoxButton.OK, MessageBoxImage.Information);
153+
return;
154+
}
155+
156+
// 디렉토리 내 모든 파일 삭제
157+
int deletedCount = 0;
158+
foreach (var file in files)
159+
{
160+
try
161+
{
162+
File.Delete(file);
163+
deletedCount++;
164+
}
165+
catch (Exception ex)
166+
{
167+
Flow.Launcher.Infrastructure.Logger.Log.Exception(
168+
$"Failed to delete favicon cache file: {file}", ex);
169+
}
170+
}
171+
172+
// 북마크 다시 로드
173+
Main.ReloadAllBookmarks();
174+
175+
// 완료 메시지 표시
176+
MessageBox.Show($"{deletedCount}개의 파비콘 캐시 파일이 삭제되었습니다.", "캐시 삭제 완료", MessageBoxButton.OK, MessageBoxImage.Information);
177+
}
178+
catch (Exception ex)
179+
{
180+
Flow.Launcher.Infrastructure.Logger.Log.Exception("Failed to remove favicon cache", ex);
181+
MessageBox.Show($"파비콘 캐시 삭제 중 오류가 발생했습니다: {ex.Message}", "오류", MessageBoxButton.OK, MessageBoxImage.Error);
182+
}
183+
}
119184
}

0 commit comments

Comments
 (0)