Merge branch 'searxng:master' into master

This commit is contained in:
Azure Star 2023-04-13 08:36:38 +02:00 committed by GitHub
commit a4788f8d90
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 250 additions and 296 deletions

View file

@ -1,6 +1,5 @@
searxng is a fork from `searx <https://github.com/searx/searx>`_ and is
maintained by Alexandre Flament (`@dalf <https://github.com/dalf>`_) and Markus
Heiser (`@return42 <https://github.com/return42>`_)
maintained by Markus Heiser (`@return42 <https://github.com/return42>`_)
People who have submitted patches/translations, reported bugs, consulted
features or generally made searx better:

View file

@ -76,7 +76,7 @@ test.shell:
MANAGE += buildenv
MANAGE += weblate.translations.commit weblate.push.translations
MANAGE += data.all data.languages data.useragents data.osm_keys_tags
MANAGE += data.all data.traits data.useragents
MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean
MANAGE += docker.build docker.push docker.buildx
MANAGE += gecko.driver

View file

@ -6,49 +6,66 @@ Configured Engines
.. sidebar:: Further reading ..
- :ref:`settings categories_as_tabs`
- :ref:`engines-dev`
- :ref:`settings engine`
Explanation of the :ref:`general engine configuration` shown in the table
:ref:`configured engines`.
- :ref:`general engine configuration`
.. jinja:: searx
SearXNG supports {{engines | length}} search engines (of which {{enabled_engine_count}} are enabled by default).
SearXNG supports {{engines | length}} search engines of which
{{enabled_engine_count}} are enabled by default.
Engines can be assigned to multiple :ref:`categories <engine categories>`.
The UI displays the tabs that are configured in :ref:`categories_as_tabs
<settings categories_as_tabs>`. In addition to these UI categories (also
called *tabs*), engines can be queried by their name or the categories they
belong to, by using a :ref:`\!bing syntax <search-syntax>`.
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. jinja:: searx
{% for category, engines in categories_as_tabs.items() %}
{{category}} search engines
tab ``!{{category.replace(' ', '_')}}``
---------------------------------------
{% for group, engines in engines | group_engines_in_tab %}
{% for group, group_bang, engines in engines | group_engines_in_tab %}
{% if loop.length > 1 %}
{{group}}
{% if group_bang %}group ``{{group_bang}}``{% else %}{{group}}{% endif %}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{% endif %}
.. flat-table::
:header-rows: 2
:stub-columns: 1
:widths: 10 1 10 1 1 1 1 1 1 1
* - :cspan:`5` Engines configured by default (in :ref:`settings.yml <engine settings>`)
- :cspan:`3` :ref:`Supported features <engine file>`
* - Name
- Shortcut
- !bang
- Module
- Disabled
- Timeout
- Weight
- Paging
- Language, Region
- Locale
- Safe search
- Time range
{% for mod in engines %}
* - `{{mod.name}} <{{mod.about and mod.about.website}}>`_
{%- if mod.about and mod.about.language %}
({{mod.about.language | upper}})
{%- endif %}
- ``!{{mod.shortcut}}``
- {%- if 'searx.engines.' + mod.__name__ in documented_modules %}
:py:mod:`~searx.engines.{{mod.__name__}}`
@ -56,9 +73,6 @@ Explanation of the :ref:`general engine configuration` shown in the table
:origin:`{{mod.__name__}} <searx/engines/{{mod.__name__}}.py>`
{%- endif %}
- {{(mod.disabled and "y") or ""}}
{%- if mod.about and mod.about.language %}
({{mod.about.language | upper}})
{%- endif %}
- {{mod.timeout}}
- {{mod.weight or 1 }}
{% if mod.engine_type == 'online' %}

View file

@ -311,7 +311,6 @@ Global Settings
``results_on_new_tab``:
Open result links in a new tab by default.
.. _settings redis:
``redis:``
@ -457,6 +456,9 @@ Communication with search engines.
``max_redirects`` :
30 by default. Maximum redirect before it is an error.
.. _settings categories_as_tabs:
``categories_as_tabs:``
-----------------------
@ -477,6 +479,15 @@ Categories not listed here can still be searched with the :ref:`search-syntax`.
files:
social media:
Engines are added to ``categories:`` (compare :ref:`engine categories`), the
categories listed in ``categories_as_tabs`` are shown as tabs in the UI. If
there are no active engines in a category, the tab is not displayed (e.g. if a
user disables all engines in a category).
On the preferences page (``/preferences``) -- under *engines* -- there is an
additional tab, called *other*. In this tab are all engines listed that are not
in one of the UI tabs (not included in ``categories_as_tabs``).
.. _settings engine:
Engine settings
@ -503,7 +514,7 @@ engine is shown. Most of the options have a default value or even are optional.
disabled: false
language: en_US
tokens: [ 'my-secret-token' ]
weigth: 1
weight: 1
display_error_messages: true
about:
website: https://example.com
@ -552,10 +563,21 @@ engine is shown. Most of the options have a default value or even are optional.
to build and send a ``Accept-Language`` header in the request to the origin
search engine.
.. _engine categories:
``categories`` : optional
Define in which categories this engine will be active. Most of the time, it is
defined in the code of the engine, but in a few cases it is useful, like when
describing multiple search engine using the same code.
Specifies to which categories the engine should be added. Engines can be
assigned to multiple categories.
Categories can be shown as tabs (:ref:`settings categories_as_tabs`) in the
UI. A search in a tab (in the UI) will query all engines that are active in
this tab. In the preferences page (``/preferences``) -- under *engines* --
users can select what engine should be active when querying in this tab.
Alternatively, :ref:`\!bang <search-syntax>` can be used to search all engines
in a category, regardless of whether they are active or not, or whether they
are in a tab of the UI or not. For example, ``!dictionaries`` can be used to
query all search engines in that category (group).
``timeout`` : optional
Timeout of the search with the current search engine. **Be careful, it will
@ -581,7 +603,7 @@ engine is shown. Most of the options have a default value or even are optional.
A list of secret tokens to make this engine *private*, more details see
:ref:`private engines`.
``weigth`` : default ``1``
``weight`` : default ``1``
Weighting of the results of this engine.
``display_error_messages`` : default ``true``

View file

@ -46,7 +46,7 @@ Engine File
======================= =========== ========================================================
argument type information
======================= =========== ========================================================
categories list pages, in which the engine is working
categories list categories, in which the engine is working
paging boolean support multiple pages
time_range_support boolean support search time range
engine_type str - ``online`` :ref:`[ref] <demo online engine>` by

32
manage
View file

@ -77,9 +77,9 @@ weblate.:
push.translations: push translation changes from SearXNG to Weblate's counterpart
to.translations: Update 'translations' branch with last additions from Weblate.
data.:
all : update searx/languages.py and ./data/*
languages : update searx/data/engines_languages.json & searx/languages.py
useragents: update searx/data/useragents.json with the most recent versions of Firefox.
all : update searx/sxng_locales.py and searx/data/*
traits : update searx/data/engine_traits.json & searx/sxng_locales.py
useragents: update searx/data/useragents.json with the most recent versions of Firefox
docs.:
html : build HTML documentation
live : autobuild HTML documentation while editing
@ -386,27 +386,33 @@ weblate.push.translations() {
data.all() {
( set -e
pyenv.activate
data.languages
data.traits
data.useragents
data.osm_keys_tags
build_msg DATA "update searx/data/osm_keys_tags.json"
pyenv.cmd python searxng_extra/update/update_osm_keys_tags.py
build_msg DATA "update searx/data/ahmia_blacklist.txt"
python searxng_extra/update/update_ahmia_blacklist.py
build_msg DATA "update searx/data/wikidata_units.json"
python searxng_extra/update/update_wikidata_units.py
build_msg DATA "update searx/data/currencies.json"
python searxng_extra/update/update_currencies.py
build_msg DATA "update searx/data/external_bangs.json"
python searxng_extra/update/update_external_bangs.py
build_msg DATA "update searx/data/engine_descriptions.json"
python searxng_extra/update/update_engine_descriptions.py
)
}
data.languages() {
data.traits() {
( set -e
pyenv.activate
build_msg ENGINES "fetch languages .."
python searxng_extra/update/update_languages.py
build_msg ENGINES "update update searx/languages.py"
build_msg DATA "update searx/data/engines_languages.json"
build_msg DATA "update searx/data/engine_traits.json"
python searxng_extra/update/update_engine_traits.py
build_msg ENGINES "update searx/sxng_locales.py"
)
dump_return $?
}
@ -417,12 +423,6 @@ data.useragents() {
dump_return $?
}
data.osm_keys_tags() {
build_msg DATA "update searx/data/osm_keys_tags.json"
pyenv.cmd python searxng_extra/update/update_osm_keys_tags.py
dump_return $?
}
docs.prebuild() {
build_msg DOCS "build ${DOCS_BUILD}/includes"
(

View file

@ -2,7 +2,7 @@ mock==5.0.1
nose2[coverage_plugin]==0.12.0
cov-core==1.15.0
black==22.12.0
pylint==2.17.1
pylint==2.17.2
splinter==0.19.0
selenium==4.8.3
twine==4.0.2

View file

@ -81,7 +81,7 @@ class Engine: # pylint: disable=too-few-public-methods
# settings.yml
categories: List[str]
"""Tabs, in which the engine is working."""
"""Specifies to which :ref:`engine categories` the engine should be added."""
name: str
"""Name that will be used across SearXNG to define this engine. In settings, on

View file

@ -45,7 +45,7 @@ ENGINE_DEFAULT_ARGS = {
"about": {},
}
# set automatically when an engine does not have any tab category
OTHER_CATEGORY = 'other'
DEFAULT_CATEGORY = 'other'
# Defaults for the namespace of an engine module, see :py:func:`load_engine`
@ -132,7 +132,7 @@ def load_engine(engine_data: dict) -> Optional[Engine]:
set_loggers(engine, engine_name)
if not any(cat in settings['categories_as_tabs'] for cat in engine.categories):
engine.categories.append(OTHER_CATEGORY)
engine.categories.append(DEFAULT_CATEGORY)
return engine

View file

@ -198,13 +198,11 @@ def response(resp):
url = link.attrib.get('href')
title = extract_text(link)
# Make sure that the element is free of <a href> links and <span class='algoSlug_icon'>
content = eval_xpath(result, '(.//p)[1]')
for p in content:
# Make sure that the element is free of <a href> links
for e in p.xpath('.//a'):
e.getparent().remove(e)
for e in p.xpath('.//span[@class="algoSlug_icon"]'):
e.getparent().remove(e)
content = extract_text(content)
# get the real URL either using the URL shown to user or following the Bing URL

View file

@ -36,7 +36,7 @@ about = {
send_accept_language_header = True
# engine dependent config
categories = ["others"]
categories = ["weather"]
URL = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}"

View file

@ -1,133 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""
Gigablast (Web)
"""
# pylint: disable=invalid-name, use-dict-literal
import re
from time import time
from json import loads
from urllib.parse import urlencode
from searx.network import get
# about
about = {
"website": 'https://www.gigablast.com',
"wikidata_id": 'Q3105449',
"official_api_documentation": 'https://gigablast.com/api.html',
"use_official_api": True,
"require_api_key": False,
"results": 'JSON',
}
# engine dependent config
categories = ['general', 'web']
# gigablast's pagination is totally damaged, don't use it
paging = False
safesearch = True
# search-url
base_url = 'https://gigablast.com'
search_path = '/search?'
# ugly hack: gigablast requires a random extra parameter which can be extracted
# from the source code of the gigablast HTTP client
extra_param = ''
# timestamp of the last fetch of extra_param
extra_param_ts = 0
# after how many seconds extra_param expire
extra_param_expiration_delay = 3000
gb_userid = ''
gb_code = ''
def fetch_extra_param(query_args, headers):
# example:
#
# var uxrl='/search?c=main&qlangcountry=en-us&q=south&s=10&rand=1590740241635&n';
# uxrl=uxrl+'sab=730863287';
#
# extra_param --> "rand=1590740241635&nsab=730863287"
global extra_param, extra_param_ts # pylint: disable=global-statement
extra_param_ts = time()
extra_param_path = search_path + urlencode(query_args)
text = get(base_url + extra_param_path, headers=headers).text
re_var = None
for line in text.splitlines():
if re_var is None and extra_param_path in line:
var = line.split("=")[0].split()[1] # e.g. var --> 'uxrl'
re_var = re.compile(var + "\\s*=\\s*" + var + "\\s*\\+\\s*'" + "(.*)" + "'(.*)")
extra_param = line.split("'")[1][len(extra_param_path) :]
continue
if re_var is not None and re_var.search(line):
extra_param += re_var.search(line).group(1)
break
# do search-request
def request(query, params): # pylint: disable=unused-argument
query_args = dict(c='main', q=query, dr=1, showgoodimages=0)
if gb_userid and gb_code:
query_args['userid'] = gb_userid
query_args['code'] = gb_code
if params['language'] and params['language'] != 'all':
query_args['qlangcountry'] = params['language']
query_args['qlang'] = params['language'].split('-')[0]
if params['safesearch'] >= 1:
query_args['ff'] = 1
# see API http://www.gigablast.com/api.html#/search
# Take into account, that the API has some quirks ..
if time() > (extra_param_ts + extra_param_expiration_delay):
fetch_extra_param(query_args, params['headers'])
query_args['format'] = 'json'
params['url'] = base_url + search_path + urlencode(query_args) + extra_param
return params
# get response from search-request
def response(resp):
results = []
response_json = loads(resp.text)
# logger.debug('gigablast returns %s results', len(response_json['results']))
for result in response_json['results']:
# see "Example JSON Output (&format=json)"
# at http://www.gigablast.com/api.html#/search
# sort out meaningless result
title = result.get('title')
if len(title) < 2:
continue
url = result.get('url')
if len(url) < 9:
continue
content = result.get('sum')
if len(content) < 5:
continue
# extend fields
subtitle = result.get('title')
if len(subtitle) > 3 and subtitle != title:
title += " - " + subtitle
results.append(dict(url=url, title=title, content=content))
return results

View file

@ -15,7 +15,7 @@ about = {
"results": "JSON",
}
categories = ["others"]
categories = ["weather"]
url = "https://wttr.in/{query}?format=j1&lang={lang}"

View file

@ -17,7 +17,7 @@ from searx.enginelib import Engine
from searx.plugins import Plugin
from searx.locales import LOCALE_NAMES
from searx.webutils import VALID_LANGUAGE_CODE
from searx.engines import OTHER_CATEGORY
from searx.engines import DEFAULT_CATEGORY
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
@ -259,7 +259,7 @@ class EnginesSetting(BooleanChoices):
choices = {}
for engine in engines:
for category in engine.categories:
if not category in list(settings['categories_as_tabs'].keys()) + [OTHER_CATEGORY]:
if not category in list(settings['categories_as_tabs'].keys()) + [DEFAULT_CATEGORY]:
continue
choices['{}__{}'.format(engine.name, category)] = not engine.disabled
super().__init__(default_value, choices)

View file

@ -15,8 +15,8 @@ __all__ = [
CONSTANT_NAMES = {
# Constants defined in other modules
'DEFAULT_GROUP_NAME': webutils.DEFAULT_GROUP_NAME,
'OTHER_CATEGORY': engines.OTHER_CATEGORY,
'NO_SUBGROUPING': webutils.NO_SUBGROUPING,
'DEFAULT_CATEGORY': engines.DEFAULT_CATEGORY,
}
CATEGORY_NAMES = {

View file

@ -670,17 +670,6 @@ engines:
engine: genius
shortcut: gen
- name: gigablast
engine: gigablast
shortcut: gb
timeout: 4.0
# API key required, see https://gigablast.com/searchfeed.html
# gb_userid: unset
# gb_code: unknown
disabled: true
additional_tests:
rosebud: *test_rosebud
- name: gentoo
engine: gentoo
shortcut: ge

View file

@ -74,6 +74,10 @@ def update_settings(default_settings, user_settings):
else:
default_settings[k] = v
categories_as_tabs = user_settings.get('categories_as_tabs')
if categories_as_tabs:
default_settings['categories_as_tabs'] = categories_as_tabs
# parse the engines
remove_engines = None
keep_only_engines = None

View file

@ -423,8 +423,11 @@ svg.leaflet-image-layer.leaflet-interactive path {
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-control-attribution svg {
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
@ -438,12 +441,10 @@ svg.leaflet-image-layer.leaflet-interactive path {
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
@ -537,8 +538,6 @@ svg.leaflet-image-layer.leaflet-interactive path {
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
@ -652,6 +651,6 @@ svg.leaflet-image-layer.leaflet-interactive path {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
color-adjust: exact;
print-color-adjust: exact;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"version":3,"file":"searxng.head.min.js","sources":["../src/js/head/00_init.js"],"sourcesContent":["/* SPDX-License-Identifier: AGPL-3.0-or-later */\n(function (w, d) {\n 'use strict';\n\n // add data- properties\n var script = d.currentScript || (function () {\n var scripts = d.getElementsByTagName('script');\n return scripts[scripts.length - 1];\n })();\n\n w.searxng = {\n settings: JSON.parse(atob(script.getAttribute('client_settings')))\n };\n\n // update the css\n var hmtlElement = d.getElementsByTagName(\"html\")[0];\n hmtlElement.classList.remove('no-js');\n hmtlElement.classList.add('js');\n\n})(window, document);\n"],"names":["w","d","script","currentScript","scripts","getElementsByTagName","length","searxng","settings","JSON","parse","atob","getAttribute","hmtlElement","classList","remove","add","window","document"],"mappings":"CACA,SAAWA,EAAGC,gBAIZ,IAAIC,EAASD,EAAEE,eAAkB,WAC/B,IAAIC,EAAUH,EAAEI,qBAAqB,UACrC,OAAOD,EAAQA,EAAQE,OAAS,GAFD,GAKjCN,EAAEO,QAAU,CACVC,SAAUC,KAAKC,MAAMC,KAAKT,EAAOU,aAAa,sBAIhD,IAAIC,EAAcZ,EAAEI,qBAAqB,QAAQ,GACjDQ,EAAYC,UAAUC,OAAO,SAC7BF,EAAYC,UAAUE,IAAI,OAhB5B,CAkBGC,OAAQC"}
{"version":3,"file":"searxng.head.min.js","sources":["../src/js/head/00_init.js"],"sourcesContent":["/* SPDX-License-Identifier: AGPL-3.0-or-later */\n(function (w, d) {\n 'use strict';\n\n // add data- properties\n var script = d.currentScript || (function () {\n var scripts = d.getElementsByTagName('script');\n return scripts[scripts.length - 1];\n })();\n\n w.searxng = {\n settings: JSON.parse(atob(script.getAttribute('client_settings')))\n };\n\n // update the css\n var hmtlElement = d.getElementsByTagName(\"html\")[0];\n hmtlElement.classList.remove('no-js');\n hmtlElement.classList.add('js');\n\n})(window, document);\n"],"names":["w","d","script","currentScript","scripts","getElementsByTagName","length","searxng","settings","JSON","parse","atob","getAttribute","hmtlElement","classList","remove","add","window","document"],"mappings":"CACA,SAAWA,EAAGC,GACZ,aAGA,IAAIC,EAASD,EAAEE,eAAkB,WAC/B,IAAIC,EAAUH,EAAEI,qBAAqB,QAAQ,EAC7C,OAAOD,EAAQA,EAAQE,OAAS,EACjC,EAAE,EAEHN,EAAEO,QAAU,CACVC,SAAUC,KAAKC,MAAMC,KAAKT,EAAOU,aAAa,iBAAiB,CAAC,CAAC,CACnE,EAGA,IAAIC,EAAcZ,EAAEI,qBAAqB,MAAM,EAAE,GACjDQ,EAAYC,UAAUC,OAAO,OAAO,EACpCF,EAAYC,UAAUE,IAAI,IAAI,CAE/B,GAAEC,OAAQC,QAAQ"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -65,6 +65,39 @@
}
});
},
_Select: function (item) {
AutoComplete.defaults._Select.call(this, item);
var form = item.closest('form');
if (form) {
form.submit();
}
},
KeyboardMappings: Object.assign({}, AutoComplete.defaults.KeyboardMappings, {
"KeyUpAndDown_up": Object.assign({}, AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up, {
Callback: function (event) {
AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up.Callback.call(this, event);
var liActive = this.DOMResults.querySelector("li.active");
if (liActive) {
AutoComplete.defaults._Select.call(this, liActive);
}
},
}),
"Tab": Object.assign({}, AutoComplete.defaults.KeyboardMappings.Enter, {
Conditions: [{
Is: 9,
Not: false
}],
Callback: function (event) {
if (this.DOMResults.getAttribute("class").indexOf("open") != -1) {
var liActive = this.DOMResults.querySelector("li.active");
if (liActive !== null) {
AutoComplete.defaults._Select.call(this, liActive);
event.preventDefault();
}
}
},
})
}),
}, "#" + qinput_id);
}

View file

@ -113,8 +113,8 @@
--color-toolkit-engine-tooltip-background: #fff;
--color-toolkit-loader-border: rgba(0, 0, 0, 0.2);
--color-toolkit-loader-borderleft: rgba(255, 255, 255, 0);
--color-doc-code: #300;
--color-doc-code-background: #fdd;
--color-doc-code: #003;
--color-doc-code-background: #ddeaff;
}
.dark-themes() {
@ -225,8 +225,8 @@
--color-toolkit-engine-tooltip-background: #222;
--color-toolkit-loader-border: rgba(255, 255, 255, 0.2);
--color-toolkit-loader-borderleft: rgba(0, 0, 0, 0);
--color-doc-code: #fdd;
--color-doc-code-background: #300;
--color-doc-code: #ddd;
--color-doc-code-background: #4d5a6f;
}
/// Dark Theme (autoswitch based on device pref)

View file

@ -1,9 +1,10 @@
.info-page {
code {
font-family: monospace;
color: var(--color-doc-code);
.rounded-corners-tiny;
background-color: var(--color-doc-code-background);
padding: 2px 5px;
.rounded-corners(5px);
color: var(--color-doc-code);
padding: 0.2rem;
border: 0 none;
}
}

View file

@ -72,6 +72,15 @@
font-size: 90%;
}
.bang {
.ltr-text-align-left();
.rounded-corners-tiny;
background-color: var(--color-doc-code-background);
color: var(--color-doc-code);
padding: 0.2rem;
border: 0 none;
}
table {
border-collapse: collapse;
}
@ -114,6 +123,10 @@
.engine-description {
margin-top: 0.5rem;
}
.bang {
margin: 0.3rem;
}
}
}

View file

@ -13,7 +13,7 @@
} -%}
<div id="categories" class="search_categories">{{- '' -}}
<div id="categories_container">
{%- for category in categories_as_tabs -%}
{%- for category in categories -%}
<div class="category"><input type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}"{% if category in selected_categories %} checked="checked"{% endif %}/>
<label for="checkbox_{{ category|replace(' ', '_') }}" class="tooltips">
{{- icon_big(category_icons[category]) if category in category_icons else icon_big('globe-outline') -}}

View file

@ -32,7 +32,8 @@
{{ _('View error logs and submit a bug report') -}}
</a>
{%- endif -%}
<p><span class="right">{{ _("!bang for this engine") }}</span>{% for bang in [search_engine.name] + [search_engine.shortcut] %}<span class="bang"> {{ '!' + bang.replace(' ', '_') }}</span>{% endfor %}</p>
<p><span class="right">{{ _("!bang for its categories") }}</span>{% for bang in search_engine.categories %}<span class="bang"> {{ '!' + bang.replace(' ', '_') }}</span>{% endfor %}</p>
</div>
{%- endif -%}
{%- endmacro %}
@ -298,18 +299,18 @@
<p>{{ _('Currently used search engines') }}</p>
{{ tabs_open() }}
{% set ns = namespace(checked=true) %}
{% for categ in categories_as_tabs + [OTHER_CATEGORY] %}
{% for categ in categories_as_tabs + [DEFAULT_CATEGORY] %}
{{ tab_header('enginetab', 'category' + categ, _(categ), ns.checked )}}
{% set ns.checked = false %}
{% if categ == OTHER_CATEGORY %}
<p>{{_('This tab does not show up for search results, but you can search the engines listed here via bangs.')}}</p>
{% if categ == DEFAULT_CATEGORY %}
<p>{{_('This tab dues not exists in the user interface, but you can search in these engines by its !bangs.')}} <a href="{{ url_for('info', pagename='search-syntax') }}">&#9432;</a></p>
{% endif %}
<div class="scrollx">
<table class="striped table_engines">
<tr>{{- "" -}}
<th class="engine_checkbox">{{ _("Allow") }}</th>{{- "" -}}
<th class="name">{{ _("Engine name") }}</th>{{- "" -}}
<th class="shortcut">{{ _("Shortcut") }}</th>{{- "" -}}
<th class="shortcut">{{ _("Bang") }}</th>{{- "" -}}
<th>{{ _("Supports selected language") }}</th>{{- "" -}}
<th>{{ _("SafeSearch") }}</th>{{- "" -}}
<th>{{ _("Time range") }}</th>{{- "" -}}
@ -317,9 +318,12 @@
<th>{{ _("Max time") }}</th>{{- "" -}}
{%- if enable_metrics %}<th>{{ _("Reliability") }}</th>{% endif -%}
</tr>
{% for group, engines in engines_by_category[categ] | group_engines_in_tab %}
{% for group, group_bang, engines in engines_by_category[categ] | group_engines_in_tab %}
{% if loop.length > 1 %}
<tr><th colspan="9" class="engine-group">{{_(group)}}</th></tr>
<tr>
<th class="engine-group" colspan="2">{{_(group)}}</th>
<th class="engine-group" colspan="7">{% if group_bang %}<span class="bang">{{group_bang}}</span>{% endif %}</th>
</tr>{{- "" -}}
{% endif %}
{% for search_engine in engines %}
{% if not search_engine.private %}
@ -329,13 +333,13 @@
<th class="name" data-engine-name="{{ search_engine.name }}">{% if search_engine.enable_http %}{{ icon_big('warning', 'No HTTPS') }}{% endif -%}
<label for="{{ engine_id }}">
{{- search_engine.name -}}
{%- if search_engine.about and search_engine.about.language -%}
{%- if search_engine.about and search_engine.about.language %}
({{search_engine.about.language | upper}})
{%- endif -%}
</label>
{{- engine_about(search_engine) -}}
</th>{{- "" -}}
<td class="shortcut">{{ shortcuts[search_engine.name] }}</td>{{- "" -}}
<td class="shortcut"><span class="bang">{{ '!' + shortcuts[search_engine.name] }}</span></td>{{- "" -}}
<td>{{ checkbox(None, supports[search_engine.name]['supports_selected_language'], true) }}</td>{{- "" -}}
<td>{{ checkbox(None, supports[search_engine.name]['safesearch'], true) }}</td>{{- "" -}}
<td>{{ checkbox(None, supports[search_engine.name]['time_range_support'], true) }}</td>{{- "" -}}

View file

@ -10,20 +10,22 @@
# Kuka Manó <dumpster@perjel.hu>, 2022.
# Markus Heiser <markus.heiser@darmarit.de>, 2022, 2023.
# Lakatos Tamás <tomimost@gmail.com>, 2022.
# return42 <markus.heiser@darmarit.de>, 2023.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-02-20 11:22+0000\n"
"PO-Revision-Date: 2023-01-06 07:14+0000\n"
"Last-Translator: Markus Heiser <markus.heiser@darmarit.de>\n"
"PO-Revision-Date: 2023-04-03 06:37+0000\n"
"Last-Translator: return42 <markus.heiser@darmarit.de>\n"
"Language-Team: Hungarian <https://translate.codeberg.org/projects/searxng/"
"searxng/hu/>\n"
"Language: hu\n"
"Language-Team: Hungarian "
"<https://weblate.bubu1.eu/projects/searxng/searxng/hu/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.16.4\n"
"Generated-By: Babel 2.11.0\n"
#. CONSTANT_NAMES['DEFAULT_GROUP_NAME']
@ -381,18 +383,23 @@ msgid ""
"This plugin checks if the address of the request is a Tor exit-node, and "
"informs the user if it is; like check.torproject.org, but from SearXNG."
msgstr ""
"Ez a beépülő modul ellenőrzi, hogy a kérés címe Tor kilépési csomópont-e, és "
"értesíti a felhasználót, ha igen; mint a check.torproject.org, de a SearXNG-"
"től."
#: searx/plugins/tor_check.py:62
msgid ""
"Could not download the list of Tor exit-nodes from: "
"https://check.torproject.org/exit-addresses"
msgstr ""
"Nem sikerült letölteni a Tor kilépési csomópontok listáját innen: "
"https://check.torproject.org/exit-addresses"
#: searx/plugins/tor_check.py:78
msgid ""
"You are using Tor and it looks like you have this external IP address: "
"{ip_address}"
msgstr ""
msgstr "Ön Tor-t használ, és úgy tűnik, ez a külső IP-címe: {ip_address}"
#: searx/plugins/tor_check.py:86
msgid "You are not using Tor and you have this external IP address: {ip_address}"
@ -1541,4 +1548,3 @@ msgstr "videó elrejtése"
#~ msgid "Automatically detect the query search language and switch to it."
#~ msgstr ""

View file

@ -11,7 +11,7 @@ msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-02-20 11:22+0000\n"
"PO-Revision-Date: 2023-03-30 12:37+0000\n"
"PO-Revision-Date: 2023-04-03 06:37+0000\n"
"Last-Translator: return42 <markus.heiser@darmarit.de>\n"
"Language-Team: Norwegian Bokmål <https://translate.codeberg.org/projects/"
"searxng/searxng/nb_NO/>\n"
@ -575,7 +575,7 @@ msgstr "Forvalgt språk"
#: searx/templates/simple/filters/languages.html:4
#: searx/templates/simple/preferences.html:119
msgid "Auto-detect"
msgstr ""
msgstr "Sug meg"
#: searx/templates/simple/preferences.html:126
msgid "What language do you prefer for search?"

View file

@ -14,13 +14,14 @@
# Peter Martin <weblate@pe7er.com>, 2022.
# Max Westen <max@maxwesten.nl>, 2023.
# gjveld <gjveld@gmail.com>, 2023.
# Vistaus <vistausss@fastmail.com>, 2023.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-02-20 11:22+0000\n"
"PO-Revision-Date: 2023-02-28 22:04+0000\n"
"Last-Translator: gjveld <gjveld@gmail.com>\n"
"PO-Revision-Date: 2023-04-01 14:37+0000\n"
"Last-Translator: Vistaus <vistausss@fastmail.com>\n"
"Language-Team: Dutch <https://translate.codeberg.org/projects/searxng/"
"searxng/nl/>\n"
"Language: nl\n"
@ -28,7 +29,7 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.15.2\n"
"X-Generator: Weblate 4.16.4\n"
"Generated-By: Babel 2.11.0\n"
#. CONSTANT_NAMES['DEFAULT_GROUP_NAME']
@ -74,7 +75,7 @@ msgstr "videos"
#. CATEGORY_NAMES['IT']
#: searx/searxng.msg
msgid "it"
msgstr "IT"
msgstr "it"
#. CATEGORY_NAMES['NEWS']
#: searx/searxng.msg
@ -89,7 +90,7 @@ msgstr "kaart"
#. CATEGORY_NAMES['ONIONS']
#: searx/searxng.msg
msgid "onions"
msgstr "uien"
msgstr "onions"
#. CATEGORY_NAMES['SCIENCE']
#: searx/searxng.msg
@ -99,7 +100,7 @@ msgstr "wetenschap"
#. CATEGORY_GROUPS['APPS']
#: searx/searxng.msg
msgid "apps"
msgstr "Apps"
msgstr "apps"
#. CATEGORY_GROUPS['DICTIONARIES']
#: searx/searxng.msg
@ -119,12 +120,12 @@ msgstr "paketten"
#. CATEGORY_GROUPS['Q_A']
#: searx/searxng.msg
msgid "q&a"
msgstr "vraag&antwoord"
msgstr "vraag en antwoord"
#. CATEGORY_GROUPS['REPOS']
#: searx/searxng.msg
msgid "repos"
msgstr "repos"
msgstr "repo's"
#. CATEGORY_GROUPS['SOFTWARE_WIKIS']
#: searx/searxng.msg
@ -144,7 +145,7 @@ msgstr "wetenschapelijke publicaties"
#. STYLE_NAMES['AUTO']
#: searx/searxng.msg
msgid "auto"
msgstr "auto"
msgstr "automatisch"
#. STYLE_NAMES['LIGHT']
#: searx/searxng.msg
@ -162,11 +163,11 @@ msgstr "verlopen"
#: searx/webapp.py:166
msgid "parsing error"
msgstr "fout bij ontleden"
msgstr "verwerkingsfout"
#: searx/webapp.py:167
msgid "HTTP protocol error"
msgstr "HTTP-protocolfout"
msgstr "Http-protocolfout"
#: searx/webapp.py:168
msgid "network error"
@ -174,31 +175,31 @@ msgstr "netwerkfout"
#: searx/webapp.py:169
msgid "SSL error: certificate validation has failed"
msgstr "SSL-fout: certificaatvalidatie is mislukt"
msgstr "Ssl-fout: de certificaatvalidatie is mislukt"
#: searx/webapp.py:171
msgid "unexpected crash"
msgstr "onverwacht falen"
msgstr "onverwachte crash"
#: searx/webapp.py:178
msgid "HTTP error"
msgstr "HTTP-fout"
msgstr "Http-fout"
#: searx/webapp.py:179
msgid "HTTP connection error"
msgstr "HTTP-verbindingsfout"
msgstr "Http-verbindingsfout"
#: searx/webapp.py:185
msgid "proxy error"
msgstr "proxy-fout"
msgstr "proxyfout"
#: searx/webapp.py:186
msgid "CAPTCHA"
msgstr "CAPTCHA"
msgstr "Captcha"
#: searx/webapp.py:187
msgid "too many requests"
msgstr "te veel aanvragen"
msgstr "teveel verzoeken"
#: searx/webapp.py:188
msgid "access denied"
@ -206,11 +207,11 @@ msgstr "toegang geweigerd"
#: searx/webapp.py:189
msgid "server API error"
msgstr "server-API-fout"
msgstr "server-api-fout"
#: searx/webapp.py:368
msgid "No item found"
msgstr "Geen resultaat gevonden"
msgstr "Er is geen resultaat gevonden"
#: searx/engines/qwant.py:217
#: searx/templates/simple/result_templates/images.html:20 searx/webapp.py:370
@ -219,11 +220,11 @@ msgstr "Bron"
#: searx/webapp.py:372
msgid "Error loading the next page"
msgstr "Fout bij het laden van de volgende pagina"
msgstr "De volgende pagina kan niet worden geladen"
#: searx/webapp.py:524 searx/webapp.py:964
msgid "Invalid settings, please edit your preferences"
msgstr "Ongeldige instellingen, gelieve je voorkeuren bij te werken"
msgstr "De instellingen zijn ongeldig - werk ze bij"
#: searx/webapp.py:540
msgid "Invalid settings"
@ -247,7 +248,7 @@ msgstr "{hours} uur, {minutes} minu(u)t(en) geleden"
#: searx/answerers/random/answerer.py:67
msgid "Random value generator"
msgstr "Generator van willekeurige waarden"
msgstr "Willekeurigewaardegenerator"
#: searx/answerers/random/answerer.py:68
msgid "Generate different random values"
@ -259,7 +260,7 @@ msgstr "Statistische functies"
#: searx/answerers/statistics/answerer.py:48
msgid "Compute {functions} of the arguments"
msgstr "Bereken {functions} van de argumenten"
msgstr "Bereken {functions} van de opties"
#: searx/engines/openstreetmap.py:160
msgid "Get directions"
@ -281,7 +282,9 @@ msgstr "Kanaal"
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
"{lastCitationVelocityYear}"
msgstr "citaties van dit jaar"
msgstr ""
"{numCitations} citaties van {firstCitationVelocityYear} tot "
"{lastCitationVelocityYear}"
#: searx/engines/tineye.py:40
msgid ""

View file

@ -63,7 +63,7 @@ from searx.settings_defaults import OUTPUT_FORMATS
from searx.settings_loader import get_default_settings_path
from searx.exceptions import SearxParameterException
from searx.engines import (
OTHER_CATEGORY,
DEFAULT_CATEGORY,
categories,
engines,
engine_shortcuts,
@ -373,16 +373,15 @@ def get_translations():
}
def _get_enable_categories(all_categories: Iterable[str]):
disabled_engines = request.preferences.engines.get_disabled()
enabled_categories = set(
# pylint: disable=consider-using-dict-items
category
for engine_name in engines
for category in engines[engine_name].categories
if (engine_name, category) not in disabled_engines
)
return [x for x in all_categories if x in enabled_categories]
def get_enabled_categories(category_names: Iterable[str]):
"""The categories in ``category_names```for which there is no active engine
are filtered out and a reduced list is returned."""
enabled_engines = [item[0] for item in request.preferences.engines.get_enabled()]
enabled_categories = set()
for engine_name in enabled_engines:
enabled_categories.update(engines[engine_name].categories)
return [x for x in category_names if x in enabled_categories]
def get_pretty_url(parsed_url: urllib.parse.ParseResult):
@ -434,8 +433,8 @@ def render(template_name: str, **kwargs):
kwargs['theme'] = request.preferences.get_value('theme')
kwargs['method'] = request.preferences.get_value('method')
kwargs['categories_as_tabs'] = list(settings['categories_as_tabs'].keys())
kwargs['categories'] = _get_enable_categories(categories.keys())
kwargs['OTHER_CATEGORY'] = OTHER_CATEGORY
kwargs['categories'] = get_enabled_categories(settings['categories_as_tabs'].keys())
kwargs['DEFAULT_CATEGORY'] = DEFAULT_CATEGORY
# i18n
kwargs['sxng_locales'] = [l for l in sxng_locales if l[0] in settings['search']['languages']]

View file

@ -18,7 +18,7 @@ from codecs import getincrementalencoder
from flask_babel import gettext, format_date
from searx import logger, settings
from searx.engines import OTHER_CATEGORY
from searx.engines import DEFAULT_CATEGORY
if TYPE_CHECKING:
from searx.enginelib import Engine
@ -222,26 +222,29 @@ def is_flask_run_cmdline():
return frames[-2].filename.endswith('flask/cli.py')
DEFAULT_GROUP_NAME = 'others'
NO_SUBGROUPING = 'without further subgrouping'
def group_engines_in_tab(engines: Iterable[Engine]) -> List[Tuple[str, Iterable[Engine]]]:
"""Groups an Iterable of engines by their first non tab category"""
"""Groups an Iterable of engines by their first non tab category (first subgroup)"""
def get_group(eng):
non_tab_categories = [
c for c in eng.categories if c not in list(settings['categories_as_tabs'].keys()) + [OTHER_CATEGORY]
]
return non_tab_categories[0] if len(non_tab_categories) > 0 else DEFAULT_GROUP_NAME
groups = itertools.groupby(sorted(engines, key=get_group), get_group)
def get_subgroup(eng):
non_tab_categories = [c for c in eng.categories if c not in tabs + [DEFAULT_CATEGORY]]
return non_tab_categories[0] if len(non_tab_categories) > 0 else NO_SUBGROUPING
def group_sort_key(group):
return (group[0] == DEFAULT_GROUP_NAME, group[0].lower())
sorted_groups = sorted(((name, list(engines)) for name, engines in groups), key=group_sort_key)
return (group[0] == NO_SUBGROUPING, group[0].lower())
def engine_sort_key(engine):
return (engine.about.get('language', ''), engine.name)
return [(groupname, sorted(engines, key=engine_sort_key)) for groupname, engines in sorted_groups]
tabs = list(settings['categories_as_tabs'].keys())
subgroups = itertools.groupby(sorted(engines, key=get_subgroup), get_subgroup)
sorted_groups = sorted(((name, list(engines)) for name, engines in subgroups), key=group_sort_key)
ret_val = []
for groupname, engines in sorted_groups:
group_bang = '!' + groupname.replace(' ', '_') if groupname != NO_SUBGROUPING else ''
ret_val.append((groupname, group_bang, sorted(engines, key=engine_sort_key)))
return ret_val