* 获取网站 favicon * 解析目标网站 HTML,提取 标签获取 favicon URL, * 然后下载图标并转为 base64
(url: string)
| 286 | * 然后下载图标并转为 base64 |
| 287 | */ |
| 288 | public async fetchFavicon(url: string): Promise<string> { |
| 289 | try { |
| 290 | const candidateUrl = this.ensureUrlProtocol(url.replace('{q}', 'test').trim()) |
| 291 | const urlObj = new URL(candidateUrl) |
| 292 | const origin = urlObj.origin |
| 293 | |
| 294 | // 先尝试请求网页获取 favicon link。部分站点的压缩响应会导致 Electron net |
| 295 | // 解码失败,这里只跳过 HTML 解析,仍继续回退到 /favicon.ico。 |
| 296 | try { |
| 297 | const html = await this.httpGet(`${origin}/`) |
| 298 | const faviconUrl = this.parseFaviconFromHtml(html, origin) |
| 299 | |
| 300 | if (faviconUrl) { |
| 301 | const base64 = await this.downloadAsBase64(faviconUrl) |
| 302 | if (base64) return base64 |
| 303 | } |
| 304 | } catch (error) { |
| 305 | console.warn('[WebSearch] 获取页面 HTML 失败,回退到 /favicon.ico:', error) |
| 306 | } |
| 307 | |
| 308 | // 回退到 /favicon.ico |
| 309 | const fallbackBase64 = await this.downloadAsBase64(`${origin}/favicon.ico`) |
| 310 | if (fallbackBase64) return fallbackBase64 |
| 311 | |
| 312 | return '' |
| 313 | } catch (error) { |
| 314 | console.error('[WebSearch] fetchFavicon error:', error) |
| 315 | return '' |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | private normalizeEngine(engine: any): WebSearchEngine { |
| 320 | const type: WebSearchEngineType = engine?.type === 'webpage' ? 'webpage' : 'search' |
no test coverage detected