From 7bda165ca340131ebfc80003789e66ac863a0dec Mon Sep 17 00:00:00 2001 From: Ben Busby Date: Wed, 11 Oct 2023 17:26:12 -0600 Subject: [PATCH] Fetch fallback site icons from DDG DDG provides favicons using the url format icons.duckduckgo.com/ip2/{site}.ico This can be used to fetch favicons in the event that the default "/favicon.ico" path does not work. --- app/routes.py | 6 ++++-- app/utils/misc.py | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/routes.py b/app/routes.py index 2938ed7..2188691 100644 --- a/app/routes.py +++ b/app/routes.py @@ -17,7 +17,8 @@ from app.models.config import Config from app.models.endpoint import Endpoint from app.request import Request, TorError from app.utils.bangs import resolve_bang -from app.utils.misc import empty_gif, placeholder_img, get_proxy_host_url +from app.utils.misc import empty_gif, placeholder_img, get_proxy_host_url, \ + fetch_favicon from app.filter import Filter from app.utils.misc import read_config_bool, get_client_ip, get_request_url, \ check_for_update @@ -493,7 +494,8 @@ def element(): # Display an empty gif if the requested element couldn't be retrieved if response.status_code != 200 or len(response.content) == 0: if 'favicon' in src_url: - return send_file(io.BytesIO(placeholder_img), mimetype='image/png') + favicon = fetch_favicon(src_url) + return send_file(io.BytesIO(favicon), mimetype='image/png') else: return send_file(io.BytesIO(empty_gif), mimetype='image/gif') diff --git a/app/utils/misc.py b/app/utils/misc.py index 140e5c4..8701c6f 100644 --- a/app/utils/misc.py +++ b/app/utils/misc.py @@ -2,11 +2,14 @@ import base64 from bs4 import BeautifulSoup as bsoup from flask import Request import hashlib +import io import os import re from requests import exceptions, get from urllib.parse import urlparse +ddg_favicon_site = 'http://icons.duckduckgo.com/ip2' + empty_gif = base64.b64decode( 'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==') @@ -21,6 +24,29 @@ placeholder_img = base64.b64decode( ) +def fetch_favicon(url: str) -> bytes: + """Fetches a favicon using DuckDuckGo's favicon retriever + + Args: + url: The url to fetch the favicon from + Returns: + bytes - the favicon bytes, or a placeholder image if one + was not returned + """ + domain = urlparse(url).netloc + + response = get(f'{ddg_favicon_site}/{domain}.ico') + + if response.status_code == 200 and len(response.content) > 0: + tmp_mem = io.BytesIO() + tmp_mem.write(response.content) + tmp_mem.seek(0) + + return tmp_mem.read() + else: + return placeholder_img + + def gen_file_hash(path: str, static_file: str) -> str: file_contents = open(os.path.join(path, static_file), 'rb').read() file_hash = hashlib.md5(file_contents).hexdigest()[:8]