mirror of
https://github.com/searxng/searxng.git
synced 2026-06-04 08:57:17 +02:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6559c9ad6 | |||
| 5bae05514b | |||
| 00ca5776f2 | |||
| 577f5f2f30 | |||
| 253dc86c10 | |||
| 3066bc19eb | |||
| e964708c00 | |||
| 7159b8aed3 | |||
| 246f5a5499 | |||
| 300695de5c | |||
| bd863f16b1 | |||
| 4ac822fd7f | |||
| e1d25c5078 | |||
| 01159b82fe | |||
| 780ee32564 | |||
| 217c9a1597 | |||
| 70e810bd7b | |||
| baab1c160a | |||
| dd4664e03a |
@@ -106,10 +106,10 @@ jobs:
|
||||
|
||||
- if: ${{ matrix.emulation }}
|
||||
name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: "${{ github.repository_owner }}"
|
||||
@@ -147,10 +147,10 @@ jobs:
|
||||
|
||||
- if: ${{ matrix.emulation }}
|
||||
name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: "${{ github.repository_owner }}"
|
||||
@@ -180,14 +180,14 @@ jobs:
|
||||
persist-credentials: "false"
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: "ghcr.io"
|
||||
username: "${{ github.repository_owner }}"
|
||||
password: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
with:
|
||||
registry: "docker.io"
|
||||
username: "${{ secrets.DOCKER_USER }}"
|
||||
|
||||
@@ -41,6 +41,6 @@ jobs:
|
||||
write-comment: "false"
|
||||
|
||||
- name: Upload SARIFs
|
||||
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||
uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
|
||||
with:
|
||||
sarif_file: "./scout.sarif"
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { KeyBindingLayout } from "./main/keyboard.ts";
|
||||
// synced with searx/webapp.py get_client_settings
|
||||
type Settings = {
|
||||
plugins?: string[];
|
||||
advanced_search?: boolean;
|
||||
autocomplete?: string;
|
||||
autocomplete_min?: number;
|
||||
doi_resolver?: string;
|
||||
|
||||
@@ -19,6 +19,7 @@ Settings
|
||||
settings_search
|
||||
settings_server
|
||||
settings_ui
|
||||
settings_preferences
|
||||
settings_redis
|
||||
settings_valkey
|
||||
settings_outgoing
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.. _settings preferences:
|
||||
|
||||
================
|
||||
``preferences:``
|
||||
================
|
||||
|
||||
.. autoclass:: searx._settings.SettingsPref
|
||||
:members:
|
||||
@@ -47,6 +47,7 @@
|
||||
activated:
|
||||
|
||||
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
|
||||
- :ref:`image_proxy`
|
||||
|
||||
.. _image_proxy:
|
||||
|
||||
|
||||
@@ -23,6 +23,6 @@ coloredlogs==15.0.1
|
||||
docutils>=0.21.2;python_version <= "3.11"
|
||||
docutils>=0.22.4; python_version > "3.11"
|
||||
parameterized==0.9.0
|
||||
granian[reload]==2.7.4
|
||||
basedpyright==1.39.5
|
||||
granian[reload]==2.7.5
|
||||
basedpyright==1.39.6
|
||||
types-lxml==2026.2.16
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
granian==2.7.4
|
||||
granian[pname]==2.7.4
|
||||
granian==2.7.5
|
||||
granian[pname]==2.7.5
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ sniffio==1.3.1
|
||||
valkey==6.1.1
|
||||
markdown-it-py==4.2.0
|
||||
msgspec==0.21.1
|
||||
typer==0.25.1
|
||||
typer==0.26.3
|
||||
isodate==0.7.2
|
||||
whitenoise==6.12.0
|
||||
typing-extensions==4.15.0
|
||||
|
||||
+8
-1
@@ -10,6 +10,7 @@ from os.path import dirname, abspath
|
||||
import logging
|
||||
|
||||
import msgspec
|
||||
from ._settings import SettingsPref
|
||||
|
||||
# Debug
|
||||
LOG_FORMAT_DEBUG: str = '%(levelname)-7s %(name)-30.30s: %(message)s'
|
||||
@@ -47,6 +48,12 @@ def init_settings():
|
||||
settings.clear()
|
||||
settings.update(cfg)
|
||||
|
||||
if get_setting("server.public_instance"):
|
||||
# enable image proxy for public instances #6125
|
||||
settings["server"]["image_proxy"] = True
|
||||
pref: SettingsPref = get_setting("preferences")
|
||||
pref.lock.add("image_proxy")
|
||||
|
||||
sxng_debug = get_setting("general.debug")
|
||||
if sxng_debug:
|
||||
_logging_config_debug()
|
||||
@@ -66,7 +73,7 @@ def init_settings():
|
||||
if settings['server']['public_instance']:
|
||||
logger.warning(
|
||||
"Be aware you have activated features intended only for public instances. "
|
||||
"This force the usage of the limiter and link_token / "
|
||||
"This force the usage of the limiter, link_token and image proxy / "
|
||||
"see https://docs.searxng.org/admin/searx.limiter.html"
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Implementation of the :py:obj:`preference <searx.preference>` settings."""
|
||||
# pylint: disable = too-few-public-methods
|
||||
|
||||
import typing as t
|
||||
import msgspec
|
||||
|
||||
|
||||
class SettingsPref(msgspec.Struct, kw_only=True, forbid_unknown_fields=True):
|
||||
"""Options for configuring the preferences
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
preferences:
|
||||
lock:
|
||||
- favicon_resolver
|
||||
- image_proxy
|
||||
- method
|
||||
# ...
|
||||
|
||||
"""
|
||||
|
||||
lock: set[
|
||||
t.Literal[
|
||||
"categories",
|
||||
"language",
|
||||
"locale",
|
||||
"autocomplete",
|
||||
"favicon_resolver",
|
||||
"image_proxy",
|
||||
"method",
|
||||
"safesearch",
|
||||
"theme",
|
||||
"results_on_new_tab",
|
||||
"doi_resolver",
|
||||
"simple_style",
|
||||
"center_alignment",
|
||||
"query_in_title",
|
||||
"search_on_category_select",
|
||||
]
|
||||
] = set()
|
||||
"""Lock arbitrary settings on the preferences page."""
|
||||
@@ -51,11 +51,10 @@ def request(query, params):
|
||||
}
|
||||
|
||||
params["url"] = f"{base_url}?{urlencode(query_params)}"
|
||||
params["headers"]["Referer"] = "https://www.bilibili.com"
|
||||
params["headers"]["Referer"] = "https://www.bilibili.com/"
|
||||
params["headers"]["Accept"] = "application/json, text/javascript, */*; q=0.01"
|
||||
params["cookies"] = cookie
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def response(resp):
|
||||
search_res = resp.json()
|
||||
|
||||
@@ -41,7 +41,9 @@ safesearch_cookies = {0: "-2", 1: None, 2: "1"}
|
||||
safesearch_args = {0: "1", 1: None, 2: "1"}
|
||||
|
||||
search_path_map = {"images": "i", "videos": "v", "news": "news"}
|
||||
|
||||
_HTTP_User_Agent: str = gen_useragent()
|
||||
send_accept_language_header = False
|
||||
|
||||
|
||||
def init(engine_settings: dict[str, t.Any]):
|
||||
|
||||
@@ -45,7 +45,7 @@ about = {
|
||||
base_url = "https://api2.marginalia-search.com"
|
||||
safesearch = True
|
||||
categories = ["general"]
|
||||
paging = False
|
||||
paging = True
|
||||
results_per_page = 20
|
||||
api_key = None
|
||||
"""To get an API key, please follow the instructions from `Key and license`_
|
||||
@@ -85,7 +85,12 @@ class ApiSearchResults(t.TypedDict):
|
||||
|
||||
def request(query: str, params: dict[str, t.Any]):
|
||||
|
||||
query_params = {"count": results_per_page, "nsfw": min(params["safesearch"], 1), "query": query}
|
||||
query_params = {
|
||||
"page": params["pageno"],
|
||||
"count": results_per_page,
|
||||
"nsfw": min(params["safesearch"], 1),
|
||||
"query": query,
|
||||
}
|
||||
|
||||
params["url"] = f"{base_url}/search?{urlencode(query_params)}"
|
||||
params["headers"]["User-Agent"] = searxng_useragent()
|
||||
|
||||
@@ -9,7 +9,7 @@ from lxml import html
|
||||
from searx.result_types import EngineResults
|
||||
from searx.utils import eval_xpath_list, gen_useragent
|
||||
from searx.enginelib import EngineCache
|
||||
from searx.exceptions import SearxEngineAPIException
|
||||
from searx.exceptions import SearxEngineAPIException, SearxEngineAccessDeniedException
|
||||
from searx.network import get
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ def _get_secret_key():
|
||||
# circumvents Cloudflare bot protections
|
||||
"User-Agent": gen_useragent(),
|
||||
"Referer": base_url,
|
||||
"Sec-GPC": "1",
|
||||
"Connection": "keep-alive",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -95,7 +97,7 @@ def request(query, params):
|
||||
try:
|
||||
secret_key = _get_secret_key()
|
||||
CACHE.set(SECRET_KEY_DB_KEY, secret_key)
|
||||
except SearxEngineAPIException as e:
|
||||
except (SearxEngineAPIException, SearxEngineAccessDeniedException) as e:
|
||||
logger.debug("failed to extract API key %s" % e)
|
||||
secret_key = api_key
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Public domain image archive"""
|
||||
|
||||
import re
|
||||
|
||||
from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl
|
||||
from json import dumps
|
||||
|
||||
@@ -49,6 +51,8 @@ paging = True
|
||||
|
||||
__CACHED_API_URL = None
|
||||
|
||||
_API_URL_RE = re.compile(r"\"(https://.*?/search-proxy)\"")
|
||||
|
||||
|
||||
def _clean_url(url):
|
||||
parsed = urlparse(url)
|
||||
@@ -74,11 +78,12 @@ def _get_algolia_api_url():
|
||||
if resp.status_code != 200:
|
||||
raise LookupError("Failed to obtain AWS api url for PDImageArchive")
|
||||
|
||||
api_url = extr(resp.text, 'const r="', '"', default=None)
|
||||
|
||||
if api_url is None:
|
||||
api_url_match = _API_URL_RE.search(resp.text)
|
||||
if api_url_match is None:
|
||||
raise LookupError("Couldn't obtain AWS api url for PDImageArchive")
|
||||
|
||||
api_url = api_url_match.group(1)
|
||||
|
||||
__CACHED_API_URL = api_url
|
||||
return api_url
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Svgrepo (icons)"""
|
||||
|
||||
from lxml import html
|
||||
from searx.utils import extract_text, eval_xpath, eval_xpath_list
|
||||
|
||||
about = {
|
||||
"website": 'https://www.svgrepo.com',
|
||||
"official_api_documentation": 'https://svgapi.com',
|
||||
"use_official_api": False,
|
||||
"require_api_key": False,
|
||||
"results": 'HTML',
|
||||
}
|
||||
|
||||
paging = True
|
||||
categories = ['images', 'icons']
|
||||
base_url = "https://www.svgrepo.com"
|
||||
|
||||
results_xpath = "//div[@class='style_nodeListing__7Nmro']/div"
|
||||
url_xpath = ".//a/@href"
|
||||
title_xpath = ".//a/@title"
|
||||
img_src_xpath = ".//img/@src"
|
||||
|
||||
|
||||
def request(query, params):
|
||||
params['url'] = f"{base_url}/vectors/{query}/{params['pageno']}/"
|
||||
return params
|
||||
|
||||
|
||||
def response(resp):
|
||||
results = []
|
||||
|
||||
dom = html.fromstring(resp.text)
|
||||
for result in eval_xpath_list(dom, results_xpath):
|
||||
results.append(
|
||||
{
|
||||
'template': 'images.html',
|
||||
'url': base_url + extract_text(eval_xpath(result, url_xpath)),
|
||||
'title': extract_text(eval_xpath(result, title_xpath)).replace(" SVG File", "").replace("Show ", ""),
|
||||
'img_src': extract_text(eval_xpath(result, img_src_xpath)),
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
@@ -174,6 +174,10 @@ number, but an offset.'''
|
||||
first_page_num = 1
|
||||
'''Number of the first page (usually 0 or 1).'''
|
||||
|
||||
send_page_num_on_first_page = True
|
||||
'''Whether to include the page number in the request for the first page.
|
||||
This can help if an engine blocks request that send a page number for the first page.'''
|
||||
|
||||
time_range_support = False
|
||||
'''Engine supports search time range.'''
|
||||
|
||||
@@ -238,10 +242,14 @@ def request(query, params):
|
||||
if safe_search_val is not None:
|
||||
safe_search = safe_search_map[safe_search_val]
|
||||
|
||||
pageno = ""
|
||||
if send_page_num_on_first_page or params["pageno"] != 1:
|
||||
pageno = (params['pageno'] - 1) * page_size + first_page_num
|
||||
|
||||
fargs = {
|
||||
'query': urlencode({'q': query})[2:],
|
||||
'lang': lang,
|
||||
'pageno': (params['pageno'] - 1) * page_size + first_page_num,
|
||||
'pageno': pageno,
|
||||
'time_range': time_range,
|
||||
'safe_search': safe_search,
|
||||
}
|
||||
|
||||
+52
-63
@@ -17,13 +17,14 @@ import babel.core
|
||||
|
||||
import searx.plugins
|
||||
|
||||
from searx import settings, autocomplete, favicons
|
||||
from searx import get_setting, settings, autocomplete, favicons
|
||||
from searx.enginelib import Engine
|
||||
from searx.engines import DEFAULT_CATEGORY
|
||||
from searx.extended_types import SXNG_Request
|
||||
from searx.locales import LOCALE_NAMES
|
||||
from searx.webutils import VALID_LANGUAGE_CODE
|
||||
|
||||
from ._settings import SettingsPref
|
||||
|
||||
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
|
||||
DOI_RESOLVERS = list(settings['doi_resolvers'])
|
||||
@@ -386,6 +387,7 @@ class ClientPref:
|
||||
return cls(locale=locale)
|
||||
|
||||
|
||||
@t.final
|
||||
class Preferences:
|
||||
"""Validates and saves preferences to cookies"""
|
||||
|
||||
@@ -400,95 +402,91 @@ class Preferences:
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.cfg: SettingsPref = get_setting("preferences")
|
||||
|
||||
self.key_value_settings: dict[str, Setting] = {
|
||||
# fmt: off
|
||||
'categories': MultipleChoiceSetting(
|
||||
['general'],
|
||||
locked=is_locked('categories'),
|
||||
choices=categories + ['none']
|
||||
["general"],
|
||||
locked="categories" in self.cfg.lock,
|
||||
choices=categories + ["none"],
|
||||
),
|
||||
'language': SearchLanguageSetting(
|
||||
settings['search']['default_lang'],
|
||||
locked=is_locked('language'),
|
||||
choices=settings['search']['languages'] + ['']
|
||||
get_setting("search.default_lang"),
|
||||
locked="language" in self.cfg.lock,
|
||||
choices=get_setting("search.languages") + [""],
|
||||
),
|
||||
'locale': EnumStringSetting(
|
||||
settings['ui']['default_locale'],
|
||||
locked=is_locked('locale'),
|
||||
choices=list(LOCALE_NAMES.keys()) + ['']
|
||||
get_setting("ui.default_locale"),
|
||||
locked="locale" in self.cfg.lock,
|
||||
choices=list(LOCALE_NAMES.keys()) + [""],
|
||||
),
|
||||
'autocomplete': EnumStringSetting(
|
||||
settings['search']['autocomplete'],
|
||||
locked=is_locked('autocomplete'),
|
||||
choices=list(autocomplete.backends.keys()) + ['']
|
||||
get_setting("search.autocomplete"),
|
||||
locked="autocomplete" in self.cfg.lock,
|
||||
choices=list(autocomplete.backends.keys()) + [""],
|
||||
),
|
||||
'favicon_resolver': EnumStringSetting(
|
||||
settings['search']['favicon_resolver'],
|
||||
locked=is_locked('favicon_resolver'),
|
||||
choices=list(favicons.proxy.CFG.resolver_map.keys()) + ['']
|
||||
get_setting("search.favicon_resolver"),
|
||||
locked="favicon_resolver" in self.cfg.lock,
|
||||
choices=list(favicons.proxy.CFG.resolver_map.keys()) + [''],
|
||||
),
|
||||
'image_proxy': BooleanSetting(
|
||||
settings['server']['image_proxy'],
|
||||
locked=is_locked('image_proxy')
|
||||
get_setting("server.image_proxy"),
|
||||
locked="image_proxy" in self.cfg.lock,
|
||||
),
|
||||
'method': EnumStringSetting(
|
||||
settings['server']['method'],
|
||||
locked=is_locked('method'),
|
||||
choices=('GET', 'POST')
|
||||
get_setting("server.method"),
|
||||
locked="method" in self.cfg.lock,
|
||||
choices=("GET", "POST"),
|
||||
),
|
||||
'safesearch': MapSetting(
|
||||
settings['search']['safe_search'],
|
||||
locked=is_locked('safesearch'),
|
||||
get_setting("search.safe_search"),
|
||||
locked="safesearch" in self.cfg.lock,
|
||||
map={
|
||||
'0': 0,
|
||||
'1': 1,
|
||||
'2': 2
|
||||
}
|
||||
"0": 0,
|
||||
"1": 1,
|
||||
"2": 2,
|
||||
},
|
||||
),
|
||||
'theme': EnumStringSetting(
|
||||
settings['ui']['default_theme'],
|
||||
locked=is_locked('theme'),
|
||||
choices=themes
|
||||
get_setting("ui.default_theme"),
|
||||
locked="theme" in self.cfg.lock,
|
||||
choices=themes,
|
||||
),
|
||||
'results_on_new_tab': BooleanSetting(
|
||||
settings['ui']['results_on_new_tab'],
|
||||
locked=is_locked('results_on_new_tab')
|
||||
get_setting("ui.results_on_new_tab"),
|
||||
locked="results_on_new_tab" in self.cfg.lock,
|
||||
),
|
||||
'doi_resolver': MultipleChoiceSetting(
|
||||
[settings['default_doi_resolver'], ],
|
||||
locked=is_locked('doi_resolver'),
|
||||
choices=DOI_RESOLVERS
|
||||
[get_setting("default_doi_resolver")],
|
||||
locked="doi_resolver" in self.cfg.lock,
|
||||
choices=DOI_RESOLVERS,
|
||||
),
|
||||
'simple_style': EnumStringSetting(
|
||||
settings['ui']['theme_args']['simple_style'],
|
||||
locked=is_locked('simple_style'),
|
||||
choices=['', 'auto', 'light', 'dark', 'black']
|
||||
get_setting("ui.theme_args.simple_style"),
|
||||
locked="simple_style" in self.cfg.lock,
|
||||
choices=["", "auto", "light", "dark", "black"],
|
||||
),
|
||||
'center_alignment': BooleanSetting(
|
||||
settings['ui']['center_alignment'],
|
||||
locked=is_locked('center_alignment')
|
||||
),
|
||||
'advanced_search': BooleanSetting(
|
||||
settings['ui']['advanced_search'],
|
||||
locked=is_locked('advanced_search')
|
||||
get_setting("ui.center_alignment"),
|
||||
locked="center_alignment" in self.cfg.lock,
|
||||
),
|
||||
'query_in_title': BooleanSetting(
|
||||
settings['ui']['query_in_title'],
|
||||
locked=is_locked('query_in_title')
|
||||
get_setting("ui.query_in_title"),
|
||||
locked="query_in_title" in self.cfg.lock,
|
||||
),
|
||||
'search_on_category_select': BooleanSetting(
|
||||
settings['ui']['search_on_category_select'],
|
||||
locked=is_locked('search_on_category_select')
|
||||
get_setting("ui.search_on_category_select"),
|
||||
locked="search_on_category_select" in self.cfg.lock,
|
||||
),
|
||||
'hotkeys': EnumStringSetting(
|
||||
settings['ui']['hotkeys'],
|
||||
choices=['default', 'vim']
|
||||
get_setting("ui.hotkeys"),
|
||||
choices=["default", "vim"],
|
||||
),
|
||||
'url_formatting': EnumStringSetting(
|
||||
settings['ui']['url_formatting'],
|
||||
choices=['pretty', 'full', 'host']
|
||||
get_setting("ui.url_formatting"),
|
||||
choices=["pretty", "full", "host"],
|
||||
),
|
||||
# fmt: on
|
||||
}
|
||||
|
||||
self.engines = EnginesSetting('engines', engines=engines.values())
|
||||
@@ -597,12 +595,3 @@ class Preferences:
|
||||
break
|
||||
|
||||
return valid
|
||||
|
||||
|
||||
def is_locked(setting_name: str):
|
||||
"""Checks if a given setting name is locked by settings.yml"""
|
||||
if 'preferences' not in settings:
|
||||
return False
|
||||
if 'lock' not in settings['preferences']:
|
||||
return False
|
||||
return setting_name in settings['preferences']['lock']
|
||||
|
||||
@@ -152,7 +152,6 @@ class OnlineProcessor(EngineProcessor):
|
||||
# add Accept-Language header
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Language
|
||||
|
||||
headers["Accept-Language"] = "en,en-US;q=0.7,en;q=0.3"
|
||||
if self.engine.send_accept_language_header and search_query.locale:
|
||||
_l = search_query.locale.language
|
||||
_t = search_query.locale.territory or _l
|
||||
|
||||
+36
-10
@@ -154,14 +154,13 @@ ui:
|
||||
# URL formatting: pretty, full or host
|
||||
url_formatting: pretty
|
||||
|
||||
preferences:
|
||||
# Lock arbitrary settings on the preferences page.
|
||||
#
|
||||
# preferences:
|
||||
# lock:
|
||||
lock: []
|
||||
# - categories
|
||||
# - language
|
||||
# - autocomplete
|
||||
# - favicon
|
||||
# - favicon_resolver
|
||||
# - safesearch
|
||||
# - method
|
||||
# - doi_resolver
|
||||
@@ -965,6 +964,22 @@ engines:
|
||||
timeout: 8.0
|
||||
disabled: true
|
||||
|
||||
- name: gabanza
|
||||
engine: xpath
|
||||
search_url: https://www.gabanza.com/search?query={query}
|
||||
shortcut: gab
|
||||
timeout: 4
|
||||
disabled: true
|
||||
results_xpath: //div[contains(@class, "border-t")]/div/div
|
||||
url_xpath: (.//a/@href)[1]
|
||||
title_xpath: ./a
|
||||
content_xpath: .//p
|
||||
about:
|
||||
website: https://www.gabanza.com
|
||||
use_official_api: false
|
||||
require_api_key: false
|
||||
results: HTML
|
||||
|
||||
- name: geizhals
|
||||
engine: geizhals
|
||||
shortcut: geiz
|
||||
@@ -2549,12 +2564,6 @@ engines:
|
||||
results: HTML
|
||||
language: de
|
||||
|
||||
- name: svgrepo
|
||||
engine: svgrepo
|
||||
shortcut: svg
|
||||
timeout: 10.0
|
||||
disabled: true
|
||||
|
||||
- name: tootfinder
|
||||
engine: tootfinder
|
||||
shortcut: toot
|
||||
@@ -2600,6 +2609,23 @@ engines:
|
||||
shortcut: wttr
|
||||
timeout: 9.0
|
||||
|
||||
- name: zapmeta
|
||||
engine: xpath
|
||||
shortcut: zpm
|
||||
search_url: https://www.zapmeta.com/search?q={query}&pg={pageno}
|
||||
results_xpath: //article[contains(@class, "organic-results-item")]
|
||||
url_xpath: ./h2/a/@href
|
||||
title_xpath: ./h2
|
||||
content_xpath: ./p
|
||||
paging: true
|
||||
send_page_num_on_first_page: false # otherwise blocks requests
|
||||
disabled: true
|
||||
about:
|
||||
website: https://www.zapmeta.com/
|
||||
use_official_api: false
|
||||
require_api_key: false
|
||||
results: HTML
|
||||
|
||||
- name: braveapi
|
||||
engine: braveapi
|
||||
# read https://docs.searxng.org/dev/engines/online/brave.html
|
||||
|
||||
@@ -15,6 +15,7 @@ import msgspec
|
||||
from typing_extensions import override
|
||||
from .brand import SettingsBrand
|
||||
from .sxng_locales import sxng_locales
|
||||
from ._settings import SettingsPref
|
||||
|
||||
searx_dir = abspath(dirname(__file__))
|
||||
|
||||
@@ -146,6 +147,8 @@ def apply_schema(settings: dict[str, t.Any], schema: dict[str, t.Any], path_list
|
||||
# Type Validation at runtime:
|
||||
# https://jcristharif.com/msgspec/structs.html#type-validation
|
||||
cfg_dict = settings.get(key)
|
||||
if cfg_dict is None:
|
||||
cfg_dict = {}
|
||||
cfg_json = msgspec.json.encode(cfg_dict)
|
||||
settings[key] = msgspec.json.decode(cfg_json, type=value)
|
||||
except msgspec.ValidationError as e:
|
||||
@@ -236,16 +239,13 @@ SCHEMA: dict[str, t.Any] = {
|
||||
},
|
||||
'center_alignment': SettingsValue(bool, False),
|
||||
'results_on_new_tab': SettingsValue(bool, False),
|
||||
'advanced_search': SettingsValue(bool, False),
|
||||
'query_in_title': SettingsValue(bool, False),
|
||||
'cache_url': SettingsValue(str, 'https://web.archive.org/web/'),
|
||||
'search_on_category_select': SettingsValue(bool, True),
|
||||
'hotkeys': SettingsValue(('default', 'vim'), 'default'),
|
||||
'url_formatting': SettingsValue(('pretty', 'full', 'host'), 'pretty'),
|
||||
},
|
||||
'preferences': {
|
||||
'lock': SettingsValue(list, []),
|
||||
},
|
||||
"preferences": SettingsPref,
|
||||
'outgoing': {
|
||||
'useragent_suffix': SettingsValue(str, ''),
|
||||
'request_timeout': SettingsValue(numbers.Real, 3.0),
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -180,7 +180,7 @@
|
||||
{%- if 'autocomplete' not in locked_preferences -%}
|
||||
{%- include 'simple/preferences/autocomplete.html' -%}
|
||||
{%- endif -%}
|
||||
{%- if 'favicon' not in locked_preferences -%}
|
||||
{%- if 'favicon_resolver' not in locked_preferences -%}
|
||||
{%- include 'simple/preferences/favicon.html' -%}
|
||||
{%- endif -%}
|
||||
{% if 'safesearch' not in locked_preferences %}
|
||||
|
||||
+6
-6
@@ -8,7 +8,7 @@ from searx.webutils import VALID_LANGUAGE_CODE
|
||||
from searx.query import RawTextQuery
|
||||
from searx.engines import categories, engines
|
||||
from searx.search.models import SearchQuery, EngineRef
|
||||
from searx.preferences import Preferences, is_locked
|
||||
from searx.preferences import Preferences
|
||||
|
||||
|
||||
# remove duplicate queries.
|
||||
@@ -53,7 +53,7 @@ def parse_pageno(form: Dict[str, str]) -> int:
|
||||
|
||||
|
||||
def parse_lang(preferences: Preferences, form: Dict[str, str], raw_text_query: RawTextQuery) -> str:
|
||||
if is_locked('language'):
|
||||
if "language" in preferences.cfg.lock:
|
||||
return preferences.get_value('language')
|
||||
# get language
|
||||
# set specific language if set on request, query or preferences
|
||||
@@ -73,7 +73,7 @@ def parse_lang(preferences: Preferences, form: Dict[str, str], raw_text_query: R
|
||||
|
||||
|
||||
def parse_safesearch(preferences: Preferences, form: Dict[str, str]) -> int:
|
||||
if is_locked('safesearch'):
|
||||
if "safesearch" in preferences.cfg.lock:
|
||||
return preferences.get_value('safesearch')
|
||||
|
||||
if 'safesearch' in form:
|
||||
@@ -135,7 +135,7 @@ def parse_category_form(query_categories: List[str], name: str, value: str) -> N
|
||||
def get_selected_categories(preferences: Preferences, form: Optional[Dict[str, str]]) -> List[str]:
|
||||
selected_categories = []
|
||||
|
||||
if not is_locked('categories') and form is not None:
|
||||
if not "categories" in preferences.cfg.lock and form is not None:
|
||||
for name, value in form.items():
|
||||
parse_category_form(selected_categories, name, value)
|
||||
|
||||
@@ -175,7 +175,7 @@ def parse_generic(preferences: Preferences, form: Dict[str, str], disabled_engin
|
||||
|
||||
# set categories/engines
|
||||
explicit_engine_list = False
|
||||
if not is_locked('categories'):
|
||||
if not "categories" in preferences.cfg.lock:
|
||||
# parse the form only if the categories are not locked
|
||||
for pd_name, pd in form.items(): # pylint: disable=invalid-name
|
||||
if pd_name == 'engines':
|
||||
@@ -266,7 +266,7 @@ def get_search_query_from_webapp(
|
||||
if query_lang == 'auto':
|
||||
query_lang = preferences.client.locale_tag or 'all'
|
||||
|
||||
if not is_locked('categories') and raw_text_query.specific:
|
||||
if not "categories" in preferences.cfg.lock and raw_text_query.specific:
|
||||
# if engines are calculated from query,
|
||||
# set categories by using that information
|
||||
query_engineref_list = raw_text_query.enginerefs
|
||||
|
||||
+1
-2
@@ -377,7 +377,6 @@ def get_client_settings():
|
||||
'theme_static_path': custom_url_for('static', filename='themes/simple'),
|
||||
'results_on_new_tab': req_pref.get_value('results_on_new_tab'),
|
||||
'favicon_resolver': req_pref.get_value('favicon_resolver'),
|
||||
'advanced_search': req_pref.get_value('advanced_search'),
|
||||
'query_in_title': req_pref.get_value('query_in_title'),
|
||||
'safesearch': req_pref.get_value('safesearch'),
|
||||
'theme': req_pref.get_value('theme'),
|
||||
@@ -977,7 +976,7 @@ def preferences():
|
||||
current_doi_resolver = get_doi_resolver(),
|
||||
allowed_plugins = allowed_plugins,
|
||||
preferences_url_params = sxng_request.preferences.get_as_url_params(),
|
||||
locked_preferences = get_setting("preferences.lock", []),
|
||||
locked_preferences = get_setting("preferences").lock,
|
||||
doi_resolvers = get_setting("doi_resolvers", {}),
|
||||
# fmt: on
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user