1
1
using System ;
2
+ using System . Collections . Concurrent ;
2
3
using System . Collections . Generic ;
3
4
using System . IO ;
4
5
using System . Linq ;
6
+ using System . Threading . Tasks ;
5
7
using Flow . Launcher . Plugin . BrowserBookmark . Models ;
6
8
using Microsoft . Data . Sqlite ;
7
9
@@ -30,8 +32,6 @@ INNER JOIN moz_bookmarks ON (
30
32
ORDER BY moz_places.visit_count DESC
31
33
""" ;
32
34
33
- private const string DbPathFormat = "Data Source={0}" ;
34
-
35
35
protected List < Bookmark > GetBookmarksFromPath ( string placesPath )
36
36
{
37
37
// Variable to store bookmark list
@@ -117,36 +117,53 @@ protected List<Bookmark> GetBookmarksFromPath(string placesPath)
117
117
return bookmarks ;
118
118
}
119
119
120
- private void LoadFaviconsFromDb ( string faviconDbPath , List < Bookmark > bookmarks )
120
+ private void LoadFaviconsFromDb ( string dbPath , List < Bookmark > bookmarks )
121
121
{
122
122
// Use a copy to avoid lock issues with the original file
123
123
var tempDbPath = Path . Combine ( _faviconCacheDir , $ "tempfavicons_{ Guid . NewGuid ( ) } .sqlite") ;
124
124
125
125
// Use a copy to avoid lock issues with the original file
126
126
try
127
127
{
128
- // Use a copy to avoid lock issues with the original file
129
- File . Copy ( faviconDbPath , tempDbPath , true ) ;
130
-
131
- var defaultIconPath = Path . Combine (
132
- Path . GetDirectoryName ( typeof ( FirefoxBookmarkLoaderBase ) . Assembly . Location ) ,
133
- "bookmark.png" ) ;
134
-
135
- string dbPath = string . Format ( DbPathFormat , tempDbPath ) ;
136
- using var connection = new SqliteConnection ( dbPath ) ;
137
- connection . Open ( ) ;
138
-
139
- // Get favicons based on bookmark URLs
140
- foreach ( var bookmark in bookmarks )
128
+ File . Copy ( dbPath , tempDbPath , true ) ;
129
+ }
130
+ catch ( Exception ex )
131
+ {
132
+ try
141
133
{
134
+ if ( File . Exists ( tempDbPath ) )
135
+ {
136
+ File . Delete ( tempDbPath ) ;
137
+ }
138
+ }
139
+ catch ( Exception ex1 )
140
+ {
141
+ Main . _context . API . LogException ( ClassName , $ "Failed to delete temporary favicon DB: { tempDbPath } ", ex1 ) ;
142
+ }
143
+ Main . _context . API . LogException ( ClassName , $ "Failed to copy favicon DB: { dbPath } ", ex ) ;
144
+ return ;
145
+ }
146
+
147
+ try
148
+ {
149
+ // Since some bookmarks may have same favorite icon id, we need to record them to avoid duplicates
150
+ var savedPaths = new ConcurrentDictionary < string , bool > ( ) ;
151
+
152
+ // Get favicons based on bookmarks concurrently
153
+ Parallel . ForEach ( bookmarks , bookmark =>
154
+ {
155
+ // Use read-only connection to avoid locking issues
156
+ var connection = new SqliteConnection ( $ "Data Source={ tempDbPath } ;Mode=ReadOnly") ;
157
+ connection . Open ( ) ;
158
+
142
159
try
143
160
{
144
161
if ( string . IsNullOrEmpty ( bookmark . Url ) )
145
- continue ;
162
+ return ;
146
163
147
164
// Extract domain from URL
148
165
if ( ! Uri . TryCreate ( bookmark . Url , UriKind . Absolute , out Uri uri ) )
149
- continue ;
166
+ return ;
150
167
151
168
var domain = uri . Host ;
152
169
@@ -166,12 +183,12 @@ ORDER BY i.width DESC -- Select largest icon available
166
183
167
184
using var reader = cmd . ExecuteReader ( ) ;
168
185
if ( ! reader . Read ( ) || reader . IsDBNull ( 0 ) )
169
- continue ;
186
+ return ;
170
187
171
188
var imageData = ( byte [ ] ) reader [ "data" ] ;
172
189
173
190
if ( imageData is not { Length : > 0 } )
174
- continue ;
191
+ return ;
175
192
176
193
string faviconPath ;
177
194
if ( IsSvgData ( imageData ) )
@@ -182,23 +199,31 @@ ORDER BY i.width DESC -- Select largest icon available
182
199
{
183
200
faviconPath = Path . Combine ( _faviconCacheDir , $ "firefox_{ domain } .png") ;
184
201
}
185
- SaveBitmapData ( imageData , faviconPath ) ;
202
+
203
+ // Filter out duplicate favorite icons
204
+ if ( savedPaths . TryAdd ( faviconPath , true ) )
205
+ {
206
+ SaveBitmapData ( imageData , faviconPath ) ;
207
+ }
186
208
187
209
bookmark . FaviconPath = faviconPath ;
188
210
}
189
211
catch ( Exception ex )
190
212
{
191
213
Main . _context . API . LogException ( ClassName , $ "Failed to extract Firefox favicon: { bookmark . Url } ", ex ) ;
192
214
}
193
- }
194
-
195
- // https://github.com/dotnet/efcore/issues/26580
196
- SqliteConnection . ClearPool ( connection ) ;
197
- connection . Close ( ) ;
215
+ finally
216
+ {
217
+ // https://github.com/dotnet/efcore/issues/26580
218
+ SqliteConnection . ClearPool ( connection ) ;
219
+ connection . Close ( ) ;
220
+ connection . Dispose ( ) ;
221
+ }
222
+ } ) ;
198
223
}
199
224
catch ( Exception ex )
200
225
{
201
- Main . _context . API . LogException ( ClassName , $ "Failed to load Firefox favicon DB: { faviconDbPath } ", ex ) ;
226
+ Main . _context . API . LogException ( ClassName , $ "Failed to load Firefox favicon DB: { tempDbPath } ", ex ) ;
202
227
}
203
228
204
229
// Delete temporary file
0 commit comments