101 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			101 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|  | # SPDX-License-Identifier: AGPL-3.0-or-later | ||
|  | """Implementations of the favicon *resolvers* that are available in the favicon
 | ||
|  | proxy by default.  A *resolver* is a function that obtains the favicon from an | ||
|  | external source.  The *resolver* function receives two arguments (``domain, | ||
|  | timeout``) and returns a tuple ``(data, mime)``. | ||
|  | 
 | ||
|  | """
 | ||
|  | 
 | ||
|  | from __future__ import annotations | ||
|  | 
 | ||
|  | __all__ = ["DEFAULT_RESOLVER_MAP", "allesedv", "duckduckgo", "google", "yandex"] | ||
|  | 
 | ||
|  | from typing import Callable | ||
|  | from searx import network | ||
|  | from searx import logger | ||
|  | 
 | ||
|  | DEFAULT_RESOLVER_MAP: dict[str, Callable] | ||
|  | logger = logger.getChild('favicons.resolvers') | ||
|  | 
 | ||
|  | 
 | ||
|  | def _req_args(**kwargs): | ||
|  |     # add the request arguments from the searx.network | ||
|  |     d = {"raise_for_httperror": False} | ||
|  |     d.update(kwargs) | ||
|  |     return d | ||
|  | 
 | ||
|  | 
 | ||
|  | def allesedv(domain: str, timeout: int) -> tuple[None | bytes, None | str]: | ||
|  |     """Favicon Resolver from allesedv.com / https://favicon.allesedv.com/""" | ||
|  |     data, mime = (None, None) | ||
|  |     url = f"https://f1.allesedv.com/32/{domain}" | ||
|  |     logger.debug("fetch favicon from: %s", url) | ||
|  | 
 | ||
|  |     # will just return a 200 regardless of the favicon existing or not | ||
|  |     # sometimes will be correct size, sometimes not | ||
|  |     response = network.get(url, **_req_args(timeout=timeout)) | ||
|  |     if response and response.status_code == 200: | ||
|  |         mime = response.headers['Content-Type'] | ||
|  |         if mime != 'image/gif': | ||
|  |             data = response.content | ||
|  |     return data, mime | ||
|  | 
 | ||
|  | 
 | ||
|  | def duckduckgo(domain: str, timeout: int) -> tuple[None | bytes, None | str]: | ||
|  |     """Favicon Resolver from duckduckgo.com / https://blog.jim-nielsen.com/2021/displaying-favicons-for-any-domain/""" | ||
|  |     data, mime = (None, None) | ||
|  |     url = f"https://icons.duckduckgo.com/ip2/{domain}.ico" | ||
|  |     logger.debug("fetch favicon from: %s", url) | ||
|  | 
 | ||
|  |     # will return a 404 if the favicon does not exist and a 200 if it does, | ||
|  |     response = network.get(url, **_req_args(timeout=timeout)) | ||
|  |     if response and response.status_code == 200: | ||
|  |         # api will respond with a 32x32 png image | ||
|  |         mime = response.headers['Content-Type'] | ||
|  |         data = response.content | ||
|  |     return data, mime | ||
|  | 
 | ||
|  | 
 | ||
|  | def google(domain: str, timeout: int) -> tuple[None | bytes, None | str]: | ||
|  |     """Favicon Resolver from google.com""" | ||
|  |     data, mime = (None, None) | ||
|  | 
 | ||
|  |     # URL https://www.google.com/s2/favicons?sz=32&domain={domain}" will be | ||
|  |     # redirected (HTTP 301 Moved Permanently) to t1.gstatic.com/faviconV2: | ||
|  |     url = ( | ||
|  |         f"https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL" | ||
|  |         f"&url=https://{domain}&size=32" | ||
|  |     ) | ||
|  |     logger.debug("fetch favicon from: %s", url) | ||
|  | 
 | ||
|  |     # will return a 404 if the favicon does not exist and a 200 if it does, | ||
|  |     response = network.get(url, **_req_args(timeout=timeout)) | ||
|  |     if response and response.status_code == 200: | ||
|  |         # api will respond with a 32x32 png image | ||
|  |         mime = response.headers['Content-Type'] | ||
|  |         data = response.content | ||
|  |     return data, mime | ||
|  | 
 | ||
|  | 
 | ||
|  | def yandex(domain: str, timeout: int) -> tuple[None | bytes, None | str]: | ||
|  |     """Favicon Resolver from yandex.com""" | ||
|  |     data, mime = (None, None) | ||
|  |     url = f"https://favicon.yandex.net/favicon/{domain}" | ||
|  |     logger.debug("fetch favicon from: %s", url) | ||
|  | 
 | ||
|  |     # api will respond with a 16x16 png image, if it doesn't exist, it will be a | ||
|  |     # 1x1 png image (70 bytes) | ||
|  |     response = network.get(url, **_req_args(timeout=timeout)) | ||
|  |     if response and response.status_code == 200 and len(response.content) > 70: | ||
|  |         mime = response.headers['Content-Type'] | ||
|  |         data = response.content | ||
|  |     return data, mime | ||
|  | 
 | ||
|  | 
 | ||
|  | DEFAULT_RESOLVER_MAP = { | ||
|  |     "allesedv": allesedv, | ||
|  |     "duckduckgo": duckduckgo, | ||
|  |     "google": google, | ||
|  |     "yandex": yandex, | ||
|  | } |