From 783a094625ba29a8e348f4b43f37e460d1ca4cea Mon Sep 17 00:00:00 2001 From: Bnyro Date: Thu, 3 Sep 2026 17:15:14 +0200 Subject: [PATCH] [feat] engines: add xprivo - general, news, videos (#6641) Add support for xprivo.com. Xprivo has a rate-limit of smth like 20 requests per day without an API key. However, if you use a proxy like the IPv6 rotating proxy [1] you basically get unlimited requests. [1] https://github.com/vojkovic/http-proxy-ipv6-pool-docker --- searx/engines/xprivo.py | 85 +++++++++++++++++++++++++++++++++++++++++ searx/settings.yml | 22 +++++++++++ 2 files changed, 107 insertions(+) create mode 100644 searx/engines/xprivo.py diff --git a/searx/engines/xprivo.py b/searx/engines/xprivo.py new file mode 100644 index 000000000..8abb4474a --- /dev/null +++ b/searx/engines/xprivo.py @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +"""XPrivo""" + +import json +import typing as t + +from searx.result_types import EngineResults + +if t.TYPE_CHECKING: + from searx.extended_types import SXNG_Response + from searx.search.processors import OnlineParams + + +about = { + "website": 'https://www.xprivo.com', + "wikidata_id": None, + "official_api_documentation": None, + "use_official_api": False, + "require_api_key": False, + "results": 'JSON', +} + +categories = ["general"] +paging = True +time_range_support = True + +base_url = "https://www.xprivo.com" +_page_size = 10 # not configurable + +api_key = "API_KEY_XPRIVO" + +XPrivoSearchType = t.Literal["all", "news", "videos"] +xprivo_search_type: XPrivoSearchType = "all" + + +def setup(_): + if xprivo_search_type not in t.get_args(XPrivoSearchType): + raise ValueError(f"invalid search type: {xprivo_search_type}") + + +def request(query: str, params: "OnlineParams"): + params["url"] = f"{base_url}/v1/searchengine/completions" + params["headers"].update({"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}) + + params["method"] = "POST" + params["json"] = { + "query": query, + "mode": "classic", + "search_type": xprivo_search_type, + "time_filter": params["time_range"], + "region": "", + "offset": (params["pageno"] - 1) * _page_size, + "source": "", + } + + +def response(resp: "SXNG_Response") -> EngineResults: + res = EngineResults() + + # response is a Server-Side Events (SSE) stream + data_raw = resp.text.split("\n", 1)[0].removeprefix("data: ") + data = json.loads(data_raw) + + result: dict[str, str] + for result in data["results"]: + if xprivo_search_type == "videos": + res.add( + res.types.Video( + url=result["url"], + title=result["title"], + content=result["snippet"], + thumbnail=result.get("og_image") or "", + ) + ) + else: + res.add( + res.types.MainResult( + url=result["url"], + title=result["title"], + content=result["snippet"], + thumbnail=result.get("og_image") or "", + ) + ) + + return res diff --git a/searx/settings.yml b/searx/settings.yml index c8a15a4c2..3289b5b9e 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -3231,6 +3231,28 @@ engines: description: "Canadian search engine with its own index" results: HTML + - name: xprivo + engine: xprivo + shortcut: xpr + disabled: true + inactive: true + + - name: xprivo news + engine: xprivo + categories: news + xprivo_search_type: news + shortcut: xprn + disabled: true + inactive: true + + - name: xprivo videos + engine: xprivo + categories: videos + xprivo_search_type: videos + shortcut: xprv + disabled: true + inactive: true + - name: zapmeta engine: xpath shortcut: zpm