forked from zaclys/searxng
Merge pull request #634 from not-my-profile/powered-by
Introduce `categories_as_tabs` & group engines in tabs
This commit is contained in:
commit
aedd6279b3
@ -16,11 +16,18 @@ Explanation of the :ref:`general engine configuration` shown in the table
|
||||
|
||||
SearXNG supports {{engines | length}} search engines (of which {{enabled_engine_count}} are enabled by default).
|
||||
|
||||
{% for category, engines in engines.items() | groupby('1.categories.0') %}
|
||||
{% for category, engines in categories_as_tabs.items() %}
|
||||
|
||||
{{category}} search engines
|
||||
---------------------------------------
|
||||
|
||||
{% for group, engines in engines | group_engines_in_tab %}
|
||||
|
||||
{% if loop.length > 1 %}
|
||||
{{group}}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{% endif %}
|
||||
|
||||
.. flat-table::
|
||||
:header-rows: 2
|
||||
:stub-columns: 1
|
||||
@ -39,9 +46,9 @@ Explanation of the :ref:`general engine configuration` shown in the table
|
||||
- Safe search
|
||||
- Time range
|
||||
|
||||
{% for name, mod in engines | sort_engines %}
|
||||
{% for mod in engines %}
|
||||
|
||||
* - `{{name}} <{{mod.about and mod.about.website}}>`_
|
||||
* - `{{mod.name}} <{{mod.about and mod.about.website}}>`_
|
||||
- ``!{{mod.shortcut}}``
|
||||
- {%- if 'searx.engines.' + mod.__name__ in documented_modules %}
|
||||
:py:mod:`~searx.engines.{{mod.__name__}}`
|
||||
@ -65,3 +72,4 @@ Explanation of the :ref:`general engine configuration` shown in the table
|
||||
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
@ -222,6 +222,26 @@ Communication with search engines.
|
||||
``max_redirects`` :
|
||||
30 by default. Maximum redirect before it is an error.
|
||||
|
||||
``categories_as_tabs:``
|
||||
-----------------------
|
||||
|
||||
A list of the categories that are displayed as tabs in the user interface.
|
||||
Categories not listed here can still be searched with the :ref:`search-syntax`.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
categories_as_tabs:
|
||||
general:
|
||||
images:
|
||||
videos:
|
||||
news:
|
||||
map:
|
||||
music:
|
||||
it:
|
||||
science:
|
||||
files:
|
||||
social media:
|
||||
|
||||
.. _settings engine:
|
||||
|
||||
Engine settings
|
||||
|
10
docs/conf.py
10
docs/conf.py
@ -39,7 +39,9 @@ exclude_patterns = ['build-templates/*.rst']
|
||||
|
||||
import searx.engines
|
||||
import searx.plugins
|
||||
import searx.webutils
|
||||
searx.engines.load_engines(searx.settings['engines'])
|
||||
|
||||
jinja_contexts = {
|
||||
'searx': {
|
||||
'engines': searx.engines.engines,
|
||||
@ -48,14 +50,12 @@ jinja_contexts = {
|
||||
'node': os.getenv('NODE_MINIMUM_VERSION')
|
||||
},
|
||||
'enabled_engine_count': sum(not x.disabled for x in searx.engines.engines.values()),
|
||||
'categories': searx.engines.categories,
|
||||
'categories_as_tabs': {c: searx.engines.categories[c] for c in searx.settings['categories_as_tabs']},
|
||||
},
|
||||
}
|
||||
jinja_filters = {
|
||||
'sort_engines':
|
||||
lambda engines: sorted(
|
||||
engines,
|
||||
key=lambda engine: (engine[1].disabled, engine[1].about.get('language', ''), engine[0])
|
||||
)
|
||||
'group_engines_in_tab': searx.webutils.group_engines_in_tab,
|
||||
}
|
||||
|
||||
# Let the Jinja template in configured_engines.rst access documented_modules
|
||||
|
@ -13,6 +13,7 @@ usage::
|
||||
|
||||
import sys
|
||||
import copy
|
||||
from typing import List
|
||||
|
||||
from os.path import realpath, dirname
|
||||
from babel.localedata import locale_identifiers
|
||||
@ -44,7 +45,29 @@ ENGINE_DEFAULT_ARGS = {
|
||||
"display_error_messages": True,
|
||||
"tokens": [],
|
||||
}
|
||||
"""Defaults for the namespace of an engine module, see :py:func:`load_engine`"""
|
||||
# set automatically when an engine does not have any tab category
|
||||
OTHER_CATEGORY = 'other'
|
||||
|
||||
|
||||
class Engine: # pylint: disable=too-few-public-methods
|
||||
"""This class is currently never initialized and only used for type hinting."""
|
||||
|
||||
name: str
|
||||
engine: str
|
||||
shortcut: str
|
||||
categories: List[str]
|
||||
supported_languages: List[str]
|
||||
about: dict
|
||||
inactive: bool
|
||||
disabled: bool
|
||||
language_support: bool
|
||||
paging: bool
|
||||
safesearch: bool
|
||||
time_range_support: bool
|
||||
timeout: float
|
||||
|
||||
|
||||
# Defaults for the namespace of an engine module, see :py:func:`load_engine``
|
||||
|
||||
categories = {'general': []}
|
||||
engines = {}
|
||||
@ -113,6 +136,9 @@ def load_engine(engine_data):
|
||||
|
||||
set_loggers(engine, engine_name)
|
||||
|
||||
if not any(cat in settings['categories_as_tabs'] for cat in engine.categories):
|
||||
engine.categories.append(OTHER_CATEGORY)
|
||||
|
||||
return engine
|
||||
|
||||
|
||||
@ -138,6 +164,8 @@ def update_engine_attributes(engine, engine_data):
|
||||
if isinstance(param_value, str):
|
||||
param_value = list(map(str.strip, param_value.split(',')))
|
||||
engine.categories = param_value
|
||||
elif hasattr(engine, 'about') and param_name == 'about':
|
||||
engine.about = {**engine.about, **engine_data['about']}
|
||||
else:
|
||||
setattr(engine, param_name, param_value)
|
||||
|
||||
|
@ -24,7 +24,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['files']
|
||||
categories = ['files', 'apps']
|
||||
paging = True
|
||||
time_range_support = False
|
||||
|
||||
|
@ -20,7 +20,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['it']
|
||||
categories = ['it', 'software wikis']
|
||||
paging = True
|
||||
base_url = 'https://wiki.archlinux.org'
|
||||
|
||||
|
@ -20,7 +20,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
paging = True
|
||||
time_range_support = False
|
||||
safesearch = False
|
||||
|
@ -27,7 +27,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['images']
|
||||
categories = ['images', 'web']
|
||||
paging = True
|
||||
safesearch = True
|
||||
time_range_support = True
|
||||
|
@ -26,7 +26,7 @@ about = {
|
||||
"results": 'HTML',
|
||||
}
|
||||
|
||||
categories = ['videos']
|
||||
categories = ['videos', 'web']
|
||||
paging = True
|
||||
safesearch = True
|
||||
time_range_support = True
|
||||
|
@ -27,7 +27,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
paging = True
|
||||
supported_languages_url = 'https://duckduckgo.com/util/u588.js'
|
||||
time_range_support = True
|
||||
|
@ -27,7 +27,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['images']
|
||||
categories = ['images', 'web']
|
||||
paging = True
|
||||
safesearch = True
|
||||
|
||||
|
@ -19,7 +19,7 @@ about = {
|
||||
"language": 'de',
|
||||
}
|
||||
|
||||
categories = ['general']
|
||||
categories = ['dictionaries']
|
||||
paging = True
|
||||
|
||||
# search-url
|
||||
|
@ -17,7 +17,7 @@ about = {
|
||||
"results": 'HTML',
|
||||
}
|
||||
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
paging = False
|
||||
safesearch = True
|
||||
|
||||
|
@ -18,7 +18,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['files']
|
||||
categories = ['files', 'apps']
|
||||
paging = True
|
||||
|
||||
# search-url
|
||||
|
@ -20,7 +20,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['music']
|
||||
categories = ['music', 'lyrics']
|
||||
paging = True
|
||||
page_size = 5
|
||||
|
||||
|
@ -18,7 +18,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['it']
|
||||
categories = ['it', 'software wikis']
|
||||
paging = True
|
||||
base_url = 'https://wiki.gentoo.org'
|
||||
|
||||
|
@ -22,7 +22,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
# gigablast's pagination is totally damaged, don't use it
|
||||
paging = False
|
||||
safesearch = True
|
||||
|
@ -17,7 +17,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['it']
|
||||
categories = ['it', 'repos']
|
||||
|
||||
# search-url
|
||||
search_url = 'https://api.github.com/search/repositories?sort=stars&order=desc&{query}' # noqa
|
||||
|
@ -41,7 +41,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
paging = True
|
||||
time_range_support = True
|
||||
safesearch = True
|
||||
|
@ -45,7 +45,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['images']
|
||||
categories = ['images', 'web']
|
||||
paging = False
|
||||
use_locale_domain = True
|
||||
time_range_support = True
|
||||
|
@ -54,7 +54,7 @@ about = {
|
||||
|
||||
# engine dependent config
|
||||
|
||||
categories = ['videos']
|
||||
categories = ['videos', 'web']
|
||||
paging = False
|
||||
language_support = True
|
||||
use_locale_domain = True
|
||||
|
@ -27,9 +27,7 @@ about = {
|
||||
"results": 'HTML',
|
||||
}
|
||||
|
||||
categories = [
|
||||
'general',
|
||||
]
|
||||
categories = []
|
||||
paging = False
|
||||
|
||||
# suggestion_url = "https://sg.media-imdb.com/suggestion/{letter}/{query}.json"
|
||||
|
@ -25,6 +25,7 @@ about = {
|
||||
"language": "cz",
|
||||
}
|
||||
|
||||
categories = ['general', 'web']
|
||||
base_url = 'https://search.seznam.cz/'
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ about = {
|
||||
"language": 'pl',
|
||||
}
|
||||
|
||||
categories = ['general']
|
||||
categories = ['dictionaries']
|
||||
paging = False
|
||||
|
||||
URL = 'https://sjp.pwn.pl'
|
||||
|
@ -23,7 +23,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
# there is a mechanism to block "bot" search
|
||||
# (probably the parameter qid), require
|
||||
# storing of qid's between mulitble search-calls
|
||||
|
@ -14,7 +14,7 @@ about = {
|
||||
}
|
||||
|
||||
engine_type = 'online_dictionary'
|
||||
categories = ['general']
|
||||
categories = ['dictionaries']
|
||||
url = 'https://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}'
|
||||
web_url = 'https://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}'
|
||||
weight = 100
|
||||
|
@ -31,7 +31,7 @@ about = {
|
||||
}
|
||||
|
||||
# engine dependent config
|
||||
categories = ['general']
|
||||
categories = ['general', 'web']
|
||||
paging = True
|
||||
time_range_support = True
|
||||
supported_languages_url = 'https://search.yahoo.com/preferences/languages'
|
||||
|
@ -12,6 +12,7 @@ from urllib.parse import parse_qs, urlencode
|
||||
from searx import settings, autocomplete
|
||||
from searx.locales import LOCALE_NAMES
|
||||
from searx.webutils import VALID_LANGUAGE_CODE
|
||||
from searx.engines import OTHER_CATEGORY
|
||||
|
||||
|
||||
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
|
||||
@ -271,6 +272,8 @@ class EnginesSetting(SwitchableSetting):
|
||||
transformed_choices = []
|
||||
for engine_name, engine in self.choices.items(): # pylint: disable=no-member,access-member-before-definition
|
||||
for category in engine.categories:
|
||||
if not category in list(settings['categories_as_tabs'].keys()) + [OTHER_CATEGORY]:
|
||||
continue
|
||||
transformed_choice = {}
|
||||
transformed_choice['default_on'] = not engine.disabled
|
||||
transformed_choice['id'] = '{}__{}'.format(engine_name, category)
|
||||
|
@ -222,7 +222,7 @@ class BangParser(QueryPartParser):
|
||||
# check if query starts with categorie name
|
||||
for category in categories:
|
||||
if category.startswith(value):
|
||||
self._add_autocomplete(first_char + category)
|
||||
self._add_autocomplete(first_char + category.replace(' ', '_'))
|
||||
|
||||
# check if query starts with engine name
|
||||
for engine in engines:
|
||||
|
@ -82,12 +82,6 @@ ui:
|
||||
simple_style: auto
|
||||
# Open result links in a new tab by default
|
||||
# results_on_new_tab: false
|
||||
# categories_order :
|
||||
# - general
|
||||
# - files
|
||||
# - map
|
||||
# - it
|
||||
# - science
|
||||
|
||||
# Lock arbitrary settings on the preferences page. To find the ID of the user
|
||||
# setting you want to lock, check the ID of the form on the page "preferences".
|
||||
@ -234,6 +228,18 @@ checker:
|
||||
result_container:
|
||||
- has_infobox
|
||||
|
||||
categories_as_tabs:
|
||||
general:
|
||||
images:
|
||||
videos:
|
||||
news:
|
||||
map:
|
||||
music:
|
||||
it:
|
||||
science:
|
||||
files:
|
||||
social media:
|
||||
|
||||
engines:
|
||||
- name: apk mirror
|
||||
engine: apkmirror
|
||||
@ -320,7 +326,7 @@ engines:
|
||||
url_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]/@href
|
||||
title_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]
|
||||
content_xpath: //article[@class="repo-summary"]/p
|
||||
categories: it
|
||||
categories: [it, repos]
|
||||
timeout: 4.0
|
||||
disabled: true
|
||||
shortcut: bb
|
||||
@ -419,7 +425,7 @@ engines:
|
||||
- name: docker hub
|
||||
engine: docker_hub
|
||||
shortcut: dh
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
|
||||
- name: erowid
|
||||
engine: xpath
|
||||
@ -430,7 +436,7 @@ engines:
|
||||
url_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/@href
|
||||
title_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/text()
|
||||
content_xpath: //dl[@class="results-list"]/dd[@class="result-details"]
|
||||
categories: general
|
||||
categories: []
|
||||
shortcut: ew
|
||||
disabled: true
|
||||
about:
|
||||
@ -489,7 +495,8 @@ engines:
|
||||
content_xpath: //section[contains(@class, "word__defination")]
|
||||
first_page_num: 1
|
||||
shortcut: et
|
||||
disabled: true
|
||||
categories: [dictionaries]
|
||||
disabled: false
|
||||
about:
|
||||
website: https://www.etymonline.com/
|
||||
wikidata_id: Q1188617
|
||||
@ -528,7 +535,7 @@ engines:
|
||||
- name: free software directory
|
||||
engine: mediawiki
|
||||
shortcut: fsd
|
||||
categories: it
|
||||
categories: [it, software wikis]
|
||||
base_url: https://directory.fsf.org/
|
||||
number_of_results: 5
|
||||
# what part of a page matches the query string: title, text, nearmatch
|
||||
@ -579,7 +586,7 @@ engines:
|
||||
title_query: name_with_namespace
|
||||
content_query: description
|
||||
page_size: 20
|
||||
categories: it
|
||||
categories: [it, repos]
|
||||
shortcut: gl
|
||||
timeout: 10.0
|
||||
disabled: true
|
||||
@ -605,7 +612,7 @@ engines:
|
||||
url_query: html_url
|
||||
title_query: name
|
||||
content_query: description
|
||||
categories: it
|
||||
categories: [it, repos]
|
||||
shortcut: cb
|
||||
disabled: true
|
||||
about:
|
||||
@ -671,7 +678,7 @@ engines:
|
||||
url_xpath: './/div[@class="RZEgze"]//div[@class="kCSSQe"]//a/@href'
|
||||
content_xpath: './/div[@class="RZEgze"]//a[@class="mnKHRc"]'
|
||||
thumbnail_xpath: './/div[@class="uzcko"]/div/span[1]//img/@data-src'
|
||||
categories: files
|
||||
categories: [files, apps]
|
||||
shortcut: gpa
|
||||
disabled: true
|
||||
about:
|
||||
@ -749,7 +756,7 @@ engines:
|
||||
url_xpath: './/div[@class="ans"]//a/@href'
|
||||
content_xpath: './/div[@class="from"]'
|
||||
page_size: 20
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
shortcut: ho
|
||||
about:
|
||||
website: https://hoogle.haskell.org/
|
||||
@ -844,7 +851,7 @@ engines:
|
||||
engine: xpath
|
||||
timeout: 4.0
|
||||
disabled: true
|
||||
categories: music
|
||||
categories: [music, lyrics]
|
||||
paging: true
|
||||
search_url: https://search.azlyrics.com/search.php?q={query}&w=lyrics&p={pageno}
|
||||
url_xpath: //td[@class="text-left visitedlyr"]/a/@href
|
||||
@ -899,7 +906,7 @@ engines:
|
||||
title_query: package/name
|
||||
content_query: package/description
|
||||
page_size: 25
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
disabled: true
|
||||
timeout: 5.0
|
||||
shortcut: npm
|
||||
@ -1008,7 +1015,7 @@ engines:
|
||||
url_query: url
|
||||
title_query: name
|
||||
content_query: description
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
disabled: true
|
||||
timeout: 5.0
|
||||
shortcut: pack
|
||||
@ -1065,7 +1072,7 @@ engines:
|
||||
content_xpath: ./p
|
||||
suggestion_xpath: /html/body/main/div/div/div/form/div/div[@class="callout-block"]/p/span/a[@class="link"]
|
||||
first_page_num: 1
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
about:
|
||||
website: https://pypi.org
|
||||
wikidata_id: Q2984686
|
||||
@ -1077,7 +1084,7 @@ engines:
|
||||
- name: qwant
|
||||
engine: qwant
|
||||
shortcut: qw
|
||||
categories: general
|
||||
categories: [general, web]
|
||||
disabled: false
|
||||
additional_tests:
|
||||
rosebud: *test_rosebud
|
||||
@ -1092,14 +1099,14 @@ engines:
|
||||
- name: qwant images
|
||||
engine: qwant
|
||||
shortcut: qwi
|
||||
categories: images
|
||||
categories: [images, web]
|
||||
disabled: false
|
||||
network: qwant
|
||||
|
||||
- name: qwant videos
|
||||
engine: qwant
|
||||
shortcut: qwv
|
||||
categories: videos
|
||||
categories: [videos, web]
|
||||
disabled: false
|
||||
network: qwant
|
||||
|
||||
@ -1159,19 +1166,19 @@ engines:
|
||||
engine: stackexchange
|
||||
shortcut: st
|
||||
api_site: 'stackoverflow'
|
||||
categories: it
|
||||
categories: [it, q&a]
|
||||
|
||||
- name: askubuntu
|
||||
engine: stackexchange
|
||||
shortcut: ubuntu
|
||||
api_site: 'askubuntu'
|
||||
categories: it
|
||||
categories: [it, q&a]
|
||||
|
||||
- name: superuser
|
||||
engine: stackexchange
|
||||
shortcut: su
|
||||
api_site: 'superuser'
|
||||
categories: it
|
||||
categories: [it, q&a]
|
||||
|
||||
- name: searchcode code
|
||||
engine: searchcode_code
|
||||
@ -1354,7 +1361,7 @@ engines:
|
||||
url_query: URL
|
||||
title_query: Title
|
||||
content_query: Snippet
|
||||
categories: general
|
||||
categories: [general, web]
|
||||
shortcut: wib
|
||||
disabled: true
|
||||
about:
|
||||
@ -1413,11 +1420,11 @@ engines:
|
||||
- name: wiktionary
|
||||
engine: mediawiki
|
||||
shortcut: wt
|
||||
categories: general
|
||||
categories: [dictionaries]
|
||||
base_url: "https://{language}.wiktionary.org/"
|
||||
number_of_results: 5
|
||||
search_type: text
|
||||
disabled: true
|
||||
disabled: false
|
||||
about:
|
||||
website: https://www.wiktionary.org/
|
||||
wikidata_id: Q151
|
||||
@ -1467,7 +1474,7 @@ engines:
|
||||
engine: translated
|
||||
shortcut: tl
|
||||
timeout: 5.0
|
||||
disabled: true
|
||||
disabled: false
|
||||
# You can use without an API key, but you are limited to 1000 words/day
|
||||
# See: https://mymemory.translated.net/doc/usagelimits.php
|
||||
# api_key: ''
|
||||
@ -1501,6 +1508,7 @@ engines:
|
||||
shortcut: mjk
|
||||
engine: xpath
|
||||
paging: true
|
||||
categories: [general, web]
|
||||
search_url: https://www.mojeek.com/search?q={query}&s={pageno}
|
||||
results_xpath: /html/body//div[@class="results"]/ul[@class="results-standard"]/li
|
||||
url_xpath: ./h2/a/@href
|
||||
@ -1520,6 +1528,7 @@ engines:
|
||||
|
||||
- name: naver
|
||||
shortcut: nvr
|
||||
categories: [general, web]
|
||||
engine: xpath
|
||||
paging: true
|
||||
search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno}
|
||||
@ -1549,7 +1558,7 @@ engines:
|
||||
content_xpath: ./span/p
|
||||
suggestion_xpath: /html/body/main/div/div[@class="search__suggestions"]/p/a
|
||||
first_page_num: 1
|
||||
categories: it
|
||||
categories: [it, packages]
|
||||
disabled: true
|
||||
about:
|
||||
website: https://rubygems.org/
|
||||
@ -1593,14 +1602,14 @@ engines:
|
||||
engine: wordnik
|
||||
shortcut: def
|
||||
base_url: https://www.wordnik.com/
|
||||
categories: general
|
||||
categories: [dictionaries]
|
||||
timeout: 5.0
|
||||
disabled: true
|
||||
disabled: false
|
||||
|
||||
- name: woxikon.de synonyme
|
||||
engine: xpath
|
||||
shortcut: woxi
|
||||
categories: general
|
||||
categories: [dictionaries]
|
||||
timeout: 5.0
|
||||
disabled: true
|
||||
search_url: https://synonyme.woxikon.de/synonyme/{query}.php
|
||||
@ -1619,7 +1628,6 @@ engines:
|
||||
engine: sjp
|
||||
shortcut: sjp
|
||||
base_url: https://sjp.pwn.pl/
|
||||
categories: general
|
||||
timeout: 5.0
|
||||
disabled: true
|
||||
|
||||
@ -1652,7 +1660,7 @@ engines:
|
||||
title_xpath: //span[@class="snippet-title"]
|
||||
content_xpath: //p[1][@class="snippet-description"]
|
||||
suggestion_xpath: //div[@class="text-gray h6"]/a
|
||||
categories: general
|
||||
categories: [general, web]
|
||||
about:
|
||||
website: https://brave.com/search/
|
||||
wikidata_id: Q107355971
|
||||
|
@ -20,18 +20,18 @@ OUTPUT_FORMATS = ['html', 'csv', 'json', 'rss']
|
||||
LANGUAGE_CODES = ['all'] + list(l[0] for l in languages)
|
||||
OSCAR_STYLE = ('logicodev', 'logicodev-dark', 'pointhi')
|
||||
SIMPLE_STYLE = ('auto', 'light', 'dark')
|
||||
CATEGORY_ORDER = [
|
||||
'general',
|
||||
'images',
|
||||
'videos',
|
||||
'news',
|
||||
'map',
|
||||
'music',
|
||||
'it',
|
||||
'science',
|
||||
'files',
|
||||
'social media',
|
||||
]
|
||||
CATEGORIES_AS_TABS = {
|
||||
'general': {},
|
||||
'images': {},
|
||||
'videos': {},
|
||||
'news': {},
|
||||
'map': {},
|
||||
'music': {},
|
||||
'it': {},
|
||||
'science': {},
|
||||
'files': {},
|
||||
'social media': {},
|
||||
}
|
||||
STR_TO_BOOL = {
|
||||
'0': False,
|
||||
'false': False,
|
||||
@ -182,7 +182,6 @@ SCHEMA = {
|
||||
'results_on_new_tab': SettingsValue(bool, False),
|
||||
'advanced_search': SettingsValue(bool, False),
|
||||
'query_in_title': SettingsValue(bool, False),
|
||||
'categories_order': SettingsValue(list, CATEGORY_ORDER),
|
||||
},
|
||||
'preferences': {
|
||||
'lock': SettingsValue(list, []),
|
||||
@ -213,6 +212,7 @@ SCHEMA = {
|
||||
'checker': {
|
||||
'off_when_debug': SettingsValue(bool, True),
|
||||
},
|
||||
'categories_as_tabs': SettingsValue(dict, CATEGORIES_AS_TABS),
|
||||
'engines': SettingsValue(list, []),
|
||||
'doi_resolvers': {},
|
||||
}
|
||||
|
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
@ -72,6 +72,7 @@
|
||||
/// Settings Colors
|
||||
--color-settings-tr-hover: #f7f7f7;
|
||||
--color-settings-engine-description-font: darken(#dcdcdc, 30%);
|
||||
--color-settings-engine-group-background: #0001;
|
||||
/// Detail modal
|
||||
--color-result-detail-font: #fff;
|
||||
--color-result-detail-label-font: lightgray;
|
||||
@ -180,6 +181,7 @@
|
||||
/// Settings Colors
|
||||
--color-settings-tr-hover: #2d2d2d;
|
||||
--color-settings-engine-description-font: darken(#dcdcdc, 30%);
|
||||
--color-settings-engine-group-background: #1a1919;
|
||||
/// Toolkit Colors
|
||||
--color-toolkit-badge-font: #fff;
|
||||
--color-toolkit-badge-background: #777;
|
||||
|
@ -161,6 +161,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.engine-group {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
background: var(--color-settings-engine-group-background);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: @tablet) {
|
||||
|
@ -1,11 +1,11 @@
|
||||
<div id="categories">
|
||||
{%- if rtl -%}
|
||||
{% for category in categories | reverse -%}
|
||||
{% for category in categories_as_tabs | reverse -%}
|
||||
<input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />{{- '' -}}
|
||||
<label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label>
|
||||
{%- endfor %}
|
||||
{%- else -%}
|
||||
{% for category in categories -%}
|
||||
{% for category in categories_as_tabs -%}
|
||||
<input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />{{- '' -}}
|
||||
<label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label>
|
||||
{%- endfor %}
|
||||
|
@ -298,7 +298,7 @@
|
||||
<div class="tab-pane active_if_nojs" id="tab_engine">
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs nav-justified hide_if_nojs" role="tablist">
|
||||
{% for categ in all_categories %}
|
||||
{% for categ in categories_as_tabs + [OTHER_CATEGORY] %}
|
||||
<li{% if loop.first %} class="active"{% endif %}><a href="#tab_engine_{{ categ|replace(' ', '_') }}" role="tab" data-toggle="tab">{{ _(categ) }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
@ -317,10 +317,13 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% for categ in all_categories %}
|
||||
{% for categ in categories_as_tabs + [OTHER_CATEGORY] %}
|
||||
<noscript><label>{{ _(categ) }}</label>
|
||||
</noscript>
|
||||
<div class="tab-pane{% if loop.first %} active{% endif %} active_if_nojs" id="tab_engine_{{ categ|replace(' ', '_') }}">
|
||||
{% 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>
|
||||
{% endif %}
|
||||
<div class="container-fluid">
|
||||
<fieldset>
|
||||
<div class="table-responsive">
|
||||
@ -348,7 +351,11 @@
|
||||
<th scope="col" class="text-right">{{ _("Allow") }}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% for search_engine in engines_by_category[categ] %}
|
||||
{% for group, engines in engines_by_category[categ] | group_engines_in_tab %}
|
||||
{% if loop.length > 1 %}
|
||||
<tr><th colspan="9">{{_(group)}}</th></tr>
|
||||
{% endif %}
|
||||
{% for search_engine in engines %}
|
||||
{% if not search_engine.private %}
|
||||
<tr>
|
||||
{% if not rtl %}
|
||||
@ -357,7 +364,11 @@
|
||||
</td>
|
||||
<th scope="row" data-engine-name="{{ search_engine.name }}"><span aria-labelledby="{{ 'tooltip_' + categ + '_' + search_engine.name }}">
|
||||
{%- if search_engine.enable_http %}{{ icon('exclamation-sign', 'No HTTPS') }}{% endif -%}
|
||||
{{- search_engine.name -}}</span>
|
||||
{{- search_engine.name -}}
|
||||
{%- if search_engine.about and search_engine.about.language %}
|
||||
({{search_engine.about.language | upper}})
|
||||
{%- endif %}
|
||||
</span>
|
||||
{{- engine_about(search_engine, 'tooltip_' + categ + '_' + search_engine.name) -}}
|
||||
</th>
|
||||
<td class="name">{{ shortcuts[search_engine.name] }}</td>
|
||||
@ -382,6 +393,7 @@
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<div id="categories" class="search_categories">{{- '' -}}
|
||||
<div id="categories_container">
|
||||
{%- if display_tooltip %}<div class="help">{{ _('Click on the magnifier to perform search') }}</div>{% endif -%}
|
||||
{%- for category in categories -%}
|
||||
{%- for category in categories_as_tabs -%}
|
||||
<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') -}}
|
||||
|
@ -274,8 +274,11 @@
|
||||
{{ tab_header('maintab', 'engines', _('Engines')) }}
|
||||
<p>{{ _('Currently used search engines') }}</p>
|
||||
{{ tabs_open() }}
|
||||
{% for categ in all_categories %}
|
||||
{% for categ in categories_as_tabs + [OTHER_CATEGORY] %}
|
||||
{{ tab_header('enginetab', 'category' + categ, _(categ)) }}
|
||||
{% 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>
|
||||
{% endif %}
|
||||
<div class="scrollx">
|
||||
<table class="striped">
|
||||
<tr>
|
||||
@ -289,12 +292,22 @@
|
||||
<th>{{ _("Max time") }}</th>
|
||||
<th>{{ _("Reliability") }}</th>
|
||||
</tr>
|
||||
{% for search_engine in engines_by_category[categ] %}
|
||||
{% for group, engines in engines_by_category[categ] | group_engines_in_tab %}
|
||||
{% if loop.length > 1 %}
|
||||
<tr><th colspan="9" class="engine-group">{{_(group)}}</th></tr>
|
||||
{% endif %}
|
||||
{% for search_engine in engines %}
|
||||
{% if not search_engine.private %}
|
||||
{% set engine_id = 'engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_') %}
|
||||
<tr>
|
||||
<td class="engine_checkbox">{{ checkbox_onoff(engine_id, (search_engine.name, categ) in disabled_engines) }}</td>
|
||||
<th class="name" data-engine-name="{{ search_engine.name }}">{% if search_engine.enable_http %}{{ icon_big('warning', 'No HTTPS') }}{% endif %} {{ search_engine.name }} {{ engine_about(search_engine) }}</th>
|
||||
<th class="name" data-engine-name="{{ search_engine.name }}">{% if search_engine.enable_http %}{{ icon_big('warning', 'No HTTPS') }}{% endif %}
|
||||
{{ search_engine.name }}
|
||||
{%- if search_engine.about and search_engine.about.language %}
|
||||
({{search_engine.about.language | upper}})
|
||||
{%- endif %}
|
||||
{{ engine_about(search_engine) }}
|
||||
</th>
|
||||
<td class="shortcut">{{ shortcuts[search_engine.name] }}</td>
|
||||
<td>{{ checkbox(engine_id + '_supported_languages', supports[search_engine.name]['supports_selected_language'], true, true) }}</td>
|
||||
<td>{{ checkbox(engine_id + '_safesearch', supports[search_engine.name]['safesearch'], true, true) }}</td>
|
||||
@ -305,6 +318,7 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{{ tab_footer() }}
|
||||
|
@ -59,6 +59,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,
|
||||
categories,
|
||||
engines,
|
||||
engine_shortcuts,
|
||||
@ -73,6 +74,8 @@ from searx.webutils import (
|
||||
new_hmac,
|
||||
is_hmac_of,
|
||||
is_flask_run_cmdline,
|
||||
DEFAULT_GROUP_NAME,
|
||||
group_engines_in_tab,
|
||||
)
|
||||
from searx.webadapter import (
|
||||
get_search_query_from_webapp,
|
||||
@ -152,6 +155,7 @@ app = Flask(__name__, static_folder=settings['ui']['static_path'], template_fold
|
||||
app.jinja_env.trim_blocks = True
|
||||
app.jinja_env.lstrip_blocks = True
|
||||
app.jinja_env.add_extension('jinja2.ext.loopcontrols') # pylint: disable=no-member
|
||||
app.jinja_env.filters['group_engines_in_tab'] = group_engines_in_tab # pylint: disable=no-member
|
||||
app.secret_key = settings['server']['secret_key']
|
||||
|
||||
babel = Babel(app)
|
||||
@ -169,6 +173,17 @@ _category_names = (
|
||||
gettext('map'),
|
||||
gettext('onions'),
|
||||
gettext('science'),
|
||||
# non-tab categories
|
||||
gettext('apps'),
|
||||
gettext('dictionaries'),
|
||||
gettext('lyrics'),
|
||||
gettext('packages'),
|
||||
gettext('q&a'),
|
||||
gettext('repos'),
|
||||
gettext('software wikis'),
|
||||
gettext('web'),
|
||||
gettext(DEFAULT_GROUP_NAME),
|
||||
gettext(OTHER_CATEGORY),
|
||||
)
|
||||
|
||||
_simple_style = (gettext('auto'), gettext('light'), gettext('dark'))
|
||||
@ -390,12 +405,6 @@ def get_translations():
|
||||
}
|
||||
|
||||
|
||||
def _get_ordered_categories():
|
||||
ordered_categories = list(settings['ui']['categories_order'])
|
||||
ordered_categories.extend(x for x in sorted(categories.keys()) if x not in ordered_categories)
|
||||
return ordered_categories
|
||||
|
||||
|
||||
def _get_enable_categories(all_categories):
|
||||
disabled_engines = request.preferences.engines.get_disabled()
|
||||
enabled_categories = set(
|
||||
@ -430,8 +439,9 @@ def render(template_name, override_theme=None, **kwargs):
|
||||
kwargs['query_in_title'] = request.preferences.get_value('query_in_title')
|
||||
kwargs['safesearch'] = str(request.preferences.get_value('safesearch'))
|
||||
kwargs['theme'] = get_current_theme_name(override=override_theme)
|
||||
kwargs['all_categories'] = _get_ordered_categories()
|
||||
kwargs['categories'] = _get_enable_categories(kwargs['all_categories'])
|
||||
kwargs['categories_as_tabs'] = list(settings['categories_as_tabs'].keys())
|
||||
kwargs['categories'] = _get_enable_categories(categories.keys())
|
||||
kwargs['OTHER_CATEGORY'] = OTHER_CATEGORY
|
||||
|
||||
# i18n
|
||||
kwargs['language_codes'] = [l for l in languages if l[0] in settings['search']['languages']]
|
||||
|
@ -5,11 +5,14 @@ import hashlib
|
||||
import hmac
|
||||
import re
|
||||
import inspect
|
||||
import itertools
|
||||
from typing import Iterable, List, Tuple
|
||||
|
||||
from io import StringIO
|
||||
from codecs import getincrementalencoder
|
||||
|
||||
from searx import logger
|
||||
from searx import logger, settings
|
||||
from searx.engines import Engine, OTHER_CATEGORY
|
||||
|
||||
|
||||
VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(-[a-zA-Z]{2})?$')
|
||||
@ -134,3 +137,28 @@ def is_flask_run_cmdline():
|
||||
if len(frames) < 2:
|
||||
return False
|
||||
return frames[-2].filename.endswith('flask/cli.py')
|
||||
|
||||
|
||||
DEFAULT_GROUP_NAME = 'others'
|
||||
|
||||
|
||||
def group_engines_in_tab(engines: Iterable[Engine]) -> List[Tuple[str, Iterable[Engine]]]:
|
||||
"""Groups an Iterable of engines by their first non tab category"""
|
||||
|
||||
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 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)
|
||||
|
||||
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]
|
||||
|
@ -33,6 +33,10 @@ outgoing:
|
||||
request_timeout: 1.0 # seconds
|
||||
useragent_suffix: ""
|
||||
|
||||
categories_as_tabs:
|
||||
general:
|
||||
dummy:
|
||||
|
||||
engines:
|
||||
- name: general dummy
|
||||
engine: dummy
|
||||
|
Loading…
Reference in New Issue
Block a user