@@ -83,6 +83,7 @@ private async Task SaveFailedFetchesAsync()
83
83
await File . WriteAllTextAsync ( _failsFilePath , json , _cts . Token ) ;
84
84
}
85
85
catch ( OperationCanceledException ) { /* Swallow if app is closing */ }
86
+ catch ( ObjectDisposedException ) { /* Swallow if disposing */ }
86
87
catch ( Exception ex )
87
88
{
88
89
_context . API . LogException ( nameof ( FaviconService ) , $ "Failed to save failed favicons file to { _failsFilePath } ", ex ) ;
@@ -102,22 +103,37 @@ public async Task ProcessBookmarkFavicons(IReadOnlyList<Bookmark> bookmarks, Can
102
103
103
104
await Parallel . ForEachAsync ( bookmarks , options , async ( bookmark , token ) =>
104
105
{
105
- var cachePath = GetCachePath ( bookmark . Url , _faviconCacheDir ) ;
106
- if ( File . Exists ( cachePath ) )
106
+ var pageCachePath = GetCachePath ( bookmark . Url , _faviconCacheDir ) ;
107
+ var hostCachePath = Uri . TryCreate ( bookmark . Url , UriKind . Absolute , out var pageUri )
108
+ ? GetCachePath ( pageUri . GetLeftPart ( UriPartial . Authority ) , _faviconCacheDir )
109
+ : pageCachePath ;
110
+ if ( File . Exists ( hostCachePath ) )
107
111
{
108
- bookmark . FaviconPath = cachePath ;
112
+ bookmark . FaviconPath = hostCachePath ;
113
+ return ;
114
+ }
115
+ if ( File . Exists ( pageCachePath ) )
116
+ {
117
+ bookmark . FaviconPath = pageCachePath ;
109
118
return ;
110
119
}
111
-
112
120
// 1. Try local browser database
113
121
var localData = await _localExtractor . GetFaviconDataAsync ( bookmark , token ) ;
114
122
if ( localData != null )
115
123
{
116
124
var ( pngData , _) = await _imageConverter . ToPngAsync ( new MemoryStream ( localData ) , token ) ;
117
125
if ( pngData != null )
118
126
{
119
- await File . WriteAllBytesAsync ( cachePath , pngData , token ) ;
120
- bookmark . FaviconPath = cachePath ;
127
+ var path = hostCachePath ;
128
+ try
129
+ {
130
+ await File . WriteAllBytesAsync ( path , pngData , token ) ;
131
+ }
132
+ catch ( IOException )
133
+ {
134
+ // Another thread may have created it concurrently.
135
+ }
136
+ bookmark . FaviconPath = path ;
121
137
return ;
122
138
}
123
139
}
@@ -132,6 +148,7 @@ await Parallel.ForEachAsync(bookmarks, options, async (bookmark, token) =>
132
148
}
133
149
}
134
150
} ) ;
151
+
135
152
}
136
153
137
154
private async Task < string ? > GetFaviconFromWebAsync ( Uri url , CancellationToken token )
@@ -211,7 +228,14 @@ private static string GetCachePath(string url, string cacheDir)
211
228
212
229
if ( bestResult . PngData != null )
213
230
{
214
- await File . WriteAllBytesAsync ( cachePath , bestResult . PngData , _cts . Token ) ;
231
+ try
232
+ {
233
+ await File . WriteAllBytesAsync ( cachePath , bestResult . PngData , _cts . Token ) ;
234
+ }
235
+ catch ( IOException )
236
+ {
237
+ // Another thread may have created it concurrently.
238
+ }
215
239
_context . API . LogDebug ( nameof ( FaviconService ) , $ "Favicon for { urlString } cached successfully.") ;
216
240
if ( _failedFetches . TryRemove ( urlString , out _ ) )
217
241
{
0 commit comments