mirror of
https://github.com/searxng/searxng.git
synced 2024-11-22 04:01:40 +01:00
[feat] add favicons to result urls
This commit is contained in:
parent
3e747d0491
commit
e17d7632d0
@ -9,6 +9,7 @@
|
|||||||
search:
|
search:
|
||||||
safe_search: 0
|
safe_search: 0
|
||||||
autocomplete: ""
|
autocomplete: ""
|
||||||
|
favicon_resolver: ""
|
||||||
default_lang: ""
|
default_lang: ""
|
||||||
ban_time_on_fail: 5
|
ban_time_on_fail: 5
|
||||||
max_ban_time_on_fail: 120
|
max_ban_time_on_fail: 120
|
||||||
@ -41,6 +42,14 @@
|
|||||||
- ``qwant``
|
- ``qwant``
|
||||||
- ``wikipedia``
|
- ``wikipedia``
|
||||||
|
|
||||||
|
``favicon_resolver``:
|
||||||
|
Favicon resolver, leave blank to turn off the feature by default.
|
||||||
|
|
||||||
|
- ``allesedv``
|
||||||
|
- ``duckduckgo``
|
||||||
|
- ``google``
|
||||||
|
- ``yandex``
|
||||||
|
|
||||||
``default_lang``:
|
``default_lang``:
|
||||||
Default search language - leave blank to detect from browser information or
|
Default search language - leave blank to detect from browser information or
|
||||||
use codes from :origin:`searx/languages.py`.
|
use codes from :origin:`searx/languages.py`.
|
||||||
|
105
searx/favicon_resolver.py
Normal file
105
searx/favicon_resolver.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""This module implements functions needed for the favicon resolver.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# pylint: disable=use-dict-literal
|
||||||
|
|
||||||
|
from httpx import HTTPError
|
||||||
|
|
||||||
|
from searx import settings
|
||||||
|
|
||||||
|
from searx.network import get as http_get, post as http_post
|
||||||
|
from searx.exceptions import SearxEngineResponseException
|
||||||
|
|
||||||
|
|
||||||
|
def update_kwargs(**kwargs):
|
||||||
|
if 'timeout' not in kwargs:
|
||||||
|
kwargs['timeout'] = settings['outgoing']['request_timeout']
|
||||||
|
kwargs['raise_for_httperror'] = False
|
||||||
|
|
||||||
|
|
||||||
|
def get(*args, **kwargs):
|
||||||
|
update_kwargs(**kwargs)
|
||||||
|
return http_get(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def post(*args, **kwargs):
|
||||||
|
update_kwargs(**kwargs)
|
||||||
|
return http_post(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def allesedv(domain):
|
||||||
|
"""Favicon Resolver from allesedv.com"""
|
||||||
|
|
||||||
|
url = 'https://f1.allesedv.com/32/{domain}'
|
||||||
|
|
||||||
|
# will just return a 200 regardless of the favicon existing or not
|
||||||
|
# sometimes will be correct size, sometimes not
|
||||||
|
response = get(url.format(domain=domain))
|
||||||
|
|
||||||
|
# returns image/gif if the favicon does not exist
|
||||||
|
if response.headers['Content-Type'] == 'image/gif':
|
||||||
|
return []
|
||||||
|
|
||||||
|
return response.content
|
||||||
|
|
||||||
|
|
||||||
|
def duckduckgo(domain):
|
||||||
|
"""Favicon Resolver from duckduckgo.com"""
|
||||||
|
|
||||||
|
url = 'https://icons.duckduckgo.com/ip2/{domain}.ico'
|
||||||
|
|
||||||
|
# will return a 404 if the favicon does not exist and a 200 if it does,
|
||||||
|
response = get(url.format(domain=domain))
|
||||||
|
|
||||||
|
# api will respond with a 32x32 png image
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.content
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def google(domain):
|
||||||
|
"""Favicon Resolver from google.com"""
|
||||||
|
|
||||||
|
url = 'https://www.google.com/s2/favicons?sz=32&domain={domain}'
|
||||||
|
|
||||||
|
# will return a 404 if the favicon does not exist and a 200 if it does,
|
||||||
|
response = get(url.format(domain=domain))
|
||||||
|
|
||||||
|
# api will respond with a 32x32 png image
|
||||||
|
if response.status_code == 200:
|
||||||
|
return response.content
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def yandex(domain):
|
||||||
|
"""Favicon Resolver from yandex.com"""
|
||||||
|
|
||||||
|
url = 'https://favicon.yandex.net/favicon/{domain}'
|
||||||
|
|
||||||
|
# will always return 200
|
||||||
|
response = get(url.format(domain=domain))
|
||||||
|
|
||||||
|
# api will respond with a 16x16 png image, if it doesn't exist, it will be a 1x1 png image (70 bytes)
|
||||||
|
if response.status_code == 200:
|
||||||
|
if len(response.content) > 70:
|
||||||
|
return response.content
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
backends = {
|
||||||
|
'allesedv': allesedv,
|
||||||
|
'duckduckgo': duckduckgo,
|
||||||
|
'google': google,
|
||||||
|
'yandex': yandex,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def search_favicon(backend_name, domain):
|
||||||
|
backend = backends.get(backend_name)
|
||||||
|
if backend is None:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
return backend(domain)
|
||||||
|
except (HTTPError, SearxEngineResponseException):
|
||||||
|
return []
|
@ -13,7 +13,7 @@ from collections import OrderedDict
|
|||||||
import flask
|
import flask
|
||||||
import babel
|
import babel
|
||||||
|
|
||||||
from searx import settings, autocomplete
|
from searx import settings, autocomplete, favicon_resolver
|
||||||
from searx.enginelib import Engine
|
from searx.enginelib import Engine
|
||||||
from searx.plugins import Plugin
|
from searx.plugins import Plugin
|
||||||
from searx.locales import LOCALE_NAMES
|
from searx.locales import LOCALE_NAMES
|
||||||
@ -406,6 +406,11 @@ class Preferences:
|
|||||||
locked=is_locked('autocomplete'),
|
locked=is_locked('autocomplete'),
|
||||||
choices=list(autocomplete.backends.keys()) + ['']
|
choices=list(autocomplete.backends.keys()) + ['']
|
||||||
),
|
),
|
||||||
|
'favicon_resolver': EnumStringSetting(
|
||||||
|
settings['search']['favicon_resolver'],
|
||||||
|
locked=is_locked('favicon_resolver'),
|
||||||
|
choices=list(favicon_resolver.backends.keys()) + ['']
|
||||||
|
),
|
||||||
'image_proxy': BooleanSetting(
|
'image_proxy': BooleanSetting(
|
||||||
settings['server']['image_proxy'],
|
settings['server']['image_proxy'],
|
||||||
locked=is_locked('image_proxy')
|
locked=is_locked('image_proxy')
|
||||||
|
@ -35,6 +35,9 @@ search:
|
|||||||
autocomplete: ""
|
autocomplete: ""
|
||||||
# minimun characters to type before autocompleter starts
|
# minimun characters to type before autocompleter starts
|
||||||
autocomplete_min: 4
|
autocomplete_min: 4
|
||||||
|
# backend for the favicon near URL in search results.
|
||||||
|
# Available resolvers: "allesedv", "duckduckgo", "google", "yandex" - leave blank to turn it off by default.
|
||||||
|
favicon_resolver: ""
|
||||||
# Default search language - leave blank to detect from browser information or
|
# Default search language - leave blank to detect from browser information or
|
||||||
# use codes from 'languages.py'
|
# use codes from 'languages.py'
|
||||||
default_lang: "auto"
|
default_lang: "auto"
|
||||||
|
@ -156,6 +156,7 @@ SCHEMA = {
|
|||||||
'safe_search': SettingsValue((0, 1, 2), 0),
|
'safe_search': SettingsValue((0, 1, 2), 0),
|
||||||
'autocomplete': SettingsValue(str, ''),
|
'autocomplete': SettingsValue(str, ''),
|
||||||
'autocomplete_min': SettingsValue(int, 4),
|
'autocomplete_min': SettingsValue(int, 4),
|
||||||
|
'favicon_resolver': SettingsValue(str, ''),
|
||||||
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
|
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
|
||||||
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
|
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
|
||||||
'ban_time_on_fail': SettingsValue(numbers.Real, 5),
|
'ban_time_on_fail': SettingsValue(numbers.Real, 5),
|
||||||
|
5
searx/static/themes/simple/img/empty_favicon.svg
Normal file
5
searx/static/themes/simple/img/empty_favicon.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path fill="#fff" d="M0 0h24v24H0z"/>
|
||||||
|
<path fill="#58f" d="M11 20.85a.92.92 0 0 1-1.1.93A10 10 0 0 1 2.06 13c-.06-.55.4-1 .95-1h3a1 1 0 0 1 1 1 3 3 0 0 0 3 3 1 1 0 0 1 1 1v3.85Zm6-1.92c0 .77.83 1.23 1.42.74a10 10 0 0 0 2.03-2.32c.39-.61-.09-1.35-.81-1.35H18a1 1 0 0 0-1 1v1.93ZM12 2a10 10 0 0 1 6.65 2.53c.61.55.17 1.47-.65 1.47h-.15A2.85 2.85 0 0 0 15 8.85c0 .33-.18.62-.47.77l-.08.04a1 1 0 0 1-.9 0l-.08-.04a.85.85 0 0 1-.47-.77A2.85 2.85 0 0 0 10.15 6H10a1 1 0 0 1-1-1V3.2c0-.44.28-.84.7-.94C10.45 2.1 11.22 2 12 2Z"/>
|
||||||
|
<path fill="#58f" d="M3.42 10c-.63 0-1.1-.58-.9-1.18.6-1.8 1.7-3.36 3.12-4.53C6.2 3.82 7 4.26 7 5a3 3 0 0 0 3 3h.15c.47 0 .85.38.85.85 0 1.09.61 2.07 1.58 2.56l.08.04a3 3 0 0 0 2.68 0l.08-.04A2.85 2.85 0 0 0 17 8.85c0-.47.38-.85.85-.85h2.66c.4 0 .77.23.9.6a9.98 9.98 0 0 1 .52 4.6.94.94 0 0 1-.95.8H18a3 3 0 0 0-3 3v3.8c0 .44-.28.84-.7.94l-.2.04a.92.92 0 0 1-1.1-.93V17a3 3 0 0 0-3-3 1 1 0 0 1-1-1 3 3 0 0 0-3-3H3.42Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -378,3 +378,12 @@ html.no-js #clear_search.hide_if_nojs {
|
|||||||
#categories_container {
|
#categories_container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favicon img {
|
||||||
|
height: 1.8rem;
|
||||||
|
width: 1.8rem;
|
||||||
|
border-radius: 20%;
|
||||||
|
background-color: #ddd;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
@ -82,4 +82,8 @@
|
|||||||
transform: scale(1, 1);
|
transform: scale(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favicon {
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
@import "style.less";
|
@import "style.less";
|
||||||
|
@ -96,6 +96,10 @@
|
|||||||
|
|
||||||
.result .url_wrapper {
|
.result .url_wrapper {
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
|
||||||
|
.favicon {
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,6 +234,7 @@ article[data-vim-selected].category-social {
|
|||||||
|
|
||||||
.url_wrapper {
|
.url_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: var(--color-result-url-font);
|
color: var(--color-result-url-font);
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
@ -21,9 +21,29 @@
|
|||||||
{% macro result_header(result, favicons, image_proxify) -%}
|
{% macro result_header(result, favicons, image_proxify) -%}
|
||||||
<article class="result {% if result['template'] %}result-{{ result.template|replace('.html', '') }}{% else %}result-default{% endif %} {% if result['category'] %}category-{{ result['category'] }}{% endif %}{% for e in result.engines %} {{ e }}{% endfor %}">
|
<article class="result {% if result['template'] %}result-{{ result.template|replace('.html', '') }}{% else %}result-default{% endif %} {% if result['category'] %}category-{{ result['category'] }}{% endif %}{% for e in result.engines %} {{ e }}{% endfor %}">
|
||||||
{{- result_open_link(result.url, "url_wrapper") -}}
|
{{- result_open_link(result.url, "url_wrapper") -}}
|
||||||
|
{% if not rtl %}
|
||||||
|
{%- if favicon_resolver != "" %}
|
||||||
|
<div class="favicon">
|
||||||
|
<img
|
||||||
|
alt="{{ result.parsed_url.netloc }}"
|
||||||
|
src="{{ favicon_proxify(result.parsed_url.netloc) }}"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
{%- for part in get_pretty_url(result.parsed_url) -%}
|
{%- for part in get_pretty_url(result.parsed_url) -%}
|
||||||
<span class="url_o{{loop.index}}"><span class="url_i{{loop.index}}">{{- part -}}</span></span>
|
<span class="url_o{{loop.index}}"><span class="url_i{{loop.index}}">{{- part -}}</span></span>
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
{% if rtl %}
|
||||||
|
{%- if favicon_resolver != "" %}
|
||||||
|
<div class="favicon">
|
||||||
|
<img
|
||||||
|
alt="{{ result.parsed_url.netloc }}"
|
||||||
|
src="{{ favicon_proxify(result.parsed_url.netloc) }}"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endif -%}
|
||||||
{{- result_close_link() -}}
|
{{- result_close_link() -}}
|
||||||
{%- if result.thumbnail %}{{ result_open_link(result.url) }}<img class="thumbnail" src="{{ image_proxify(result.thumbnail) }}" title="{{ result.title|striptags }}" loading="lazy">{{ result_close_link() }}{% endif -%}
|
{%- if result.thumbnail %}{{ result_open_link(result.url) }}<img class="thumbnail" src="{{ image_proxify(result.thumbnail) }}" title="{{ result.title|striptags }}" loading="lazy">{{ result_close_link() }}{% endif -%}
|
||||||
<h3>{{ result_link(result.url, result.title|safe) }}</h3>
|
<h3>{{ result_link(result.url, result.title|safe) }}</h3>
|
||||||
|
@ -173,6 +173,9 @@
|
|||||||
{%- if 'autocomplete' not in locked_preferences -%}
|
{%- if 'autocomplete' not in locked_preferences -%}
|
||||||
{%- include 'simple/preferences/autocomplete.html' -%}
|
{%- include 'simple/preferences/autocomplete.html' -%}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
{%- if 'favicon' not in locked_preferences -%}
|
||||||
|
{%- include 'simple/preferences/favicon.html' -%}
|
||||||
|
{%- endif -%}
|
||||||
{% if 'safesearch' not in locked_preferences %}
|
{% if 'safesearch' not in locked_preferences %}
|
||||||
{%- include 'simple/preferences/safesearch.html' -%}
|
{%- include 'simple/preferences/safesearch.html' -%}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
17
searx/templates/simple/preferences/favicon.html
Normal file
17
searx/templates/simple/preferences/favicon.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<fieldset>{{- '' -}}
|
||||||
|
<legend id="pref_favicon_resolver">{{- _('Favicon Resolver') -}}</legend>{{- '' -}}
|
||||||
|
<div class="value">{{- '' -}}
|
||||||
|
<select name="favicon_resolver" aria-labelledby="pref_favicon_resolver">{{- '' -}}
|
||||||
|
<option value=""> - </option>
|
||||||
|
{%- for backend in favicon_backends -%}
|
||||||
|
<option value="{{ backend }}"
|
||||||
|
{%- if backend == favicon_resolver %} selected="selected" {%- endif -%}>
|
||||||
|
{{- backend -}}
|
||||||
|
</option>
|
||||||
|
{%- endfor -%}
|
||||||
|
</select>{{- '' -}}
|
||||||
|
</div>{{- '' -}}
|
||||||
|
<div class="description">
|
||||||
|
{{- _('Display favicons near search results') -}}
|
||||||
|
</div>{{- '' -}}
|
||||||
|
</fieldset>{{- '' -}}
|
@ -123,6 +123,7 @@ from searx.locales import (
|
|||||||
|
|
||||||
# renaming names from searx imports ...
|
# renaming names from searx imports ...
|
||||||
from searx.autocomplete import search_autocomplete, backends as autocomplete_backends
|
from searx.autocomplete import search_autocomplete, backends as autocomplete_backends
|
||||||
|
from searx.favicon_resolver import search_favicon, backends as favicon_backends
|
||||||
from searx.redisdb import initialize as redis_initialize
|
from searx.redisdb import initialize as redis_initialize
|
||||||
from searx.sxng_locales import sxng_locales
|
from searx.sxng_locales import sxng_locales
|
||||||
from searx.search import SearchWithPlugins, initialize as search_initialize
|
from searx.search import SearchWithPlugins, initialize as search_initialize
|
||||||
@ -297,6 +298,24 @@ def morty_proxify(url: str):
|
|||||||
return '{0}?{1}'.format(settings['result_proxy']['url'], urlencode(url_params))
|
return '{0}?{1}'.format(settings['result_proxy']['url'], urlencode(url_params))
|
||||||
|
|
||||||
|
|
||||||
|
def favicon_proxify(url: str):
|
||||||
|
# url is a FQDN (e.g. example.com, en.wikipedia.org)
|
||||||
|
|
||||||
|
resolver = request.preferences.get_value('favicon_resolver')
|
||||||
|
|
||||||
|
# if resolver is empty, just return nothing
|
||||||
|
if not resolver:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# check resolver is valid
|
||||||
|
if resolver not in favicon_backends:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
h = new_hmac(settings['server']['secret_key'], url.encode())
|
||||||
|
|
||||||
|
return '{0}?{1}'.format(url_for('favicon_proxy'), urlencode(dict(q=url.encode(), h=h)))
|
||||||
|
|
||||||
|
|
||||||
def image_proxify(url: str):
|
def image_proxify(url: str):
|
||||||
|
|
||||||
if url.startswith('//'):
|
if url.startswith('//'):
|
||||||
@ -358,6 +377,7 @@ def get_client_settings():
|
|||||||
return {
|
return {
|
||||||
'autocomplete_provider': req_pref.get_value('autocomplete'),
|
'autocomplete_provider': req_pref.get_value('autocomplete'),
|
||||||
'autocomplete_min': get_setting('search.autocomplete_min'),
|
'autocomplete_min': get_setting('search.autocomplete_min'),
|
||||||
|
'favicon_resolver': req_pref.get_value('favicon_resolver'),
|
||||||
'http_method': req_pref.get_value('method'),
|
'http_method': req_pref.get_value('method'),
|
||||||
'infinite_scroll': req_pref.get_value('infinite_scroll'),
|
'infinite_scroll': req_pref.get_value('infinite_scroll'),
|
||||||
'translations': get_translations(),
|
'translations': get_translations(),
|
||||||
@ -388,6 +408,7 @@ def render(template_name: str, **kwargs):
|
|||||||
# values from the preferences
|
# values from the preferences
|
||||||
kwargs['preferences'] = request.preferences
|
kwargs['preferences'] = request.preferences
|
||||||
kwargs['autocomplete'] = request.preferences.get_value('autocomplete')
|
kwargs['autocomplete'] = request.preferences.get_value('autocomplete')
|
||||||
|
kwargs['favicon_resolver'] = request.preferences.get_value('favicon_resolver')
|
||||||
kwargs['infinite_scroll'] = request.preferences.get_value('infinite_scroll')
|
kwargs['infinite_scroll'] = request.preferences.get_value('infinite_scroll')
|
||||||
kwargs['search_on_category_select'] = request.preferences.get_value('search_on_category_select')
|
kwargs['search_on_category_select'] = request.preferences.get_value('search_on_category_select')
|
||||||
kwargs['hotkeys'] = request.preferences.get_value('hotkeys')
|
kwargs['hotkeys'] = request.preferences.get_value('hotkeys')
|
||||||
@ -431,6 +452,7 @@ def render(template_name: str, **kwargs):
|
|||||||
# helpers to create links to other pages
|
# helpers to create links to other pages
|
||||||
kwargs['url_for'] = custom_url_for # override url_for function in templates
|
kwargs['url_for'] = custom_url_for # override url_for function in templates
|
||||||
kwargs['image_proxify'] = image_proxify
|
kwargs['image_proxify'] = image_proxify
|
||||||
|
kwargs['favicon_proxify'] = favicon_proxify
|
||||||
kwargs['proxify'] = morty_proxify if settings['result_proxy']['url'] is not None else None
|
kwargs['proxify'] = morty_proxify if settings['result_proxy']['url'] is not None else None
|
||||||
kwargs['proxify_results'] = settings['result_proxy']['proxify_results']
|
kwargs['proxify_results'] = settings['result_proxy']['proxify_results']
|
||||||
kwargs['cache_url'] = settings['ui']['cache_url']
|
kwargs['cache_url'] = settings['ui']['cache_url']
|
||||||
@ -873,6 +895,42 @@ def autocompleter():
|
|||||||
return Response(suggestions, mimetype=mimetype)
|
return Response(suggestions, mimetype=mimetype)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/favicon', methods=['GET'])
|
||||||
|
def favicon_proxy():
|
||||||
|
"""Return proxied favicon results"""
|
||||||
|
url = request.args.get('q')
|
||||||
|
|
||||||
|
# malformed request
|
||||||
|
if not url:
|
||||||
|
return '', 400
|
||||||
|
|
||||||
|
# malformed request / does not have authorisation
|
||||||
|
if not is_hmac_of(settings['server']['secret_key'], url.encode(), request.args.get('h', '')):
|
||||||
|
return '', 400
|
||||||
|
|
||||||
|
resolver = request.preferences.get_value('favicon_resolver')
|
||||||
|
|
||||||
|
# check if the favicon resolver is valid
|
||||||
|
if not resolver or resolver not in favicon_backends:
|
||||||
|
return '', 400
|
||||||
|
|
||||||
|
# parse query
|
||||||
|
raw_text_query = RawTextQuery(url, [])
|
||||||
|
|
||||||
|
resp = search_favicon(resolver, raw_text_query)
|
||||||
|
|
||||||
|
# return 404 if the favicon is not found
|
||||||
|
if not resp:
|
||||||
|
theme = request.preferences.get_value("theme")
|
||||||
|
# return favicon from /static/themes/simple/img/empty_favicon.svg
|
||||||
|
# we can't rely on an onerror event in the img tag to display a default favicon as this violates the CSP.
|
||||||
|
# using redirect to save network bandwidth (user will have this location cached).
|
||||||
|
return redirect(url_for('static', filename='themes/' + theme + '/img/empty_favicon.svg'))
|
||||||
|
|
||||||
|
# will always return a PNG image
|
||||||
|
return Response(resp, mimetype='image/png')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/preferences', methods=['GET', 'POST'])
|
@app.route('/preferences', methods=['GET', 'POST'])
|
||||||
def preferences():
|
def preferences():
|
||||||
"""Render preferences page && save user preferences"""
|
"""Render preferences page && save user preferences"""
|
||||||
@ -1020,6 +1078,7 @@ def preferences():
|
|||||||
],
|
],
|
||||||
disabled_engines = disabled_engines,
|
disabled_engines = disabled_engines,
|
||||||
autocomplete_backends = autocomplete_backends,
|
autocomplete_backends = autocomplete_backends,
|
||||||
|
favicon_backends = favicon_backends,
|
||||||
shortcuts = {y: x for x, y in engine_shortcuts.items()},
|
shortcuts = {y: x for x, y in engine_shortcuts.items()},
|
||||||
themes = themes,
|
themes = themes,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
|
@ -5,6 +5,7 @@ general:
|
|||||||
search:
|
search:
|
||||||
safe_search: 0
|
safe_search: 0
|
||||||
autocomplete: ""
|
autocomplete: ""
|
||||||
|
favicon_resolver: ""
|
||||||
default_lang: ""
|
default_lang: ""
|
||||||
ban_time_on_fail: 5
|
ban_time_on_fail: 5
|
||||||
max_ban_time_on_fail: 120
|
max_ban_time_on_fail: 120
|
||||||
|
Loading…
Reference in New Issue
Block a user