Compare commits

...

7 Commits

Author SHA1 Message Date
Bnyro 0037d43d87 [fix] aol: disable http2 to prevent request fingerprinting (#6149) 2026-05-26 12:15:35 +02:00
Bnyro f5be39e245 [mod] podcastindex: remove engine (#6140)
PodcastIndex.org started using a Proof-of-Work JavaScript
challenge whose results are sent as `X-Pow-*` request headers.
Although it is technically possible to re-implement the
PoW challenge in Python, it's likely impossible to maintain
because

- the actual Proof of Concept logic might change very often

- the whole idea of the Proof of Work challenge is to use
  a "big" amount of resources (about 1s on my PC); so executing the challenge
  would almost block all other work on the SearXNG instance

At first glance, the challenge looks very similar to what
Anubis does, because it also uses SHA256 hashes.
2026-05-26 11:53:20 +02:00
Bnyro 1574939441 [fix] json, xpath engine: rename safe_search_support option to safesearch (#6143) 2026-05-26 11:38:07 +02:00
Markus Heiser f1a22dec9e [fix] disable qwant engine / the rate-limits are just very strict (#6148)
Qwant is set to inactive by default due to its strict rate-limits

Related:

- https://github.com/searxng/searxng/pull/6127

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-26 11:05:06 +02:00
Markus Heiser 3db8b424a8 [mod] engine flaticon: migrate from LegacyResult to Image (#6142)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-26 09:19:17 +02:00
Markus Heiser a16a3dedb4 [build] /static (#6142) 2026-05-26 09:19:17 +02:00
Markus Heiser c629dd4f3c [mod] typification of SearXNG: add new result type Image (#6142)
- Python class:   searx/result_types/image.py
- Jinja template: searx/templates/simple/result_templates/images.html
- CSS (less)      client/simple/src/less/result_types/image.less

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-26 09:19:17 +02:00
16 changed files with 80 additions and 114 deletions
+1 -1
View File
@@ -7,7 +7,6 @@
@import "mixins.less"; @import "mixins.less";
@import "toolkit.less"; @import "toolkit.less";
@import "autocomplete.less"; @import "autocomplete.less";
@import "detail.less";
@import "animations.less"; @import "animations.less";
@import "embedded.less"; @import "embedded.less";
@import "info.less"; @import "info.less";
@@ -1165,3 +1164,4 @@ pre code {
@import "result_types/code.less"; @import "result_types/code.less";
@import "result_types/paper.less"; @import "result_types/paper.less";
@import "result_types/file.less"; @import "result_types/file.less";
@import "result_types/image.less";
+7
View File
@@ -0,0 +1,7 @@
.. _result_types.image:
=============
Image Results
=============
.. automodule:: searx.result_types.image
@@ -1,4 +1,8 @@
.. _result_types.mainresult: .. _result_types.mainresult:
============
Main Results
============
.. autoclass:: searx.result_types._base.MainResult .. autoclass:: searx.result_types._base.MainResult
:members: :members:
+1 -1
View File
@@ -18,13 +18,13 @@ following types have been implemented so far ..
main/code main/code
main/paper main/paper
main/file main/file
main/image
The :ref:`LegacyResult <LegacyResult>` is used internally for the results that The :ref:`LegacyResult <LegacyResult>` is used internally for the results that
have not yet been typed. The templates can be used as orientation until the have not yet been typed. The templates can be used as orientation until the
final typing is complete. final typing is complete.
- :ref:`template default` / :py:obj:`Result` - :ref:`template default` / :py:obj:`Result`
- :ref:`template images`
- :ref:`template videos` - :ref:`template videos`
- :ref:`template torrent` - :ref:`template torrent`
- :ref:`template map` - :ref:`template map`
-47
View File
@@ -129,53 +129,6 @@ audio_src : uri,
URL of an embedded ``<audio controls>``. URL of an embedded ``<audio controls>``.
.. _template images:
``images.html``
---------------
The images are displayed as small thumbnails in the main results list.
title : :py:class:`str`
Title of the image.
thumbnail_src : :py:class:`str`
URL of a preview of the image.
resolution :py:class:`str`
The resolution of the image (e.g. ``1920 x 1080`` pixel)
Image labels
~~~~~~~~~~~~
Clicking on the preview opens a gallery view in which all further metadata for
the image is displayed. Addition fields used in the :origin:`images.html
<searx/templates/simple/result_templates/images.html>`:
img_src : :py:class:`str`
URL of the full size image.
content: :py:class:`str`
Description of the image.
author: :py:class:`str`
Name of the author of the image.
img_format : :py:class:`str`
The format of the image (e.g. ``png``).
source : :py:class:`str`
Source of the image.
filesize: :py:class:`str`
Size of bytes in :py:obj:`human readable <searx.humanize_bytes>` notation
(e.g. ``MB`` for 1024 \* 1024 Bytes filesize).
url : :py:class:`str`
URL of the page from where the images comes from (source).
.. _template videos: .. _template videos:
``videos.html`` ``videos.html``
+2
View File
@@ -60,6 +60,8 @@ base_url = "https://search.aol.com"
time_range_map = {"day": "1d", "week": "1w", "month": "1m", "year": "1y"} time_range_map = {"day": "1d", "week": "1w", "month": "1m", "year": "1y"}
safesearch_map = {0: "p", 1: "r", 2: "i"} safesearch_map = {0: "p", 1: "r", 2: "i"}
enable_http2 = False
def init(_): def init(_):
if search_type not in ("search", "image", "video"): if search_type not in ("search", "image", "video"):
+7 -10
View File
@@ -54,16 +54,13 @@ def response(resp: "SXNG_Response"):
result: dict[str, str] # TBH: dict[str, t.Any] result: dict[str, str] # TBH: dict[str, t.Any]
for result in resp.json()["items"]: for result in resp.json()["items"]:
res.add( res.add(
res.types.LegacyResult( res.types.Image(
{ title=result["name"],
"template": "images.html", content=", ".join([tag["tag"] for tag in result["tags"]]), # pyright: ignore[reportArgumentType]
"url": _fix_url(result["slug"]), url=_fix_url(result["slug"]),
"thumbnail_src": _fix_url(result["png"]), thumbnail_src=_fix_url(result["png"]),
"img_src": _fix_url(result["png512"]), img_src=_fix_url(result["png512"]),
"title": result["name"], author=result["team_name"],
"content": ", ".join([tag["tag"] for tag in result["tags"]]), # pyright: ignore[reportArgumentType]
"author": result["team_name"],
}
) )
) )
+3 -4
View File
@@ -29,7 +29,7 @@ Time Range:
Safe-Search: Safe-Search:
- :py:obj:`safe_search_support` - :py:obj:`safesearch`
- :py:obj:`safe_search_map` - :py:obj:`safe_search_map`
Response: Response:
@@ -103,7 +103,7 @@ Replacements are:
``{safe_search}``: ``{safe_search}``:
Safe-search :py:obj:`URL parameter <safe_search_map>` if engine Safe-search :py:obj:`URL parameter <safe_search_map>` if engine
:py:obj:`supports safe-search <safe_search_support>`. The ``{safe_search}`` :py:obj:`supports safe-search <safesearch>`. The ``{safe_search}``
replacement is taken from the :py:obj:`safes_search_map`. Filter results:: replacement is taken from the :py:obj:`safes_search_map`. Filter results::
0: none, 1: moderate, 2:strict 0: none, 1: moderate, 2:strict
@@ -236,7 +236,7 @@ time_range_map = {
year: 365 year: 365
''' '''
safe_search_support = False safesearch = False
'''Engine supports safe-search.''' '''Engine supports safe-search.'''
safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'} safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'}
@@ -286,7 +286,6 @@ def do_query(data, q): # pylint: disable=invalid-name
qkey = q[0] qkey = q[0]
for key, value in iterate(data): for key, value in iterate(data):
if len(q) == 1: if len(q) == 1:
if key == qkey: if key == qkey:
ret.append(value) ret.append(value)
-41
View File
@@ -1,41 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Podcast Index"""
from urllib.parse import quote_plus
from datetime import datetime
about = {
'website': 'https://podcastindex.org',
'official_api_documentation': None, # requires an account
'use_official_api': False,
'require_api_key': False,
'results': 'JSON',
}
categories = []
base_url = "https://podcastindex.org"
def request(query, params):
params['url'] = f"{base_url}/api/search/byterm?q={quote_plus(query)}"
return params
def response(resp):
results = []
json = resp.json()
for result in json['feeds']:
results.append(
{
'url': result['link'],
'title': result['title'],
'content': result['description'],
'thumbnail': result['image'],
'publishedDate': datetime.fromtimestamp(result['newestItemPubdate']),
'metadata': f"{result['author']}, {result['episodeCount']} episodes",
}
)
return results
+3 -4
View File
@@ -31,7 +31,7 @@ Time Range:
Safe-Search: Safe-Search:
- :py:obj:`safe_search_support` - :py:obj:`safesearch`
- :py:obj:`safe_search_map` - :py:obj:`safe_search_map`
Response: Response:
@@ -100,7 +100,7 @@ Replacements are:
``{safe_search}``: ``{safe_search}``:
Safe-search :py:obj:`URL parameter <safe_search_map>` if engine Safe-search :py:obj:`URL parameter <safe_search_map>` if engine
:py:obj:`supports safe-search <safe_search_support>`. The ``{safe_search}`` :py:obj:`supports safe-search <safesearch>`. The ``{safe_search}``
replacement is taken from the :py:obj:`safes_search_map`. Filter results:: replacement is taken from the :py:obj:`safes_search_map`. Filter results::
0: none, 1: moderate, 2:strict 0: none, 1: moderate, 2:strict
@@ -205,7 +205,7 @@ time_range_map = {
year: 365 year: 365
''' '''
safe_search_support = False safesearch = False
'''Engine supports safe-search.''' '''Engine supports safe-search.'''
safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'} safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'}
@@ -280,7 +280,6 @@ def response(resp) -> EngineResults: # pylint: disable=too-many-branches
if results_xpath: if results_xpath:
for result in eval_xpath_list(dom, results_xpath): for result in eval_xpath_list(dom, results_xpath):
url = extract_url(eval_xpath_list(result, url_xpath, min_len=1), search_url) url = extract_url(eval_xpath_list(result, url_xpath, min_len=1), search_url)
title = extract_text(eval_xpath_list(result, title_xpath, min_len=1)) title = extract_text(eval_xpath_list(result, title_xpath, min_len=1))
content = extract_text(eval_xpath_list(result, content_xpath)) content = extract_text(eval_xpath_list(result, content_xpath))
+2
View File
@@ -35,6 +35,7 @@ from .keyvalue import KeyValue
from .code import Code from .code import Code
from .paper import Paper from .paper import Paper
from .file import File from .file import File
from .image import Image
class ResultList(list[Result | LegacyResult], abc.ABC): class ResultList(list[Result | LegacyResult], abc.ABC):
@@ -50,6 +51,7 @@ class ResultList(list[Result | LegacyResult], abc.ABC):
Code = Code Code = Code
Paper = Paper Paper = Paper
File = File File = File
Image = Image
MainResult = MainResult MainResult = MainResult
Result = Result Result = Result
Translations = Translations Translations = Translations
+44
View File
@@ -0,0 +1,44 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Typification of the *image* results. Results of this type are rendered in
the :origin:`images.html <searx/templates/simple/result_templates/images.html>`
template.
.. autoclass:: Image
:members:
:show-inheritance:
"""
__all__ = ["Image"]
import typing as t
from ._base import MainResult
@t.final
class Image(MainResult, kw_only=True):
"""Result type suitable for displaying images.
The images are displayed as small thumbnails in the main results list.
Clicking on the preview opens a gallery view in which all further metadata
for the image is displayed."""
template: str = "images.html"
thumbnail_src: str = ""
"""URL of a preview of the image."""
resolution: str = ""
"""The resolution of the image (e.g. ``1920 x 1080`` pixel)"""
img_format: str = ""
"""The format of the image (e.g. ``png``)."""
source: str = ""
"""Source of the image."""
filesize: str = ""
"""Size of bytes in :py:obj:`human readable <searx.humanize_bytes>` notation
(e.g. ``1MB`` for ``1024*1024`` Bytes filesize)."""
+4 -4
View File
@@ -1697,10 +1697,6 @@ engines:
# image proxy of https://pixiv.perennialte.ch # image proxy of https://pixiv.perennialte.ch
# - https://pximg.perennialte.ch # - https://pximg.perennialte.ch
- name: podcastindex
engine: podcastindex
shortcut: podcast
# Required dependency: psychopg2 # Required dependency: psychopg2
# - name: postgresql # - name: postgresql
# engine: postgresql # engine: postgresql
@@ -1798,6 +1794,7 @@ engines:
engine: qwant engine: qwant
shortcut: qw shortcut: qw
categories: [general, web] categories: [general, web]
disabled: true
- name: qwant news - name: qwant news
qwant_categ: news qwant_categ: news
@@ -1805,6 +1802,7 @@ engines:
shortcut: qwn shortcut: qwn
categories: news categories: news
network: qwant network: qwant
disabled: true
- name: qwant images - name: qwant images
qwant_categ: images qwant_categ: images
@@ -1812,6 +1810,7 @@ engines:
shortcut: qwi shortcut: qwi
categories: [images, web] categories: [images, web]
network: qwant network: qwant
disabled: true
- name: qwant videos - name: qwant videos
qwant_categ: videos qwant_categ: videos
@@ -1819,6 +1818,7 @@ engines:
shortcut: qwv shortcut: qwv
categories: [videos, web] categories: [videos, web]
network: qwant network: qwant
disabled: true
# - name: library # - name: library
# engine: recoll # engine: recoll
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long