123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- # 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,
- }
|