53 Commits

Author SHA1 Message Date
searxng-bot 6d93543019 [l10n] update translations from Weblate
207f98ecc - 2026-05-26 - mustafa-phd <mustafa-phd@noreply.codeberg.org>
3b51fbca7 - 2026-05-25 - Amirkhandrend-Nicest-XII <amirkhandrend-nicest-xii@noreply.codeberg.org>
2026-05-29 08:23:23 +00:00
github-actions[bot] 4ea5c57a84 [data] update searx.data - update_firefox_version.py (#6161) 2026-05-29 06:25:18 +02:00
github-actions[bot] 6917395dc1 [data] update searx.data - update_wikidata_units.py (#6160) 2026-05-29 06:24:50 +02:00
github-actions[bot] 128e28fe3f [data] update searx.data - update_gsa_useragents.py (#6158) 2026-05-29 06:24:11 +02:00
github-actions[bot] fb3ed5b081 [data] update searx.data - update_ahmia_blacklist.py (#6159) 2026-05-29 06:22:55 +02:00
github-actions[bot] 4ebe6b90d6 [data] update searx.data - update_currencies.py (#6162) 2026-05-29 06:21:55 +02:00
github-actions[bot] 0657217a3e [data] update searx.data - update_engine_descriptions.py (#6163) 2026-05-29 06:20:57 +02:00
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
Markus Heiser 28ef4f7447 [mod] hardening of the Result.filter_urls() method (#6117)
Exceptions in the execution of the callback must be caught / ignored and logged
on the ERROR log.

To test, apply this patch to provoke a ValueError exception::

    diff --git a/searx/data/tracker_patterns.py b/searx/data/tracker_patterns.py
    index ed4415bce..695ed05d2 100644
    --- a/searx/data/tracker_patterns.py
    +++ b/searx/data/tracker_patterns.py
    @@ -114,6 +114,7 @@ class TrackerPatternsDB:
             Returns bool ``True`` to use URL unchanged (``False`` to ignore URL).
             If URL should be modified, the returned string is the new URL to use.
             """
    +        raise ValueError("test callback exceptions")

             new_url = url
             parsed_new_url = urlparse(url=new_url)

Start a `make run` instance and query for example `amazon` .. have a look at the
ERROR log:

    ERROR   searx.result_types: filter_urls (field 'url'): ignore ValueError('test callback exceptions') from callback searx/data/tracker_patterns.py:117

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-25 18:12:40 +02:00
Bnyro cb4b70ac50 [fix] qwant news: results don't have any descriptions (#6135)
BTW: fix some typecast issues
2026-05-25 18:04:14 +02:00
Markus Heiser e29e861e2c [fix] bing engines - geoblocking in China (#6134)
In regions like China, the domain must be adjusted to avoid a redirect.

- https://github.com/searxng/searxng/issues/5243
- https://github.com/searxng/searxng/pull/5324
- https://github.com/searxng/searxng/pull/6133

Suggested / tested by @hubutui in https://github.com/searxng/searxng/pull/6133#issuecomment-4534637069

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-25 17:05:08 +02:00
Markus Heiser 89b89a88fe [mod] engine: MyMemory Translated - typification and html to text (#6132)
The implementation is normalized, type annotations are applied, and the results
are freed from the HTML markup (which is partially present).

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-25 16:38:06 +02:00
Bnyro 46071a011a [mod] qwant: remove web lite and improve request spoofing (#6127)
- https://lite.qwant.com seems to be dead.
- The request parameters were changed to match the ones from the Qwant website.
- Qwant is now set to inactive by default due to its strict rate-limits
2026-05-25 15:46:40 +02:00
Bnyro b0d8af96bf [feat] engines: add flaticon icons engine (#6122) 2026-05-25 13:41:44 +02:00
Markus Heiser dd27fce3b7 [unbload] drop meaningless field `number_of_results_xpath` from results (#6130)
In the result-list, the ``number_of_results`` indicate the number of hits in the
Index, they do not indicate how many results are in the answer.

In the past, search engines such as google or ddg had an indication on the first
page of a search term of how many hits there were for this term in total in
their index.

This info was added up in SearXNG and delivered under ``number_of_results``.
Nowadays the search engines no longer indicate how many hits there are in the
index and so this field in SearXNG is also superfluous.

- https://github.com/searxng/searxng/issues/2457#issuecomment-2566181574
- https://github.com/searxng/searxng/issues/2987
- https://github.com/searxng/searxng/issues/5034

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-25 12:43:02 +02:00
Markus Heiser efc305b7f9 [mod] normalize variable name for the max number of results per request (#6131)
[mod] normalize variable name for the max number of results per request

In the past, we have used different names for the variable that specifies the
maximum number of hits in the outgoing request.

- ``page_size``
- ``number_of_results``
- ``nb_per_page``

Since *page_size* is the most accurate term and is also used in the XPath
engines, all other engines are adjusted accordingly within this
patch .. documentation adjusted accordingly.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-25 12:41:31 +02:00
Bnyro 323ce76004 [fix] startpage: all requests get blocked with CAPTCHA
Changes:
- Setting the "abp" query parameter causes instant blocks, it's no longer
used at Startpage
- The safesearch map changed for both the request form and the cookies. As
we were sending invalid values, that also made it easier to detect us
2026-05-23 09:43:17 +02:00
Bnyro dfc2da707b [fix] mojeek: access denied because of wrong request parameters 2026-05-23 09:43:03 +02:00
Bnyro fc90c5b09c [fix] yep: api path changed 2026-05-22 21:38:26 +02:00
dependabot[bot] c57f772ad0 [upd] github-actions: Bump github/codeql-action from 4.35.4 to 4.35.5 (#6114)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.4 to 4.35.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/68bde559dea0fdcac2102bfdf6230c5f70eb485e...9e0d7b8d25671d64c341c19c0152d693099fb5ba)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 14:50:44 +02:00
dependabot[bot] b8498d7891 [upd] github-actions: Bump docker/scout-action from 1.20.4 to 1.21.0 (#6112)
Bumps [docker/scout-action](https://github.com/docker/scout-action) from 1.20.4 to 1.21.0.
- [Release notes](https://github.com/docker/scout-action/releases)
- [Commits](https://github.com/docker/scout-action/compare/bacf462e8d090c09660de30a6ccc718035f961e3...cd72f264beff1cd72735de31148b9d3244a0234a)

---
updated-dependencies:
- dependency-name: docker/scout-action
  dependency-version: 1.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 14:50:31 +02:00
github-actions[bot] d791a3906a [l10n] update translations from Weblate (#6116) 2026-05-22 14:29:10 +02:00
dependabot[bot] 295e0bffaf [upd] pypi: Bump the minor group with 3 updates (#6113)
Bumps the minor group with 3 updates: [certifi](https://github.com/certifi/python-certifi), [lxml](https://github.com/lxml/lxml) and [basedpyright](https://github.com/detachhead/basedpyright).


Updates `certifi` from 2026.4.22 to 2026.5.20
- [Commits](https://github.com/certifi/python-certifi/compare/2026.04.22...2026.05.20)

Updates `lxml` from 6.1.0 to 6.1.1
- [Release notes](https://github.com/lxml/lxml/releases)
- [Changelog](https://github.com/lxml/lxml/blob/master/CHANGES.txt)
- [Commits](https://github.com/lxml/lxml/compare/lxml-6.1.0...lxml-6.1.1)

Updates `basedpyright` from 1.39.4 to 1.39.5
- [Release notes](https://github.com/detachhead/basedpyright/releases)
- [Commits](https://github.com/detachhead/basedpyright/compare/v1.39.4...v1.39.5)
2026-05-22 14:28:09 +02:00
Bnyro b9340f50c2 [fix] preferences: opening preferences page is very slow (multiple seconds on bad hardware) (#6090)
I've been profiling the `/preferences` endpoint using werkzeug's
`ProfilerMiddleware` (i.e. just do `app.wsgi_app = ProfilerMiddleware(app.wsgi_app)`)
and look at the outputs in the terminal when doing `make run`.

It turns out that 95%+ of the time spent were inside babel's
Locale parsing (> 700ms on my machine). That's because, when opening the settings,
we loaded the full engine traits of each engine and checked if it matches
the user-defined search language. As we have 250+ engines, and babel is
very slow when parsing Locale's, this took a very long time.

By removing this feature that shows whether the selected search language
is supported by the engine, the load time went down from 800ms to 50ms
on my machine (which is still very slow, but well, that's future work on
optimizing).
2026-05-21 21:15:09 +02:00
Markus Heiser d3deacc6d4 [mod] engine fyyd: typing added, no functional change (#6103)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-21 12:06:55 +02:00
Bnyro d8f74af3d1 [mod] engine 500px: calc cursor instead of relying on pageInfo (#6091) 2026-05-21 07:31:13 +02:00
Bnyro 24b1a1b6a8 [feat] engines: add 500px.com engine (#6091) 2026-05-21 07:31:13 +02:00
Bnyro d7e8b7cd18 [feat] engines: add cara.app engine (#6092) 2026-05-17 18:39:47 +02:00
Markus Heiser f26e450778 [fix] engine: google-news - Google pushed a frontend update (#5984)
Around March 9 - 10, 2026, Google pushed a frontend update to Google News that
completely changed the HTML structure of search results.

This is a complete overhaul of the Google News engine.

- The real URL is encoded in the "jslog" attribute.
  @SeriousConcept1134: the attribute is a base64 encoded JSON
- CEID list is updated
- The typification was pushed forward

Related:

- https://github.com/searxng/searxng/issues/5852#issuecomment-4254438184
- https://github.com/searxng/searxng/issues/5852#issuecomment-4265598833

Closes: https://github.com/searxng/searxng/issues/5852
Suggested-by: SeriousConcept1134

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-17 15:27:00 +02:00
Bnyro dce3bb69bb [chore] settings.yml: set broken engines yahoo and karmasearch to inactive 2026-05-16 16:04:36 +02:00
Bnyro de49d27846 [fix] yandex images: crashes when parsing images without fallback source (#6084) 2026-05-16 15:53:23 +02:00
Bnyro 16a7537bfd [chore] engines: remove ask.com (service was discontinued) (#6083)
Source: https://www.ask.com/
2026-05-16 15:36:47 +02:00
Markus Heiser afafca93f3 [fix] engine wikidata - fails to initialize with HTTP 403 (#6081)
In order not to be further blocked, the WIKIDATA_PROPERTIES are cached, which
drastically reduces the WD-SQL request.

BTW: improve type hints

Closes: https://github.com/searxng/searxng/issues/6051

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-15 16:21:47 +02:00
dependabot[bot] 240f403d93 [upd] web-client (simple): Bump the minor group (#6080)
Bumps the minor group in /client/simple with 4 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [stylelint](https://github.com/stylelint/stylelint) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).

Updates `@biomejs/biome` from 2.4.14 to 2.4.15
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.15/packages/@biomejs/biome)

Updates `@types/node` from 25.6.2 to 25.8.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `stylelint` from 17.11.0 to 17.11.1
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/17.11.0...17.11.1)

Updates `vite` from 8.0.11 to 8.0.13
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.13/packages/vite)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.4.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 25.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: stylelint
  dependency-version: 17.11.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: vite
  dependency-version: 8.0.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 14:19:28 +02:00
dependabot[bot] 9b30ae005b [upd] pypi: Bump the minor group with 2 updates (#6079)
Bumps the minor group with 2 updates: [selenium](https://github.com/SeleniumHQ/Selenium) and [basedpyright](https://github.com/detachhead/basedpyright).


Updates `selenium` from 4.43.0 to 4.44.0
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/compare/selenium-4.43.0...selenium-4.44.0)

Updates `basedpyright` from 1.39.3 to 1.39.4
- [Release notes](https://github.com/detachhead/basedpyright/releases)
- [Commits](https://github.com/detachhead/basedpyright/compare/v1.39.3...v1.39.4)

---
updated-dependencies:
- dependency-name: selenium
  dependency-version: 4.44.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: basedpyright
  dependency-version: 1.39.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
2026-05-15 11:12:57 +02:00
Arnaud Jeannin 790683bbd7 [fix] google: improve CAPTCHA detection (#5922)
- Detect HTTP 302 responses (Google redirecting to /sorry/index
  without the HTTP client following the redirect)
- Detect short HTML responses (<2000 bytes) containing "/sorry/"
  links (meta-refresh or JS redirect variants)

Instances with rotating IPs can set the `suspended_times.SearxEngineCaptcha` to
0 in the search settings [1], the next request will typically use a different
outgoing IP when rotating proxies are configured

[1] https://docs.searxng.org/admin/settings/settings_search.html
2026-05-15 09:25:13 +02:00
Bnyro 52b446b4ad [make] update searx.data.traits (#6075) 2026-05-15 08:37:11 +02:00
Bnyro 6cee4b8947 [feat] yep: add support for selecting search language (#6075) 2026-05-15 08:37:11 +02:00
Markus Heiser 8e5aa9d394 [doc] Development Quickstart: documentation of the tools (dev.env)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-13 13:55:50 +02:00
Markus Heiser cf4d7e31c4 [mod] update_engine_traits.py: improve type annotations
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-13 13:55:50 +02:00
Markus Heiser 471f2b205f [feat] update_engine_traits.py: add option to update engines selective
Previously, `update_engine_traits.py` would fetch traits for all engines, which
is very slow and by side-effect touches engine data that are unrelated to the
engine you're currently working on.

To be faster with developing `update_engine_traits.py` supports now engine
arguments.

To test, jump into the developer environment and run the script::

    $ ./manage dev.env
    (dev.env)$ ./searxng_extra/update/update_engine_traits.py --help

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-05-13 13:55:50 +02:00
Markus Heiser 09829b1ccc [fix] installation instructions - launch SearXNG by python -m (#6078)
[1] https://github.com/searxng/searxng/issues/126#issuecomment-4433874986

Suggested-by: @virtadpt in [1]
2026-05-13 11:34:31 +02:00
dependabot[bot] df1f24fb7f [upd] web-client (simple): Bump the minor group (#6056)
Bumps the minor group in /client/simple with 4 updates: [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [stylelint](https://github.com/stylelint/stylelint) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).

Updates `@biomejs/biome` from 2.4.13 to 2.4.14
- [Release notes](https://github.com/biomejs/biome/releases)
- [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md)
- [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.4.14/packages/@biomejs/biome)

Updates `@types/node` from 25.6.0 to 25.6.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `stylelint` from 17.9.1 to 17.11.0
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/17.9.1...17.11.0)

Updates `vite` from 8.0.10 to 8.0.11
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.11/packages/vite)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.4.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 25.6.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: stylelint
  dependency-version: 17.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: vite
  dependency-version: 8.0.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-10 15:34:19 +02:00
dependabot[bot] 0cba32c15f [upd] pypi: Bump markdown-it-py from 4.0.0 to 4.2.0 in the minor group (#6054)
Bumps the minor group with 1 update: [markdown-it-py](https://github.com/executablebooks/markdown-it-py).


Updates `markdown-it-py` from 4.0.0 to 4.2.0
- [Release notes](https://github.com/executablebooks/markdown-it-py/releases)
- [Changelog](https://github.com/executablebooks/markdown-it-py/blob/master/CHANGELOG.md)
- [Commits](https://github.com/executablebooks/markdown-it-py/compare/v4.0.0...v4.2.0)

---
updated-dependencies:
- dependency-name: markdown-it-py
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor
2026-05-09 08:43:28 +02:00
Tommaso Colella 849e17e431 [fix] 360search: improve empty results set management and increase engine timeout (#6058) 2026-05-09 08:35:21 +02:00
github-actions[bot] d8ab61a9e0 [l10n] update translations from Weblate (#6057)
94e9ade46 - 2026-05-04 - Aindriú Mac Giolla Eoin <aindriu80@noreply.codeberg.org>
883cac081 - 2026-04-30 - alexgabi <alexgabi@noreply.codeberg.org>

Co-authored-by: searxng-bot <searxng-bot@users.noreply.github.com>
2026-05-08 14:27:49 +02:00
dependabot[bot] 7eb130b1a8 [upd] github-actions: Bump github/codeql-action from 4.35.2 to 4.35.4 (#6055)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.35.2 to 4.35.4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/95e58e9a2cdfd71adc6e0353d5c52f41a045d225...68bde559dea0fdcac2102bfdf6230c5f70eb485e)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-08 14:25:27 +02:00
215 changed files with 10217 additions and 6577 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
persist-credentials: "false" persist-credentials: "false"
- name: Sync GHCS from Docker Scout - name: Sync GHCS from Docker Scout
uses: docker/scout-action@bacf462e8d090c09660de30a6ccc718035f961e3 # v1.20.4 uses: docker/scout-action@cd72f264beff1cd72735de31148b9d3244a0234a # v1.21.0
with: with:
organization: "searxng" organization: "searxng"
dockerhub-user: "${{ secrets.DOCKER_USER }}" dockerhub-user: "${{ secrets.DOCKER_USER }}"
@@ -41,6 +41,6 @@ jobs:
write-comment: "false" write-comment: "false"
- name: Upload SARIFs - name: Upload SARIFs
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
with: with:
sarif_file: "./scout.sarif" sarif_file: "./scout.sarif"
+140 -151
View File
@@ -15,8 +15,8 @@
"swiped-events": "1.2.0" "swiped-events": "1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.4.13", "@biomejs/biome": "2.4.15",
"@types/node": "^25.6.0", "@types/node": "^25.8.0",
"browserslist": "^4.28.2", "browserslist": "^4.28.2",
"browserslist-to-esbuild": "^2.1.1", "browserslist-to-esbuild": "^2.1.1",
"edge.js": "^6.5.0", "edge.js": "^6.5.0",
@@ -24,12 +24,12 @@
"mathjs": "^15.2.0", "mathjs": "^15.2.0",
"sharp": "~0.34.5", "sharp": "~0.34.5",
"sort-package-json": "^3.6.1", "sort-package-json": "^3.6.1",
"stylelint": "^17.9.1", "stylelint": "^17.11.1",
"stylelint-config-standard-less": "^4.1.0", "stylelint-config-standard-less": "^4.1.0",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^4.0.1", "svgo": "^4.0.1",
"typescript": "~6.0.3", "typescript": "~6.0.3",
"vite": "^8.0.10", "vite": "^8.0.13",
"vite-bundle-analyzer": "^1.3.8" "vite-bundle-analyzer": "^1.3.8"
} }
}, },
@@ -69,9 +69,9 @@
} }
}, },
"node_modules/@biomejs/biome": { "node_modules/@biomejs/biome": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.15.tgz",
"integrity": "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA==", "integrity": "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw==",
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"bin": { "bin": {
@@ -85,20 +85,20 @@
"url": "https://opencollective.com/biome" "url": "https://opencollective.com/biome"
}, },
"optionalDependencies": { "optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.4.13", "@biomejs/cli-darwin-arm64": "2.4.15",
"@biomejs/cli-darwin-x64": "2.4.13", "@biomejs/cli-darwin-x64": "2.4.15",
"@biomejs/cli-linux-arm64": "2.4.13", "@biomejs/cli-linux-arm64": "2.4.15",
"@biomejs/cli-linux-arm64-musl": "2.4.13", "@biomejs/cli-linux-arm64-musl": "2.4.15",
"@biomejs/cli-linux-x64": "2.4.13", "@biomejs/cli-linux-x64": "2.4.15",
"@biomejs/cli-linux-x64-musl": "2.4.13", "@biomejs/cli-linux-x64-musl": "2.4.15",
"@biomejs/cli-win32-arm64": "2.4.13", "@biomejs/cli-win32-arm64": "2.4.15",
"@biomejs/cli-win32-x64": "2.4.13" "@biomejs/cli-win32-x64": "2.4.15"
} }
}, },
"node_modules/@biomejs/cli-darwin-arm64": { "node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.15.tgz",
"integrity": "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg==", "integrity": "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -113,9 +113,9 @@
} }
}, },
"node_modules/@biomejs/cli-darwin-x64": { "node_modules/@biomejs/cli-darwin-x64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.15.tgz",
"integrity": "sha512-BKrJklbaFN4p1Ts4kPBczo+PkbsHQg57kmJ+vON9u2t6uN5okYHaSr7h/MutPCWQgg2lglaWoSmm+zhYW+oOkg==", "integrity": "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -130,9 +130,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64": { "node_modules/@biomejs/cli-linux-arm64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.15.tgz",
"integrity": "sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ==", "integrity": "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -147,9 +147,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64-musl": { "node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.15.tgz",
"integrity": "sha512-U5MsuBQW25dXaYtqWWSPM3P96H6Y+fHuja3TQpMNnylocHW0tEbtFTDlUj6oM+YJLntvEkQy4grBvQNUD4+RCg==", "integrity": "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -164,9 +164,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64": { "node_modules/@biomejs/cli-linux-x64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.15.tgz",
"integrity": "sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ==", "integrity": "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -181,9 +181,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64-musl": { "node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.15.tgz",
"integrity": "sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ==", "integrity": "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -198,9 +198,9 @@
} }
}, },
"node_modules/@biomejs/cli-win32-arm64": { "node_modules/@biomejs/cli-win32-arm64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.15.tgz",
"integrity": "sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A==", "integrity": "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -215,9 +215,9 @@
} }
}, },
"node_modules/@biomejs/cli-win32-x64": { "node_modules/@biomejs/cli-win32-x64": {
"version": "2.4.13", "version": "2.4.15",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.13.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.15.tgz",
"integrity": "sha512-tTcMkXyBrmHi9BfrD2VNHs/5rYIUKETqsBlYOvSAABwBkJhSDVb5e7wPukftsQbO3WzQkXe6kaztC6WtUOXSoQ==", "integrity": "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1023,9 +1023,9 @@
} }
}, },
"node_modules/@oxc-project/types": { "node_modules/@oxc-project/types": {
"version": "0.127.0", "version": "0.130.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz",
"integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
@@ -1107,9 +1107,9 @@
} }
}, },
"node_modules/@rolldown/binding-android-arm64": { "node_modules/@rolldown/binding-android-arm64": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz",
"integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1124,9 +1124,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-arm64": { "node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz",
"integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1141,9 +1141,9 @@
} }
}, },
"node_modules/@rolldown/binding-darwin-x64": { "node_modules/@rolldown/binding-darwin-x64": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz",
"integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1158,9 +1158,9 @@
} }
}, },
"node_modules/@rolldown/binding-freebsd-x64": { "node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz",
"integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1175,9 +1175,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm-gnueabihf": { "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz",
"integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1192,9 +1192,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-gnu": { "node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz",
"integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1209,9 +1209,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-arm64-musl": { "node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz",
"integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1226,9 +1226,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-ppc64-gnu": { "node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz",
"integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1243,9 +1243,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-s390x-gnu": { "node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz",
"integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -1260,9 +1260,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-gnu": { "node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz",
"integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1277,9 +1277,9 @@
} }
}, },
"node_modules/@rolldown/binding-linux-x64-musl": { "node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz",
"integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1294,9 +1294,9 @@
} }
}, },
"node_modules/@rolldown/binding-openharmony-arm64": { "node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz",
"integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1311,9 +1311,9 @@
} }
}, },
"node_modules/@rolldown/binding-wasm32-wasi": { "node_modules/@rolldown/binding-wasm32-wasi": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz",
"integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==",
"cpu": [ "cpu": [
"wasm32" "wasm32"
], ],
@@ -1330,9 +1330,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-arm64-msvc": { "node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz",
"integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1347,9 +1347,9 @@
} }
}, },
"node_modules/@rolldown/binding-win32-x64-msvc": { "node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz",
"integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1364,9 +1364,9 @@
} }
}, },
"node_modules/@rolldown/pluginutils": { "node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -1511,9 +1511,9 @@
} }
}, },
"node_modules/@tybys/wasm-util": { "node_modules/@tybys/wasm-util": {
"version": "0.10.1", "version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@@ -1522,13 +1522,13 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "25.6.0", "version": "25.8.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz",
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.19.0" "undici-types": ">=7.24.0 <7.24.7"
} }
}, },
"node_modules/@types/pluralize": { "node_modules/@types/pluralize": {
@@ -2434,9 +2434,9 @@
} }
}, },
"node_modules/get-east-asian-width": { "node_modules/get-east-asian-width": {
"version": "1.5.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
"integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -2747,16 +2747,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-what": { "node_modules/is-what": {
"version": "4.1.16", "version": "4.1.16",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
@@ -3475,9 +3465,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.10", "version": "8.5.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -3741,14 +3731,14 @@
} }
}, },
"node_modules/rolldown": { "node_modules/rolldown": {
"version": "1.0.0-rc.17", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz",
"integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@oxc-project/types": "=0.127.0", "@oxc-project/types": "=0.130.0",
"@rolldown/pluginutils": "1.0.0-rc.17" "@rolldown/pluginutils": "^1.0.0"
}, },
"bin": { "bin": {
"rolldown": "bin/cli.mjs" "rolldown": "bin/cli.mjs"
@@ -3757,21 +3747,21 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-android-arm64": "1.0.1",
"@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.1",
"@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.1",
"@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.1",
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.1",
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.1",
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.1",
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.1",
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.1",
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.1",
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.1",
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.1",
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.1",
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.1",
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" "@rolldown/binding-win32-x64-msvc": "1.0.1"
} }
}, },
"node_modules/run-parallel": { "node_modules/run-parallel": {
@@ -4010,9 +4000,9 @@
} }
}, },
"node_modules/string-width": { "node_modules/string-width": {
"version": "8.2.0", "version": "8.2.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
"integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4059,9 +4049,9 @@
} }
}, },
"node_modules/stylelint": { "node_modules/stylelint": {
"version": "17.9.1", "version": "17.11.1",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.9.1.tgz", "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.11.1.tgz",
"integrity": "sha512-THTmnAPJTrg/JhkTWZlSyrO+HUYMx6ELthIHeMyD2WOKqXIJUFQv2Yxn91bvUrZdbBJaW2dUuQdPST2wcQ6C3g==", "integrity": "sha512-+smN/HqVTggUx3iuAzOi9fPh8SrH+cJWlZrYVldXoJ06orWBhZ4Ue/QEp64oei6pVrAh4w3tG+Y12Vw7MbCFRQ==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -4096,17 +4086,16 @@
"html-tags": "^5.1.0", "html-tags": "^5.1.0",
"ignore": "^7.0.5", "ignore": "^7.0.5",
"import-meta-resolve": "^4.2.0", "import-meta-resolve": "^4.2.0",
"is-plain-object": "^5.0.0",
"mathml-tag-names": "^4.0.0", "mathml-tag-names": "^4.0.0",
"meow": "^14.1.0", "meow": "^14.1.0",
"micromatch": "^4.0.8", "micromatch": "^4.0.8",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"postcss": "^8.5.9", "postcss": "^8.5.14",
"postcss-safe-parser": "^7.0.1", "postcss-safe-parser": "^7.0.1",
"postcss-selector-parser": "^7.1.1", "postcss-selector-parser": "^7.1.1",
"postcss-value-parser": "^4.2.0", "postcss-value-parser": "^4.2.0",
"string-width": "^8.2.0", "string-width": "^8.2.1",
"supports-hyperlinks": "^4.4.0", "supports-hyperlinks": "^4.4.0",
"svg-tags": "^1.0.0", "svg-tags": "^1.0.0",
"table": "^6.9.0", "table": "^6.9.0",
@@ -4478,9 +4467,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.19.2", "version": "7.24.6",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -4545,16 +4534,16 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "8.0.10", "version": "8.0.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz",
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.4", "picomatch": "^4.0.4",
"postcss": "^8.5.10", "postcss": "^8.5.14",
"rolldown": "1.0.0-rc.17", "rolldown": "1.0.1",
"tinyglobby": "^0.2.16" "tinyglobby": "^0.2.16"
}, },
"bin": { "bin": {
@@ -4571,7 +4560,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0", "@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.1.0", "@vitejs/devtools": "^0.1.18",
"esbuild": "^0.27.0 || ^0.28.0", "esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0", "jiti": ">=1.21.0",
"less": "^4.0.0", "less": "^4.0.0",
+4 -4
View File
@@ -29,8 +29,8 @@
"swiped-events": "1.2.0" "swiped-events": "1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.4.13", "@biomejs/biome": "2.4.15",
"@types/node": "^25.6.0", "@types/node": "^25.8.0",
"browserslist": "^4.28.2", "browserslist": "^4.28.2",
"browserslist-to-esbuild": "^2.1.1", "browserslist-to-esbuild": "^2.1.1",
"edge.js": "^6.5.0", "edge.js": "^6.5.0",
@@ -38,12 +38,12 @@
"mathjs": "^15.2.0", "mathjs": "^15.2.0",
"sharp": "~0.34.5", "sharp": "~0.34.5",
"sort-package-json": "^3.6.1", "sort-package-json": "^3.6.1",
"stylelint": "^17.9.1", "stylelint": "^17.11.1",
"stylelint-config-standard-less": "^4.1.0", "stylelint-config-standard-less": "^4.1.0",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^4.0.1", "svgo": "^4.0.1",
"typescript": "~6.0.3", "typescript": "~6.0.3",
"vite": "^8.0.10", "vite": "^8.0.13",
"vite-bundle-analyzer": "^1.3.8" "vite-bundle-analyzer": "^1.3.8"
} }
} }
+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";
+1 -1
View File
@@ -169,7 +169,7 @@ ${fedora_build}
$ sudo -H -u ${SERVICE_USER} -i $ sudo -H -u ${SERVICE_USER} -i
(${SERVICE_USER})$ cd ${SEARXNG_SRC} (${SERVICE_USER})$ cd ${SEARXNG_SRC}
(${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\" (${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\"
(${SERVICE_USER})$ python searx/webapp.py (${SERVICE_USER})$ python -m searx.webapp
# disable debug # disable debug
$ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\" $ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\"
+1 -1
View File
@@ -107,7 +107,7 @@ module:
======================= =========== =========================================== ======================= =========== ===========================================
base_url string base-url, can be overwritten to use same base_url string base-url, can be overwritten to use same
engine on other URL engine on other URL
number_of_results int maximum number of results per request page_size int maximum number of results per request
language string ISO code of language and country like en_US language string ISO code of language and country like en_US
api_key string api-key if required by engine api_key string api-key if required by engine
======================= =========== =========================================== ======================= =========== ===========================================
+8
View File
@@ -0,0 +1,8 @@
.. _500px engine:
=====
500px
=====
.. automodule:: searx.engines.500px
:members:
+8
View File
@@ -0,0 +1,8 @@
.. _cara engine:
===========
Cara Images
===========
.. automodule:: searx.engines.cara
:members:
+19 -1
View File
@@ -60,11 +60,29 @@ into the developer environment and start a python based HTTP server by::
$ ./manage dev.env $ ./manage dev.env
... ...
(dev.env)$ SEARXNG_DEBUG=1 python -m searx.webapp (dev.env)$ SEARXNG_DEBUG=1 searxng-run
Since this is a pure Python solution, you can set breakpoints in your code with Since this is a pure Python solution, you can set breakpoints in your code with
``pdb.set_trace()`` and the debugger will wait for you in the terminal prompt. ``pdb.set_trace()`` and the debugger will wait for you in the terminal prompt.
Any other script or command line provided by SearXNG can also be used in the
same environment, here are a few examples::
# tools related to favicons
(dev.env)$ python -m searx.favicons
# tools related to DATA stored in searx/data
(dev.env)$ python -m searx.data --help
# tools related to engines
(dev.env)$ python -m searx.enginelib --help
# to test one of the update scripts
(dev.env)$ searxng_extra/update/update_engine_traits.py --help
# to test the update of the wikidata units
(dev.env)$ searxng_extra/update/update_wikidata_units.py
.. sidebar:: further read .. sidebar:: further read
+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 -2
View File
@@ -4,7 +4,7 @@ cov-core==1.15.0
black==25.9.0 black==25.9.0
pylint==4.0.5 pylint==4.0.5
splinter==0.21.0 splinter==0.21.0
selenium==4.43.0 selenium==4.44.0
Sphinx==8.2.3;python_version <= "3.11" Sphinx==8.2.3;python_version <= "3.11"
Sphinx==9.1.0; python_version > "3.11" Sphinx==9.1.0; python_version > "3.11"
sphinx-issues==6.0.0 sphinx-issues==6.0.0
@@ -24,5 +24,5 @@ docutils>=0.21.2;python_version <= "3.11"
docutils>=0.22.4; python_version > "3.11" docutils>=0.22.4; python_version > "3.11"
parameterized==0.9.0 parameterized==0.9.0
granian[reload]==2.7.4 granian[reload]==2.7.4
basedpyright==1.39.3 basedpyright==1.39.5
types-lxml==2026.2.16 types-lxml==2026.2.16
+3 -3
View File
@@ -1,9 +1,9 @@
certifi==2026.4.22 certifi==2026.5.20
babel==2.18.0 babel==2.18.0
flask-babel==4.0.0 flask-babel==4.0.0
flask==3.1.3 flask==3.1.3
jinja2==3.1.6 jinja2==3.1.6
lxml==6.1.0 lxml==6.1.1
pygments==2.20.0 pygments==2.20.0
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
pyyaml==6.0.3 pyyaml==6.0.3
@@ -11,7 +11,7 @@ httpx[http2]==0.28.1
httpx-socks[asyncio]==0.10.0 httpx-socks[asyncio]==0.10.0
sniffio==1.3.1 sniffio==1.3.1
valkey==6.1.1 valkey==6.1.1
markdown-it-py==4.0.0 markdown-it-py==4.2.0
msgspec==0.21.1 msgspec==0.21.1
typer==0.25.1 typer==0.25.1
isodate==0.7.2 isodate==0.7.2
+7 -1
View File
@@ -114,7 +114,13 @@ class ExpireCacheStats:
if expire: if expire:
valid_until = datetime.datetime.fromtimestamp(expire).strftime("%Y-%m-%d %H:%M:%S") valid_until = datetime.datetime.fromtimestamp(expire).strftime("%Y-%m-%d %H:%M:%S")
c_kv += 1 c_kv += 1
lines.append(f"[{ctx_name:20s}] {valid_until} {key:12}" f" --> ({type(value).__name__}) {value} ") value_str = str(value)
if len(value_str) > 120:
value_str = f"{value_str[:120]} ..."
lines.append(
f"[{ctx_name:20s}] {valid_until} {key:12}"
f" --> ({type(value).__name__}:{len(value)}) {value_str} "
)
lines.append(f"Number of contexts: {c_ctx}") lines.append(f"Number of contexts: {c_ctx}")
lines.append(f"number of key/value pairs: {c_kv}") lines.append(f"number of key/value pairs: {c_kv}")
File diff suppressed because it is too large Load Diff
+61 -23
View File
@@ -84,6 +84,7 @@
"sr": "УАЕ дирхам", "sr": "УАЕ дирхам",
"sv": "Emiratisk dirham", "sv": "Emiratisk dirham",
"ta": "ஐக்கிய அரபு அமீரக திர்கம்", "ta": "ஐக்கிய அரபு அமீரக திர்கம்",
"te": "యూఏఈ దిర్హామ్",
"tr": "Birleşik Arap Emirlikleri dirhemi", "tr": "Birleşik Arap Emirlikleri dirhemi",
"tt": "БГӘ дирһәме", "tt": "БГӘ дирһәме",
"uk": "дирхам ОАЕ" "uk": "дирхам ОАЕ"
@@ -165,7 +166,7 @@
"pap": "lek albanes", "pap": "lek albanes",
"pl": "lek", "pl": "lek",
"pt": "lek", "pt": "lek",
"ro": "Lek", "ro": "lek albanez",
"ru": "албанский лек", "ru": "албанский лек",
"sk": "Albánsky lek", "sk": "Albánsky lek",
"sl": "albanski lek", "sl": "albanski lek",
@@ -738,6 +739,7 @@
"sr": "брунејски долар", "sr": "брунејски долар",
"sv": "Bruneisk dollar", "sv": "Bruneisk dollar",
"ta": "புரூணை டாலர்", "ta": "புரூணை டாலர்",
"te": "బ్రూనై డాలర్",
"th": "ดอลลาร์บรูไน", "th": "ดอลลาร์บรูไน",
"tr": "Brunei doları", "tr": "Brunei doları",
"tt": "Бруней доллары", "tt": "Бруней доллары",
@@ -911,6 +913,7 @@
"sr": "бутански нгултрум", "sr": "бутански нгултрум",
"sv": "Ngultrum", "sv": "Ngultrum",
"ta": "பூட்டானின் இங்குல்ட்ரம்", "ta": "பூட்டானின் இங்குல்ட்ரம்",
"te": "భూటానీస్ గుల్త్రమ్",
"th": "งุลตรัมภูฏาน", "th": "งุลตรัมภูฏาน",
"tr": "Ngultrum", "tr": "Ngultrum",
"tt": "ңгултрум", "tt": "ңгултрум",
@@ -989,7 +992,7 @@
"pa": "ਬੈਲਾਰੂਸੀ ਰੂਬਲ", "pa": "ਬੈਲਾਰੂਸੀ ਰੂਬਲ",
"pl": "Rubel białoruski", "pl": "Rubel białoruski",
"pt": "Rublo bielorrusso", "pt": "Rublo bielorrusso",
"ro": "Rublă belarusă", "ro": "rublă belarusă",
"ru": "Белорусский рубль", "ru": "Белорусский рубль",
"sk": "Bieloruský rubeľ", "sk": "Bieloruský rubeľ",
"sl": "beloruski rubelj", "sl": "beloruski rubelj",
@@ -1083,6 +1086,7 @@
"sr": "канадски долар", "sr": "канадски долар",
"sv": "kanadensisk dollar", "sv": "kanadensisk dollar",
"ta": "கனடா டொலர்", "ta": "கனடா டொலர்",
"te": "కెనడియన్ డాలర్",
"th": "ดอลลาร์แคนาดา", "th": "ดอลลาร์แคนาดา",
"tr": "Kanada doları", "tr": "Kanada doları",
"tt": "Канада дуллыры", "tt": "Канада дуллыры",
@@ -1231,6 +1235,7 @@
"sl": "čilski peso", "sl": "čilski peso",
"sr": "чилеански пезос", "sr": "чилеански пезос",
"sv": "Chilensk peso", "sv": "Chilensk peso",
"te": "చిలీ పెసో",
"th": "เปโซชิลี", "th": "เปโซชิลี",
"tr": "Şili pesosu", "tr": "Şili pesosu",
"tt": "Чили песосы", "tt": "Чили песосы",
@@ -1284,6 +1289,7 @@
"sr": "ренминби", "sr": "ренминби",
"sv": "Renminbi", "sv": "Renminbi",
"ta": "ரென்மின்பி", "ta": "ரென்மின்பி",
"te": "రెన్మిన్బి",
"th": "เหรินหมินปี้", "th": "เหรินหมินปี้",
"tr": "Renminbi", "tr": "Renminbi",
"tt": "юән", "tt": "юән",
@@ -1702,7 +1708,7 @@
"vi": "Bảng Ai Cập" "vi": "Bảng Ai Cập"
}, },
"ERN": { "ERN": {
"ar": اكفا", "ar": قفة",
"ca": "nakfa", "ca": "nakfa",
"cs": "Eritrejská nakfa", "cs": "Eritrejská nakfa",
"da": "Nakfa", "da": "Nakfa",
@@ -1776,11 +1782,11 @@
"bg": "евро", "bg": "евро",
"bn": "ইউরো", "bn": "ইউরো",
"ca": "euro", "ca": "euro",
"cs": "euro", "cs": "Euro",
"cy": "Ewro", "cy": "Ewro",
"da": "Euro", "da": "Euro",
"de": "Euro", "de": "Euro",
"en": "euro", "en": "Euro",
"eo": "eŭro", "eo": "eŭro",
"es": "Euro", "es": "Euro",
"et": "Euro", "et": "Euro",
@@ -1794,7 +1800,7 @@
"hu": "euró", "hu": "euró",
"ia": "Euro", "ia": "Euro",
"id": "Euro", "id": "Euro",
"it": "euro", "it": "Euro",
"ja": "ユーロ", "ja": "ユーロ",
"ko": "유로", "ko": "유로",
"lt": "Euras", "lt": "Euras",
@@ -1805,11 +1811,11 @@
"oc": "Èuro", "oc": "Èuro",
"pa": "ਯੂਰੋ", "pa": "ਯੂਰੋ",
"pap": "Euro", "pap": "Euro",
"pl": "euro", "pl": "Euro",
"pt": "Euro", "pt": "Euro",
"ro": "euro", "ro": "Euro",
"ru": "евро", "ru": "евро",
"sk": "euro", "sk": "Euro",
"sl": "evro", "sl": "evro",
"sr": "евро", "sr": "евро",
"sv": "Euro", "sv": "Euro",
@@ -1935,7 +1941,7 @@
"sk": "libra šterlingov", "sk": "libra šterlingov",
"sl": "funt šterling", "sl": "funt šterling",
"sr": "британска фунта", "sr": "британска фунта",
"sv": "Brittiskt pund", "sv": "brittiskt pund",
"ta": "பிரித்தானிய பவுண்டு", "ta": "பிரித்தானிய பவுண்டு",
"th": "ปอนด์สเตอร์ลิง", "th": "ปอนด์สเตอร์ลิง",
"tr": "İngiliz sterlini", "tr": "İngiliz sterlini",
@@ -2410,6 +2416,7 @@
"pl": "rupia indonezyjska", "pl": "rupia indonezyjska",
"pt": "rupia indonésia", "pt": "rupia indonésia",
"ru": "индонезийская рупия", "ru": "индонезийская рупия",
"sk": "Indonézska rupia",
"sl": "indonezijska rupija", "sl": "indonezijska rupija",
"sr": "индонежанска рупија", "sr": "индонежанска рупија",
"sv": "Rupiah", "sv": "Rupiah",
@@ -2463,6 +2470,7 @@
"sr": "нови израелски шекел", "sr": "нови израелски шекел",
"sv": "Shekel", "sv": "Shekel",
"ta": "புது இசுரேலிய சேக்கல்", "ta": "புது இசுரேலிய சேக்கல்",
"te": "ఇజ్రాయెల్ షెకెల్",
"tr": "Yeni İsrail Şekeli", "tr": "Yeni İsrail Şekeli",
"tt": "Исраил шекеле", "tt": "Исраил шекеле",
"uk": "ізраїльський новий шекель" "uk": "ізраїльський новий шекель"
@@ -2758,6 +2766,7 @@
"sr": "јапански јен", "sr": "јапански јен",
"sv": "yen", "sv": "yen",
"ta": "யென்", "ta": "யென்",
"te": "జపనీస్ యెన్",
"th": "เยน", "th": "เยน",
"tr": "Japon yeni", "tr": "Japon yeni",
"tt": "япон иенасы", "tt": "япон иенасы",
@@ -2823,6 +2832,7 @@
"ja": "キルギス・ソム", "ja": "キルギス・ソム",
"ko": "키르기스스탄 솜", "ko": "키르기스스탄 솜",
"lt": "somas", "lt": "somas",
"lv": "Kirgizstānas soms",
"nl": "Kirgizische som", "nl": "Kirgizische som",
"pa": "ਕਿਰਗਿਜ਼ਸਤਾਨੀ ਸੋਮ", "pa": "ਕਿਰਗਿਜ਼ਸਤਾਨੀ ਸੋਮ",
"pl": "som", "pl": "som",
@@ -3236,6 +3246,7 @@
"sr": "шриланчанска рупија", "sr": "шриланчанска рупија",
"sv": "Lankesisk rupie", "sv": "Lankesisk rupie",
"ta": "இலங்கை ரூபாய்", "ta": "இலங்கை ரூபாய்",
"te": "శ్రీలంక రూపాయి",
"tr": "Sri Lanka rupisi", "tr": "Sri Lanka rupisi",
"tt": "Шри-Ланка рупиясе", "tt": "Шри-Ланка рупиясе",
"uk": "ланкійська рупія", "uk": "ланкійська рупія",
@@ -3810,6 +3821,7 @@
"MXV": { "MXV": {
"de": "UNIDAD DE INVERSION", "de": "UNIDAD DE INVERSION",
"en": "unidad de inversión", "en": "unidad de inversión",
"eo": "Meksika unuo de investo",
"es": "Unidades de Inversión", "es": "Unidades de Inversión",
"ja": "メキシコ投資単位" "ja": "メキシコ投資単位"
}, },
@@ -3901,7 +3913,7 @@
"de": "Namibia-Dollar", "de": "Namibia-Dollar",
"en": "Namibian dollar", "en": "Namibian dollar",
"eo": "namibia dolaro", "eo": "namibia dolaro",
"es": "Dólar namibio", "es": "dólar namibio",
"fi": "Namibian dollari", "fi": "Namibian dollari",
"fr": "Dollar namibien", "fr": "Dollar namibien",
"ga": "dollar na Namaibe", "ga": "dollar na Namaibe",
@@ -4090,6 +4102,7 @@
"sr": "непалска рупија", "sr": "непалска рупија",
"sv": "Nepalesisk rupee", "sv": "Nepalesisk rupee",
"ta": "நேபாள ரூபாய்", "ta": "நேபாள ரூபாய்",
"te": "నేపాలీ రూపాయి",
"th": "รูปีเนปาล", "th": "รูปีเนปาล",
"tr": "Nepal rupisi", "tr": "Nepal rupisi",
"tt": "Непал рупиясе", "tt": "Непал рупиясе",
@@ -4325,6 +4338,7 @@
"sr": "филипински пезо", "sr": "филипински пезо",
"sv": "Filippinsk peso", "sv": "Filippinsk peso",
"ta": "பிலிப்பைன் பெசோ", "ta": "பிலிப்பைன் பெசோ",
"te": "ఫిలిప్పీన్ పెసో",
"th": "เปโซฟิลิปปินส์", "th": "เปโซฟิลิปปินส์",
"tr": "Filipinler pesosu", "tr": "Filipinler pesosu",
"tt": "Филипин писысы", "tt": "Филипин писысы",
@@ -4370,6 +4384,7 @@
"sr": "пакистанска рупија", "sr": "пакистанска рупија",
"sv": "Pakistansk rupee", "sv": "Pakistansk rupee",
"ta": "பாக்கித்தானிய ரூபாய்", "ta": "பாக்கித்தானிய ரூபாய்",
"te": "పాకిస్థానీ రూపాయి",
"tr": "Pakistan rupisi", "tr": "Pakistan rupisi",
"tt": "Пакстан рупиясе", "tt": "Пакстан рупиясе",
"uk": "пакистанська рупія", "uk": "пакистанська рупія",
@@ -4549,7 +4564,7 @@
"de": "rumänischer Leu", "de": "rumänischer Leu",
"en": "Romanian Leu", "en": "Romanian Leu",
"eo": "rumana leo", "eo": "rumana leo",
"es": "Leu rumano", "es": "leu rumano",
"et": "Rumeenia leu", "et": "Rumeenia leu",
"fi": "Romanian leu", "fi": "Romanian leu",
"fr": "leu roumain", "fr": "leu roumain",
@@ -4750,6 +4765,7 @@
"sr": "саудијски ријал", "sr": "саудијски ријал",
"sv": "Saudiarabisk rial", "sv": "Saudiarabisk rial",
"ta": "சவூதி ரியால்", "ta": "சவூதி ரியால்",
"te": "సౌదీ రియాల్",
"tr": "Suudi Arabistan riyali", "tr": "Suudi Arabistan riyali",
"tt": "Согуд риялы", "tt": "Согуд риялы",
"uk": "саудівський ріал", "uk": "саудівський ріал",
@@ -4948,6 +4964,7 @@
"sr": "сингапурски долар", "sr": "сингапурски долар",
"sv": "Singaporiansk dollar", "sv": "Singaporiansk dollar",
"ta": "சிங்கப்பூர் வெள்ளி", "ta": "சிங்கப்பூர் வெள்ளி",
"te": "సింగపూర్ డాలర్",
"th": "ดอลลาร์สิงคโปร์", "th": "ดอลลาร์สิงคโปร์",
"tr": "Singapur doları", "tr": "Singapur doları",
"tt": "Сингапур доллары", "tt": "Сингапур доллары",
@@ -5175,7 +5192,7 @@
"de": "syrische Lira", "de": "syrische Lira",
"en": "Syrian pound", "en": "Syrian pound",
"eo": "siria pundo", "eo": "siria pundo",
"es": "Dolar sirio", "es": "libra siria",
"fi": "Syyrian punta", "fi": "Syyrian punta",
"fr": "livre syrienne", "fr": "livre syrienne",
"ga": "punt na Siria", "ga": "punt na Siria",
@@ -5977,7 +5994,7 @@
"ms": "Franc CFA Afrika Tengah", "ms": "Franc CFA Afrika Tengah",
"nl": "Central African CFA franc", "nl": "Central African CFA franc",
"oc": "Franc CFA d'Africa Centrala", "oc": "Franc CFA d'Africa Centrala",
"pl": "środkowoafrykański frank CFA", "pl": "frank CFA",
"pt": "franco", "pt": "franco",
"ro": "Franc CFA BEAC", "ro": "Franc CFA BEAC",
"ru": "франк КФА BEAC", "ru": "франк КФА BEAC",
@@ -5999,6 +6016,7 @@
"fr": "argent d'investissement", "fr": "argent d'investissement",
"ja": "投資対象としての銀", "ja": "投資対象としての銀",
"ms": "Perak sebagai pelaburan", "ms": "Perak sebagai pelaburan",
"pt": "Prata como investimento",
"ru": "серебро как инвестиция", "ru": "серебро как инвестиция",
"sv": "Silver som investering", "sv": "Silver som investering",
"tr": "Yatırım olarak gümüş", "tr": "Yatırım olarak gümüş",
@@ -6017,6 +6035,7 @@
"lv": "zelts kā investīcija", "lv": "zelts kā investīcija",
"ml": "സ്വർണവും സാമ്പത്തിക ശാസ്ത്രവും", "ml": "സ്വർണവും സാമ്പത്തിക ശാസ്ത്രവും",
"ms": "emas sebagai pelaburan", "ms": "emas sebagai pelaburan",
"pt": "Ouro como investimento",
"ru": "золото как инвестиция", "ru": "золото как инвестиция",
"sr": "investiciono zlato", "sr": "investiciono zlato",
"sv": "investeringsguld", "sv": "investeringsguld",
@@ -7905,7 +7924,6 @@
"dolar de las islas caimán": "KYD", "dolar de las islas caimán": "KYD",
"dolar de las islas salomon": "SBD", "dolar de las islas salomon": "SBD",
"dolar de las islas salomón": "SBD", "dolar de las islas salomón": "SBD",
"dolar de namibia": "NAD",
"dolar de nueva zelanda": "NZD", "dolar de nueva zelanda": "NZD",
"dolar de singapor": "SGD", "dolar de singapor": "SGD",
"dolar de singapur": "SGD", "dolar de singapur": "SGD",
@@ -7963,7 +7981,6 @@
"dolar namibia": "NAD", "dolar namibia": "NAD",
"dolar namibian": "NAD", "dolar namibian": "NAD",
"dolar namibijski": "NAD", "dolar namibijski": "NAD",
"dolar namibio": "NAD",
"dolar neocelandes": "NZD", "dolar neocelandes": "NZD",
"dolar neocelandés": "NZD", "dolar neocelandés": "NZD",
"dolar neozeelandez": "NZD", "dolar neozeelandez": "NZD",
@@ -7978,7 +7995,6 @@
"dolar singapura": "SGD", "dolar singapura": "SGD",
"dolar singapurski": "SGD", "dolar singapurski": "SGD",
"dolar singapurtar": "SGD", "dolar singapurtar": "SGD",
"dolar sirio": "SYP",
"dolar sua": "USD", "dolar sua": "USD",
"dolar surinamdar": "SRD", "dolar surinamdar": "SRD",
"dolar suriname": "SRD", "dolar suriname": "SRD",
@@ -8179,6 +8195,7 @@
"dominicaanse peso": "DOP", "dominicaanse peso": "DOP",
"dominican peso": "DOP", "dominican peso": "DOP",
"dominican peso oro": "DOP", "dominican peso oro": "DOP",
"dominički pezo": "DOP",
"dominik pesosu": "DOP", "dominik pesosu": "DOP",
"dominika peso": "DOP", "dominika peso": "DOP",
"dominikaanisen tasavallan peso": "DOP", "dominikaanisen tasavallan peso": "DOP",
@@ -8528,7 +8545,6 @@
"etiópsky birr": "ETB", "etiópsky birr": "ETB",
"eua 17": "XBD", "eua 17": "XBD",
"eua 9": "XBC", "eua 9": "XBC",
"eur": "EUR",
"euras": "EUR", "euras": "EUR",
"eurco": "XBA", "eurco": "XBA",
"euro": "EUR", "euro": "EUR",
@@ -8859,7 +8875,6 @@
"frank kongijski": "CDF", "frank kongijski": "CDF",
"frank rwandyjski": "RWF", "frank rwandyjski": "RWF",
"frank szwajcarski": "CHF", "frank szwajcarski": "CHF",
"frank zachodnioafrykański": "XOF",
"franko suitzar": "CHF", "franko suitzar": "CHF",
"frw": "RWF", "frw": "RWF",
"ft": "HUF", "ft": "HUF",
@@ -9180,6 +9195,7 @@
"indonezijska rupija": "IDR", "indonezijska rupija": "IDR",
"indonéská rupie": "IDR", "indonéská rupie": "IDR",
"indonéz rúpia": "IDR", "indonéz rúpia": "IDR",
"indonézska rupia": "IDR",
"indonēziešu rūpija": "IDR", "indonēziešu rūpija": "IDR",
"indonēzijas rūpija": "IDR", "indonēzijas rūpija": "IDR",
"inglise nael": "GBP", "inglise nael": "GBP",
@@ -9494,6 +9510,8 @@
"kirgizia somo": "KGS", "kirgizia somo": "KGS",
"kirgizische som": "KGS", "kirgizische som": "KGS",
"kirgizistansk som": "KGS", "kirgizistansk som": "KGS",
"kirgizstānas soms": "KGS",
"kirgīzu soms": "KGS",
"kiwi dollar": "NZD", "kiwi dollar": "NZD",
"kínai jüan": "CNY", "kínai jüan": "CNY",
"kíp": "LAK", "kíp": "LAK",
@@ -9810,7 +9828,6 @@
"lek na halbáine": "ALL", "lek na halbáine": "ALL",
"lek novo": "ALL", "lek novo": "ALL",
"lekas": "ALL", "lekas": "ALL",
"lekă albaneză": "ALL",
"lekë": "ALL", "lekë": "ALL",
"leko": "ALL", "leko": "ALL",
"lempira": "HNL", "lempira": "HNL",
@@ -9981,6 +9998,7 @@
"lira libanesa": "LBP", "lira libanesa": "LBP",
"lira libanese": "LBP", "lira libanese": "LBP",
"lira na tuirce": "TRY", "lira na tuirce": "TRY",
"lira siria": "SYP",
"lira siriana": "SYP", "lira siriana": "SYP",
"lira síria": "SYP", "lira síria": "SYP",
"lira thổ nhĩ kỳ": "TRY", "lira thổ nhĩ kỳ": "TRY",
@@ -10291,6 +10309,7 @@
"meksički pezo": "MXN", "meksički pezo": "MXN",
"meksika peso": "MXN", "meksika peso": "MXN",
"meksika pesosu": "MXN", "meksika pesosu": "MXN",
"meksika unuo de investo": "MXV",
"meksikaanse peso": "MXN", "meksikaanse peso": "MXN",
"meksikas peso": "MXN", "meksikas peso": "MXN",
"meksikon peso": "MXN", "meksikon peso": "MXN",
@@ -10746,6 +10765,7 @@
"ouguiya mauritanien": "MRU", "ouguiya mauritanien": "MRU",
"ouguiya mawritania": "MRU", "ouguiya mawritania": "MRU",
"ouguiya na máratáine": "MRU", "ouguiya na máratáine": "MRU",
"ouro como investimento": "XAU",
"ouro do zimbábue": "ZWG", "ouro do zimbábue": "ZWG",
"örmény dram": "AMD", "örmény dram": "AMD",
"östkaribisk dollar": "XCD", "östkaribisk dollar": "XCD",
@@ -11111,6 +11131,7 @@
"põhja korea vonn": "KPW", "põhja korea vonn": "KPW",
"põhja korea won": "KPW", "põhja korea won": "KPW",
"põhja makedoonia denaar": "MKD", "põhja makedoonia denaar": "MKD",
"prata como investimento": "XAG",
"pula": "BWP", "pula": "BWP",
"pula botswana": "BWP", "pula botswana": "BWP",
"pula botswanais": "BWP", "pula botswanais": "BWP",
@@ -11901,6 +11922,7 @@
"somoni taxico": "TJS", "somoni taxico": "TJS",
"somoni tayiko": "TJS", "somoni tayiko": "TJS",
"somonis": "TJS", "somonis": "TJS",
"soms": "KGS",
"sonderziehungsrecht": "XDR", "sonderziehungsrecht": "XDR",
"sos": "SOS", "sos": "SOS",
"sosh": "SOS", "sosh": "SOS",
@@ -12102,7 +12124,6 @@
"szyling tanzański": "TZS", "szyling tanzański": "TZS",
"szyling ugandyjski": "UGX", "szyling ugandyjski": "UGX",
"sırp dinarı": "RSD", "sırp dinarı": "RSD",
"środkowoafrykański frank cfa": "XAF",
"šalamounský dolar": "SBD", "šalamounský dolar": "SBD",
"šalomounský dolar": "SBD", "šalomounský dolar": "SBD",
"šekel chadaš": "ILS", "šekel chadaš": "ILS",
@@ -14017,6 +14038,7 @@
"самоанский доллар": "WST", "самоанский доллар": "WST",
"самоанська тала": "WST", "самоанська тала": "WST",
"сан томе һәм принсипи добрасы": "STN", "сан томе һәм принсипи добрасы": "STN",
"саотомеанска добра": "STN",
"саотомска добра": "STN", "саотомска добра": "STN",
"саудитски риал": "SAR", "саудитски риал": "SAR",
"саудијски риал": "SAR", "саудијски риал": "SAR",
@@ -15350,13 +15372,28 @@
"ஹங்கேரிய போரிண்ட்": "HUF", "ஹங்கேரிய போரிண்ட்": "HUF",
"ஹிருன்யா": "UAH", "ஹிருன்யா": "UAH",
"ஹொங்கொங் டொலர்": "HKD", "ஹொங்கொங் டொலர்": "HKD",
"అమెరికన్ డాలర్": "USD",
"ఇజ్రాయెల్ షెకెల్": "ILS",
"కెనడియన్ డాలర్": "CAD",
"చిలీ పెసో": "CLP",
"జపనీస్ యెన్": "JPY",
"డిజిటల్ రూపాయి": "INR", "డిజిటల్ రూపాయి": "INR",
"నేపాలీ రూపాయి": "NPR",
"పాకిస్థానీ రూపాయి": "PKR",
"ఫిలిప్పీన్ పెసో": "PHP",
"బ్రూనై డాలర్": "BND",
"భారత రూపాయి": "INR", "భారత రూపాయి": "INR",
"భారతదేశ రూపాయి": "INR", "భారతదేశ రూపాయి": "INR",
"భారతీయ రూపాయి": "INR", "భారతీయ రూపాయి": "INR",
"భూటానీస్ గుల్త్రమ్": "BTN",
"యునైటెడ్ స్టేట్స్ డాలర్": "USD", "యునైటెడ్ స్టేట్స్ డాలర్": "USD",
"యూఏఈ దిర్హామ్": "AED",
"యూరో": "EUR", "యూరో": "EUR",
"రూపాయి": "INR", "రూపాయి": "INR",
"రెన్మిన్బి": "CNY",
"శ్రీలంక రూపాయి": "LKR",
"సింగపూర్ డాలర్": "SGD",
"సౌదీ రియాల్": "SAR",
"స్విస్ ఫ్రాంక్": "CHF", "స్విస్ ఫ్రాంక్": "CHF",
"അൾജീരിയൻ ദിനാർ": "DZD", "അൾജീരിയൻ ദിനാർ": "DZD",
"ഇന്തോനേഷ്യൻ റുപിയ": "IDR", "ഇന്തോനേഷ്യൻ റുപിയ": "IDR",
@@ -15607,6 +15644,8 @@
"부탄 뉘땀": "BTN", "부탄 뉘땀": "BTN",
"부탄눌트럼": "BTN", "부탄눌트럼": "BTN",
"북마케도니아 데나르": "MKD", "북마케도니아 데나르": "MKD",
"북조선 원": "KPW",
"북한 원": "KPW",
"브라질 레알": "BRL", "브라질 레알": "BRL",
"브라질 헤알": "BRL", "브라질 헤알": "BRL",
"브루나이 달러": "BND", "브루나이 달러": "BND",
@@ -16199,7 +16238,6 @@
"香港・ドル": "HKD", "香港・ドル": "HKD",
"香港元": "HKD", "香港元": "HKD",
"﷼": "IRR", "﷼": "IRR",
"﷼'": "YER", "﷼'": "YER"
"💶": "EUR"
} }
} }
File diff suppressed because it is too large Load Diff
+178 -7
View File
@@ -4079,6 +4079,7 @@
"bg-BG": "BG:bg", "bg-BG": "BG:bg",
"bn-BD": "BD:bn", "bn-BD": "BD:bn",
"bn-IN": "IN:bn", "bn-IN": "IN:bn",
"ca-ES": "ES:ca",
"cs-CZ": "CZ:cs", "cs-CZ": "CZ:cs",
"de-AT": "AT:de", "de-AT": "AT:de",
"de-CH": "CH:de", "de-CH": "CH:de",
@@ -4110,16 +4111,15 @@
"es-CO": "CO:es-419", "es-CO": "CO:es-419",
"es-CU": "CU:es-419", "es-CU": "CU:es-419",
"es-ES": "ES:es", "es-ES": "ES:es",
"es-MX": "MX:es-419", "et-EE": "EE:et",
"es-PE": "PE:es-419", "fi-FI": "FI:fi",
"es-US": "US:es-419",
"es-VE": "VE:es-419",
"fr-BE": "BE:fr", "fr-BE": "BE:fr",
"fr-CA": "CA:fr", "fr-CA": "CA:fr",
"fr-CH": "CH:fr", "fr-CH": "CH:fr",
"fr-FR": "FR:fr", "fr-FR": "FR:fr",
"fr-MA": "MA:fr", "fr-MA": "MA:fr",
"fr-SN": "SN:fr", "fr-SN": "SN:fr",
"gu-IN": "IN:gu",
"he-IL": "IL:he", "he-IL": "IL:he",
"hi-IN": "IN:hi", "hi-IN": "IN:hi",
"hu-HU": "HU:hu", "hu-HU": "HU:hu",
@@ -4131,12 +4131,13 @@
"lv-LV": "LV:lv", "lv-LV": "LV:lv",
"ml-IN": "IN:ml", "ml-IN": "IN:ml",
"mr-IN": "IN:mr", "mr-IN": "IN:mr",
"ms-MY": "MY:ms",
"nb-NO": "NO:no", "nb-NO": "NO:no",
"nl-BE": "BE:nl", "nl-BE": "BE:nl",
"nl-NL": "NL:nl", "nl-NL": "NL:nl",
"pa-IN": "IN:pa",
"pl-PL": "PL:pl", "pl-PL": "PL:pl",
"pt-BR": "BR:pt-419", "pt-BR": "BR:pt-419",
"pt-PT": "PT:pt-150",
"ro-RO": "RO:ro", "ro-RO": "RO:ro",
"ru-RU": "RU:ru", "ru-RU": "RU:ru",
"ru-UA": "UA:ru", "ru-UA": "UA:ru",
@@ -4151,8 +4152,7 @@
"uk-UA": "UA:uk", "uk-UA": "UA:uk",
"vi-VN": "VN:vi", "vi-VN": "VN:vi",
"zh-CN": "CN:zh-Hans", "zh-CN": "CN:zh-Hans",
"zh-HK": "HK:zh-Hant", "zh-HK": "HK:zh-Hant"
"zh-TW": "TW:zh-Hant"
}, },
"supported_domains": {} "supported_domains": {}
}, },
@@ -8513,6 +8513,177 @@
"zh-classical": "zh-classical" "zh-classical": "zh-classical"
} }
}, },
"yep": {
"all_locale": null,
"custom": {},
"data_type": "traits_v1",
"languages": {
"aa": "aa",
"ab": "ab",
"af": "af",
"ak": "ak",
"am": "am",
"an": "an",
"ar": "ar",
"as": "as",
"az": "az",
"ba": "ba",
"be": "be",
"bg": "bg",
"bho": "bh",
"bm": "bm",
"bn": "bn",
"bo": "bo",
"br": "br",
"bs": "bs",
"ca": "ca",
"ce": "ce",
"co": "co",
"cs": "cs",
"cu": "cu",
"cv": "cv",
"cy": "cy",
"da": "da",
"de": "de",
"dv": "dv",
"dz": "dz",
"ee": "ee",
"el": "el",
"en": "en",
"eo": "eo",
"es": "es",
"et": "et",
"eu": "eu",
"fa": "fa",
"ff": "ff",
"fi": "fi",
"fil": "tl",
"fo": "fo",
"fr": "fr",
"fy": "fy",
"ga": "ga",
"gd": "gd",
"gl": "gl",
"gn": "gn",
"gu": "gu",
"gv": "gv",
"ha": "ha",
"he": "he",
"hi": "hi",
"hr": "hr",
"ht": "ht",
"hu": "hu",
"hy": "hy",
"ia": "ia",
"id": "id",
"ie": "ie",
"ig": "ig",
"ii": "ii",
"io": "io",
"is": "is",
"it": "it",
"iu": "iu",
"ja": "ja",
"jv": "jv",
"ka": "ka",
"ki": "ki",
"kk": "kk",
"kl": "kl",
"km": "km",
"kn": "kn",
"ko": "ko",
"ks": "ks",
"ku": "ku",
"kw": "kw",
"ky": "ky",
"la": "la",
"lb": "lb",
"lg": "lg",
"ln": "ln",
"lo": "lo",
"lt": "lt",
"lu": "lu",
"lv": "lv",
"mg": "mg",
"mi": "mi",
"mk": "mk",
"ml": "ml",
"mn": "mn",
"mr": "mr",
"ms": "ms",
"mt": "mt",
"my": "my",
"nb": "nb",
"nd": "nd",
"ne": "ne",
"nl": "nl",
"nn": "nn",
"no": "no",
"nr": "nr",
"nv": "nv",
"ny": "ny",
"oc": "oc",
"om": "om",
"or": "or",
"os": "os",
"pa": "pa",
"pl": "pl",
"ps": "ps",
"pt": "pt",
"qu": "qu",
"rm": "rm",
"rn": "rn",
"ro": "ro",
"ru": "ru",
"rw": "rw",
"sa": "sa",
"sc": "sc",
"sd": "sd",
"se": "se",
"sg": "sg",
"si": "si",
"sk": "sk",
"sl": "sl",
"sn": "sn",
"so": "so",
"sq": "sq",
"sr": "sr",
"ss": "ss",
"st": "st",
"su": "su",
"sv": "sv",
"sw": "sw",
"ta": "ta",
"te": "te",
"tg": "tg",
"th": "th",
"ti": "ti",
"tk": "tk",
"tn": "tn",
"to": "to",
"tr": "tr",
"ts": "ts",
"tt": "tt",
"ug": "ug",
"uk": "uk",
"ur": "ur",
"uz": "uz",
"ve": "ve",
"vi": "vi",
"vo": "vo",
"wa": "wa",
"wo": "wo",
"xh": "xh",
"yi": "yi",
"yo": "yo",
"za": "za",
"zh": "zh",
"zh_Hans": "zh-cn",
"zh_Hant": "zh-tw",
"zu": "zu"
},
"regions": {}
},
"z-library": { "z-library": {
"all_locale": "", "all_locale": "",
"custom": { "custom": {
+4502 -2286
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -5,7 +5,7 @@
], ],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}", "ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
"versions": [ "versions": [
"150.0", "151.0",
"149.0" "150.0"
] ]
} }
+258 -143
View File
@@ -69,11 +69,6 @@
"symbol": "pk (US)", "symbol": "pk (US)",
"to_si_factor": 0.008809768 "to_si_factor": 0.008809768
}, },
"Q101427917": {
"si_name": "Q25517",
"symbol": "pk (UK)",
"to_si_factor": 0.00909218
},
"Q101428098": { "Q101428098": {
"si_name": "Q44395", "si_name": "Q44395",
"symbol": "dbar", "symbol": "dbar",
@@ -84,6 +79,11 @@
"symbol": "μbar", "symbol": "μbar",
"to_si_factor": 0.1 "to_si_factor": 0.1
}, },
"Q101435213": {
"si_name": "Q101435195",
"symbol": "Jy s",
"to_si_factor": 1e-26
},
"Q101435332": { "Q101435332": {
"si_name": "Q44395", "si_name": "Q44395",
"symbol": "cm Hg", "symbol": "cm Hg",
@@ -124,11 +124,6 @@
"symbol": "pm²", "symbol": "pm²",
"to_si_factor": 1e-24 "to_si_factor": 1e-24
}, },
"Q101463679": {
"si_name": "Q25343",
"symbol": "hm²",
"to_si_factor": 10000.0
},
"Q101464050": { "Q101464050": {
"si_name": "Q25343", "si_name": "Q25343",
"symbol": "Mm²", "symbol": "Mm²",
@@ -199,6 +194,11 @@
"symbol": "d⁻¹", "symbol": "d⁻¹",
"to_si_factor": 1.15741e-05 "to_si_factor": 1.15741e-05
}, },
"Q102129764": {
"si_name": "Q6137407",
"symbol": "a⁻¹",
"to_si_factor": 3.17098e-08
},
"Q102130673": { "Q102130673": {
"si_name": "Q182429", "si_name": "Q182429",
"symbol": "ym/s", "symbol": "ym/s",
@@ -425,9 +425,9 @@
"to_si_factor": 10.0 "to_si_factor": 10.0
}, },
"Q1042866": { "Q1042866": {
"si_name": null, "si_name": "Q199",
"symbol": "Zib", "symbol": "Zib",
"to_si_factor": null "to_si_factor": 1.1805916207174113e+21
}, },
"Q104317437": { "Q104317437": {
"si_name": "Q25381181", "si_name": "Q25381181",
@@ -995,9 +995,9 @@
"to_si_factor": 1000000000000.0 "to_si_factor": 1000000000000.0
}, },
"Q106247880": { "Q106247880": {
"si_name": null, "si_name": "Q6137407",
"symbol": "digit/s", "symbol": "digit/s",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q106247940": { "Q106247940": {
"si_name": null, "si_name": null,
@@ -1504,6 +1504,11 @@
"symbol": "m²/(s² K)", "symbol": "m²/(s² K)",
"to_si_factor": 1.0 "to_si_factor": 1.0
}, },
"Q106707206": {
"si_name": null,
"symbol": "HU",
"to_si_factor": null
},
"Q106707404": { "Q106707404": {
"si_name": "Q106707404", "si_name": "Q106707404",
"symbol": "kg m²/(s² K)", "symbol": "kg m²/(s² K)",
@@ -2314,79 +2319,124 @@
"symbol": "cm²/(sr erg)", "symbol": "cm²/(sr erg)",
"to_si_factor": 1000.0 "to_si_factor": 1000.0
}, },
"Q107710161": {
"si_name": "Q199",
"symbol": "J/bit",
"to_si_factor": 1.0
},
"Q107821494": { "Q107821494": {
"si_name": null, "si_name": "Q21401573",
"symbol": "bit/m³", "symbol": "bit/m³",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q107822428": { "Q107822428": {
"si_name": null, "si_name": "Q11547251",
"symbol": "bit/m", "symbol": "bit/m",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q107824325": { "Q107824325": {
"si_name": null, "si_name": "Q11547252",
"symbol": "bit/m²", "symbol": "bit/m²",
"to_si_factor": null "to_si_factor": 1.0
},
"Q107825143": {
"si_name": "Q11547251",
"symbol": "Eibit/m",
"to_si_factor": 1.152921504606847e+18
},
"Q107825584": {
"si_name": "Q11547252",
"symbol": "Eibit/m²",
"to_si_factor": 1.152921504606847e+18
},
"Q107825716": {
"si_name": "Q21401573",
"symbol": "Eibit/m³",
"to_si_factor": 1.152921504606847e+18
},
"Q107862736": {
"si_name": "Q11547251",
"symbol": "Gibit/m",
"to_si_factor": 1073741824.0
},
"Q107862746": {
"si_name": "Q11547252",
"symbol": "Gibit/m²",
"to_si_factor": 1073741824.0
},
"Q107862762": {
"si_name": "Q21401573",
"symbol": "Gibit/m³",
"to_si_factor": 1073741824.0
}, },
"Q107862770": { "Q107862770": {
"si_name": null, "si_name": "Q11547251",
"symbol": "Kibit/m", "symbol": "Kibit/m",
"to_si_factor": null "to_si_factor": 1024.0
}, },
"Q107862783": { "Q107862783": {
"si_name": null, "si_name": "Q11547252",
"symbol": "Kibit/m", "symbol": "Kibit/m",
"to_si_factor": null "to_si_factor": 1024.0
}, },
"Q107862850": { "Q107862850": {
"si_name": null, "si_name": "Q21401573",
"symbol": "Kibit/m³", "symbol": "Kibit/m³",
"to_si_factor": null "to_si_factor": 1024.0
}, },
"Q107862870": { "Q107862870": {
"si_name": null, "si_name": "Q11547251",
"symbol": "Mibit/m", "symbol": "Mibit/m",
"to_si_factor": null "to_si_factor": 1048576.0
}, },
"Q107862884": { "Q107862884": {
"si_name": null, "si_name": "Q11547252",
"symbol": "Mibit/m²", "symbol": "Mibit/m²",
"to_si_factor": null "to_si_factor": 1048576.0
}, },
"Q107862898": { "Q107862898": {
"si_name": null, "si_name": "Q21401573",
"symbol": "Mibit/m³", "symbol": "Mibit/m³",
"to_si_factor": null "to_si_factor": 1048576.0
}, },
"Q107970215": { "Q107970215": {
"si_name": null, "si_name": "Q11547251",
"symbol": "Pibit/m", "symbol": "Pibit/m",
"to_si_factor": null "to_si_factor": 1125899906842624.0
}, },
"Q107970224": { "Q107970224": {
"si_name": null, "si_name": "Q11547252",
"symbol": "Pibit/m²", "symbol": "Pibit/m²",
"to_si_factor": null "to_si_factor": 1125899906842624.0
}, },
"Q107970230": { "Q107970230": {
"si_name": null, "si_name": "Q21401573",
"symbol": "Pibit/m³", "symbol": "Pibit/m³",
"to_si_factor": null "to_si_factor": 1125899906842624.0
}, },
"Q107970235": { "Q107970235": {
"si_name": null, "si_name": "Q11547251",
"symbol": "Tibit/m", "symbol": "Tibit/m",
"to_si_factor": null "to_si_factor": 1099511627776.0
}, },
"Q107970256": { "Q107970256": {
"si_name": null, "si_name": "Q21401573",
"symbol": "Tibit/m³", "symbol": "Tibit/m³",
"to_si_factor": null "to_si_factor": 1099511627776.0
}, },
"Q107970266": { "Q107970266": {
"si_name": null, "si_name": "Q11547252",
"symbol": "Tibit/m²", "symbol": "Tibit/m²",
"to_si_factor": 1099511627776.0
},
"Q108112819": {
"si_name": null,
"symbol": "€/kWh",
"to_si_factor": null
},
"Q108112891": {
"si_name": null,
"symbol": "€/(MW h)",
"to_si_factor": null "to_si_factor": null
}, },
"Q108270163": { "Q108270163": {
@@ -2395,9 +2445,9 @@
"to_si_factor": 3.169e-05 "to_si_factor": 3.169e-05
}, },
"Q1084321": { "Q1084321": {
"si_name": null, "si_name": "Q6137407",
"symbol": "Tb/s", "symbol": "Tb/s",
"to_si_factor": null "to_si_factor": 1000000000000.0
}, },
"Q108533173": { "Q108533173": {
"si_name": "Q108533173", "si_name": "Q108533173",
@@ -2434,6 +2484,16 @@
"symbol": "GeV/c²", "symbol": "GeV/c²",
"to_si_factor": 1.7826619216278976e-27 "to_si_factor": 1.7826619216278976e-27
}, },
"Q108913970": {
"si_name": null,
"symbol": "person/km²",
"to_si_factor": null
},
"Q108914485": {
"si_name": null,
"symbol": "person/m²",
"to_si_factor": null
},
"Q108920356": { "Q108920356": {
"si_name": "Q25406", "si_name": "Q25406",
"symbol": "esu", "symbol": "esu",
@@ -2464,6 +2524,11 @@
"symbol": "e.u.", "symbol": "e.u.",
"to_si_factor": 4.184 "to_si_factor": 4.184
}, },
"Q109337616": {
"si_name": "Q139710667",
"symbol": "1/(100 eV)",
"to_si_factor": 6.2415e+16
},
"Q109448508": { "Q109448508": {
"si_name": null, "si_name": null,
"symbol": "man-Sv", "symbol": "man-Sv",
@@ -2499,6 +2564,11 @@
"symbol": "nm²", "symbol": "nm²",
"to_si_factor": 1e-18 "to_si_factor": 1e-18
}, },
"Q11123": {
"si_name": "Q25517",
"symbol": "pt (UK)",
"to_si_factor": 0.00056826125
},
"Q111494193": { "Q111494193": {
"si_name": "Q111494193", "si_name": "Q111494193",
"symbol": "J/(Hz mol)", "symbol": "J/(Hz mol)",
@@ -2509,6 +2579,11 @@
"symbol": "%", "symbol": "%",
"to_si_factor": 0.01 "to_si_factor": 0.01
}, },
"Q112659472": {
"si_name": null,
"symbol": "PVU",
"to_si_factor": null
},
"Q1131660": { "Q1131660": {
"si_name": "Q11570", "si_name": "Q11570",
"symbol": "st", "symbol": "st",
@@ -2540,14 +2615,14 @@
"to_si_factor": 31688087.81402895 "to_si_factor": 31688087.81402895
}, },
"Q1140444": { "Q1140444": {
"si_name": null, "si_name": "Q199",
"symbol": "Zb", "symbol": "Zb",
"to_si_factor": null "to_si_factor": 1e+21
}, },
"Q1140577": { "Q1140577": {
"si_name": null, "si_name": "Q199",
"symbol": "Yb", "symbol": "Yb",
"to_si_factor": null "to_si_factor": 0.0
}, },
"Q114559346": { "Q114559346": {
"si_name": null, "si_name": null,
@@ -2560,14 +2635,14 @@
"to_si_factor": null "to_si_factor": null
}, },
"Q1152074": { "Q1152074": {
"si_name": null, "si_name": "Q199",
"symbol": "Pb", "symbol": "Pb",
"to_si_factor": null "to_si_factor": 1000000000000000.0
}, },
"Q1152323": { "Q1152323": {
"si_name": null, "si_name": "Q199",
"symbol": "Tb", "symbol": "Tb",
"to_si_factor": null "to_si_factor": 1000000000000.0
}, },
"Q115277430": { "Q115277430": {
"si_name": null, "si_name": null,
@@ -2810,14 +2885,14 @@
"to_si_factor": 4.4482216152605 "to_si_factor": 4.4482216152605
}, },
"Q1194580": { "Q1194580": {
"si_name": null, "si_name": "Q199",
"symbol": "Mib", "symbol": "Mib",
"to_si_factor": null "to_si_factor": 1048576.0
}, },
"Q1195111": { "Q1195111": {
"si_name": null, "si_name": "Q199",
"symbol": "Eb", "symbol": "Eb",
"to_si_factor": null "to_si_factor": 1e+18
}, },
"Q1196837": { "Q1196837": {
"si_name": "Q1063756", "si_name": "Q1063756",
@@ -2865,15 +2940,20 @@
"to_si_factor": 0.03110348 "to_si_factor": 0.03110348
}, },
"Q1204894": { "Q1204894": {
"si_name": null, "si_name": "Q199",
"symbol": "Gib", "symbol": "Gib",
"to_si_factor": null "to_si_factor": 1073741824.0
}, },
"Q12129": { "Q12129": {
"si_name": "Q11573", "si_name": "Q11573",
"symbol": "pc", "symbol": "pc",
"to_si_factor": 3.085677581491367e+16 "to_si_factor": 3.085677581491367e+16
}, },
"Q12145303": {
"si_name": "Q11573",
"symbol": "rd",
"to_si_factor": 5.02921
},
"Q121965382": { "Q121965382": {
"si_name": "Q121965382", "si_name": "Q121965382",
"symbol": "mol/mol", "symbol": "mol/mol",
@@ -3114,6 +3194,11 @@
"symbol": "QF", "symbol": "QF",
"to_si_factor": 1e+30 "to_si_factor": 1e+30
}, },
"Q12558489": {
"si_name": "Q25517",
"symbol": "pk (UK)",
"to_si_factor": 0.00909218
},
"Q125962250": { "Q125962250": {
"si_name": null, "si_name": null,
"symbol": "Ry", "symbol": "Ry",
@@ -3240,14 +3325,14 @@
"to_si_factor": 1e-30 "to_si_factor": 1e-30
}, },
"Q131395783": { "Q131395783": {
"si_name": null, "si_name": "Q199",
"symbol": "Rib", "symbol": "Rib",
"to_si_factor": null "to_si_factor": 1.2379400392853803e+27
}, },
"Q131395793": { "Q131395793": {
"si_name": null, "si_name": "Q199",
"symbol": "Qib", "symbol": "Qib",
"to_si_factor": null "to_si_factor": 1.2676506002282294e+30
}, },
"Q13147228": { "Q13147228": {
"si_name": "Q844211", "si_name": "Q844211",
@@ -3309,6 +3394,11 @@
"symbol": "1/K", "symbol": "1/K",
"to_si_factor": null "to_si_factor": null
}, },
"Q133796439": {
"si_name": null,
"symbol": "T/mm²",
"to_si_factor": null
},
"Q13400897": { "Q13400897": {
"si_name": "Q1051665", "si_name": "Q1051665",
"symbol": "g", "symbol": "g",
@@ -3317,18 +3407,23 @@
"Q13479685": { "Q13479685": {
"si_name": "Q44395", "si_name": "Q44395",
"symbol": "mm H2O", "symbol": "mm H2O",
"to_si_factor": 9.80638 "to_si_factor": 9.80665
}, },
"Q1351253": { "Q1351253": {
"si_name": null, "si_name": "Q199",
"symbol": "Eib", "symbol": "Eib",
"to_si_factor": null "to_si_factor": 1.152921504606847e+18
}, },
"Q1351334": { "Q1351334": {
"si_name": null, "si_name": null,
"symbol": "Pib", "symbol": "Pib",
"to_si_factor": null "to_si_factor": null
}, },
"Q135373020": {
"si_name": null,
"symbol": "GTexel",
"to_si_factor": null
},
"Q135415097": { "Q135415097": {
"si_name": "Q25236", "si_name": "Q25236",
"symbol": "shp", "symbol": "shp",
@@ -3380,9 +3475,9 @@
"to_si_factor": 1e-06 "to_si_factor": 1e-06
}, },
"Q136039973": { "Q136039973": {
"si_name": null, "si_name": "Q6137407",
"symbol": "FPS", "symbol": "FPS",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q1361854": { "Q1361854": {
"si_name": "Q11570", "si_name": "Q11570",
@@ -3450,9 +3545,9 @@
"to_si_factor": 1000000000000.0 "to_si_factor": 1000000000000.0
}, },
"Q139054848": { "Q139054848": {
"si_name": null, "si_name": "Q117899185",
"symbol": "A·h/m²", "symbol": "A·h/m²",
"to_si_factor": null "to_si_factor": 3600.0
}, },
"Q139086088": { "Q139086088": {
"si_name": "Q69425409", "si_name": "Q69425409",
@@ -3642,7 +3737,7 @@
"Q1654435": { "Q1654435": {
"si_name": "Q25250", "si_name": "Q25250",
"symbol": "IRE", "symbol": "IRE",
"to_si_factor": 0.007 "to_si_factor": 0.007143
}, },
"Q16673974": { "Q16673974": {
"si_name": null, "si_name": null,
@@ -3820,9 +3915,9 @@
"to_si_factor": 0.01 "to_si_factor": 0.01
}, },
"Q18434272": { "Q18434272": {
"si_name": null, "si_name": "Q199",
"symbol": "°Balling", "symbol": "°Balling",
"to_si_factor": null "to_si_factor": 0.01
}, },
"Q185078": { "Q185078": {
"si_name": "Q25343", "si_name": "Q25343",
@@ -3850,9 +3945,9 @@
"to_si_factor": 1e-21 "to_si_factor": 1e-21
}, },
"Q188768": { "Q188768": {
"si_name": null, "si_name": "Q6137407",
"symbol": "FLOPS", "symbol": "FLOPS",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q190095": { "Q190095": {
"si_name": "Q190095", "si_name": "Q190095",
@@ -3989,6 +4084,11 @@
"symbol": "ppb", "symbol": "ppb",
"to_si_factor": 1e-09 "to_si_factor": 1e-09
}, },
"Q206037": {
"si_name": "Q6137407",
"symbol": "r/min",
"to_si_factor": 0.0166667
},
"Q2064166": { "Q2064166": {
"si_name": "Q179836", "si_name": "Q179836",
"symbol": "fc", "symbol": "fc",
@@ -4320,9 +4420,9 @@
"to_si_factor": 10.0 "to_si_factor": 10.0
}, },
"Q2243141": { "Q2243141": {
"si_name": null, "si_name": "Q6137407",
"symbol": "Gb/s", "symbol": "Gb/s",
"to_si_factor": null "to_si_factor": 1000000000.0
}, },
"Q2254856": { "Q2254856": {
"si_name": "Q25343", "si_name": "Q25343",
@@ -4335,9 +4435,9 @@
"to_si_factor": 0.00508 "to_si_factor": 0.00508
}, },
"Q2269250": { "Q2269250": {
"si_name": null, "si_name": "Q6137407",
"symbol": "kb/s", "symbol": "kb/s",
"to_si_factor": null "to_si_factor": 1000.0
}, },
"Q2278977": { "Q2278977": {
"si_name": null, "si_name": null,
@@ -4535,9 +4635,9 @@
"to_si_factor": 898755178700.0 "to_si_factor": 898755178700.0
}, },
"Q25325238": { "Q25325238": {
"si_name": null, "si_name": "Q106919394",
"symbol": "bhp/cm³", "symbol": "bhp/cm³",
"to_si_factor": null "to_si_factor": 745700000.0
}, },
"Q253276": { "Q253276": {
"si_name": "Q11573", "si_name": "Q11573",
@@ -4545,9 +4645,9 @@
"to_si_factor": 1609.344 "to_si_factor": 1609.344
}, },
"Q2533495": { "Q2533495": {
"si_name": null, "si_name": "Q199",
"symbol": "°P", "symbol": "°P",
"to_si_factor": null "to_si_factor": 0.01
}, },
"Q25343": { "Q25343": {
"si_name": "Q25343", "si_name": "Q25343",
@@ -4885,9 +4985,9 @@
"to_si_factor": 9.80665 "to_si_factor": 9.80665
}, },
"Q28657331": { "Q28657331": {
"si_name": null, "si_name": "Q106688958",
"symbol": "erg/(s cm²)", "symbol": "erg/(s cm²)",
"to_si_factor": null "to_si_factor": 0.001
}, },
"Q28683485": { "Q28683485": {
"si_name": "Q28683485", "si_name": "Q28683485",
@@ -4920,9 +5020,9 @@
"to_si_factor": 0.001 "to_si_factor": 0.001
}, },
"Q29463526": { "Q29463526": {
"si_name": null, "si_name": "Q199",
"symbol": "hr/yr", "symbol": "hr/yr",
"to_si_factor": null "to_si_factor": 1.14155e-06
}, },
"Q296936": { "Q296936": {
"si_name": "Q25269", "si_name": "Q25269",
@@ -5155,9 +5255,9 @@
"to_si_factor": 1e-15 "to_si_factor": 1e-15
}, },
"Q3194304": { "Q3194304": {
"si_name": null, "si_name": "Q199",
"symbol": "kb", "symbol": "kb",
"to_si_factor": null "to_si_factor": 1000.0
}, },
"Q3196665": { "Q3196665": {
"si_name": "Q215571", "si_name": "Q215571",
@@ -5295,9 +5395,9 @@
"to_si_factor": 3517.0 "to_si_factor": 3517.0
}, },
"Q3332814": { "Q3332814": {
"si_name": null, "si_name": "Q199",
"symbol": "Mb", "symbol": "Mb",
"to_si_factor": null "to_si_factor": 1000000.0
}, },
"Q33680": { "Q33680": {
"si_name": "Q33680", "si_name": "Q33680",
@@ -5390,9 +5490,9 @@
"to_si_factor": 3.085677581e+22 "to_si_factor": 3.085677581e+22
}, },
"Q3815076": { "Q3815076": {
"si_name": null, "si_name": "Q199",
"symbol": "Kib", "symbol": "Kib",
"to_si_factor": null "to_si_factor": 1024.0
}, },
"Q3858002": { "Q3858002": {
"si_name": "Q25406", "si_name": "Q25406",
@@ -5410,9 +5510,9 @@
"to_si_factor": 0.3048 "to_si_factor": 0.3048
}, },
"Q389062": { "Q389062": {
"si_name": null, "si_name": "Q199",
"symbol": "Tib", "symbol": "Tib",
"to_si_factor": null "to_si_factor": 1099511627776.0
}, },
"Q3902688": { "Q3902688": {
"si_name": "Q25517", "si_name": "Q25517",
@@ -5534,6 +5634,11 @@
"symbol": "D", "symbol": "D",
"to_si_factor": 3.335640951981521e-30 "to_si_factor": 3.335640951981521e-30
}, },
"Q4063874": {
"si_name": "Q21401573",
"symbol": "amg",
"to_si_factor": 2.6868e+25
},
"Q4068266": { "Q4068266": {
"si_name": "Q11570", "si_name": "Q11570",
"symbol": "Ʒ", "symbol": "Ʒ",
@@ -5640,9 +5745,9 @@
"to_si_factor": null "to_si_factor": null
}, },
"Q474533": { "Q474533": {
"si_name": null, "si_name": "Q25272",
"symbol": "At", "symbol": "At",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q48013": { "Q48013": {
"si_name": "Q11570", "si_name": "Q11570",
@@ -5705,14 +5810,14 @@
"to_si_factor": 0.01 "to_si_factor": 0.01
}, },
"Q50094": { "Q50094": {
"si_name": null, "si_name": "Q199",
"symbol": "Np", "symbol": "Np",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q50098": { "Q50098": {
"si_name": null, "si_name": "Q199",
"symbol": "B", "symbol": "B",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q50190518": { "Q50190518": {
"si_name": "Q25377184", "si_name": "Q25377184",
@@ -5755,9 +5860,9 @@
"to_si_factor": 1000.0 "to_si_factor": 1000.0
}, },
"Q524410": { "Q524410": {
"si_name": null, "si_name": "Q11574",
"symbol": "Ga", "symbol": "Ga",
"to_si_factor": null "to_si_factor": 3.15576e+16
}, },
"Q531": { "Q531": {
"si_name": "Q11573", "si_name": "Q11573",
@@ -5765,9 +5870,9 @@
"to_si_factor": 9460730472580800.0 "to_si_factor": 9460730472580800.0
}, },
"Q5329": { "Q5329": {
"si_name": null, "si_name": "Q50098",
"symbol": "dB", "symbol": "dB",
"to_si_factor": null "to_si_factor": 0.1
}, },
"Q53393488": { "Q53393488": {
"si_name": "Q39369", "si_name": "Q39369",
@@ -6410,9 +6515,9 @@
"to_si_factor": 3.4262590996353905 "to_si_factor": 3.4262590996353905
}, },
"Q549389": { "Q549389": {
"si_name": null, "si_name": "Q6137407",
"symbol": "b/s", "symbol": "b/s",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q550341": { "Q550341": {
"si_name": "Q550341", "si_name": "Q550341",
@@ -6440,9 +6545,9 @@
"to_si_factor": null "to_si_factor": null
}, },
"Q5558595": { "Q5558595": {
"si_name": null, "si_name": "Q199",
"symbol": "GFLOPS", "symbol": "GFLOPS",
"to_si_factor": null "to_si_factor": 1000000000.0
}, },
"Q55663153": { "Q55663153": {
"si_name": "Q55663153", "si_name": "Q55663153",
@@ -6519,20 +6624,25 @@
"symbol": "GtCO2eq", "symbol": "GtCO2eq",
"to_si_factor": null "to_si_factor": null
}, },
"Q57084839": {
"si_name": null,
"symbol": "gCDE/km",
"to_si_factor": null
},
"Q57084901": { "Q57084901": {
"si_name": null, "si_name": null,
"symbol": "KgCO2eq", "symbol": "KgCO2eq",
"to_si_factor": null "to_si_factor": null
}, },
"Q57084921": { "Q57084921": {
"si_name": null, "si_name": "Q11570",
"symbol": "gCO2eq", "symbol": "gCO2eq",
"to_si_factor": null "to_si_factor": 0.001
}, },
"Q5711255": { "Q5711255": {
"si_name": null, "si_name": "Q25517",
"symbol": "aL", "symbol": "aL",
"to_si_factor": null "to_si_factor": 1e-21
}, },
"Q5711261": { "Q5711261": {
"si_name": "Q25517", "si_name": "Q25517",
@@ -6674,11 +6784,6 @@
"symbol": "in", "symbol": "in",
"to_si_factor": 0.0254 "to_si_factor": 0.0254
}, },
"Q61793198": {
"si_name": "Q11573",
"symbol": "rd",
"to_si_factor": 5.02921
},
"Q61794766": { "Q61794766": {
"si_name": "Q11573", "si_name": "Q11573",
"symbol": "ch (US survey)", "symbol": "ch (US survey)",
@@ -6710,9 +6815,9 @@
"to_si_factor": null "to_si_factor": null
}, },
"Q61995006": { "Q61995006": {
"si_name": null, "si_name": "Q106682512",
"symbol": "KWth", "symbol": "KWth",
"to_si_factor": null "to_si_factor": 1000.0
}, },
"Q61996348": { "Q61996348": {
"si_name": "Q794261", "si_name": "Q794261",
@@ -6755,14 +6860,14 @@
"to_si_factor": 1.1574074e-05 "to_si_factor": 1.1574074e-05
}, },
"Q64740041": { "Q64740041": {
"si_name": null, "si_name": "Q106688958",
"symbol": "kWh/(m² yr)", "symbol": "kWh/(m² yr)",
"to_si_factor": null "to_si_factor": 0.000114
}, },
"Q64740314": { "Q64740314": {
"si_name": null, "si_name": "Q106688958",
"symbol": "kWh/(m² day)", "symbol": "kWh/(m² day)",
"to_si_factor": null "to_si_factor": 41.67
}, },
"Q64748817": { "Q64748817": {
"si_name": "Q80374519", "si_name": "Q80374519",
@@ -6825,9 +6930,9 @@
"to_si_factor": 1016.0469088 "to_si_factor": 1016.0469088
}, },
"Q66778234": { "Q66778234": {
"si_name": null, "si_name": "Q199",
"symbol": "TFLOPS", "symbol": "TFLOPS",
"to_si_factor": null "to_si_factor": 1000000000000.0
}, },
"Q66778809": { "Q66778809": {
"si_name": null, "si_name": null,
@@ -6839,6 +6944,11 @@
"symbol": "PFLOPS", "symbol": "PFLOPS",
"to_si_factor": null "to_si_factor": null
}, },
"Q66778951": {
"si_name": null,
"symbol": "ZFLOPS",
"to_si_factor": null
},
"Q67060736": { "Q67060736": {
"si_name": "Q67060736", "si_name": "Q67060736",
"symbol": "W/kg", "symbol": "W/kg",
@@ -7225,9 +7335,9 @@
"to_si_factor": 1.0 "to_si_factor": 1.0
}, },
"Q7350781": { "Q7350781": {
"si_name": null, "si_name": "Q6137407",
"symbol": "Mb/s", "symbol": "Mb/s",
"to_si_factor": null "to_si_factor": 1000000.0
}, },
"Q743895": { "Q743895": {
"si_name": "Q39369", "si_name": "Q39369",
@@ -7545,9 +7655,9 @@
"to_si_factor": 1.0 "to_si_factor": 1.0
}, },
"Q836941": { "Q836941": {
"si_name": null, "si_name": "Q199",
"symbol": "°Bx", "symbol": "°Bx",
"to_si_factor": null "to_si_factor": 0.01
}, },
"Q83853845": { "Q83853845": {
"si_name": "Q83853845", "si_name": "Q83853845",
@@ -7599,6 +7709,11 @@
"symbol": "hm", "symbol": "hm",
"to_si_factor": 100.0 "to_si_factor": 100.0
}, },
"Q84451486": {
"si_name": "Q84451486",
"symbol": "K m/W",
"to_si_factor": 1.0
},
"Q844976": { "Q844976": {
"si_name": "Q2844478", "si_name": "Q2844478",
"symbol": "Oe", "symbol": "Oe",
@@ -7630,9 +7745,9 @@
"to_si_factor": 1000000000.0 "to_si_factor": 1000000000.0
}, },
"Q855161": { "Q855161": {
"si_name": null, "si_name": "Q199",
"symbol": "Yib", "symbol": "Yib",
"to_si_factor": null "to_si_factor": 1.2089258196146292e+24
}, },
"Q856240": { "Q856240": {
"si_name": "Q794261", "si_name": "Q794261",
@@ -7659,6 +7774,11 @@
"symbol": "abA", "symbol": "abA",
"to_si_factor": 10.0 "to_si_factor": 10.0
}, },
"Q86897783": {
"si_name": "Q86897783",
"symbol": "Pa² s",
"to_si_factor": 1.0
},
"Q87047886": { "Q87047886": {
"si_name": "Q87047886", "si_name": "Q87047886",
"symbol": "Pa s/m³", "symbol": "Pa s/m³",
@@ -7680,14 +7800,14 @@
"to_si_factor": 1e-12 "to_si_factor": 1e-12
}, },
"Q8799": { "Q8799": {
"si_name": null, "si_name": "Q199",
"symbol": "B", "symbol": "B",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q8805": { "Q8805": {
"si_name": null, "si_name": "Q8805",
"symbol": "bit", "symbol": "bit",
"to_si_factor": null "to_si_factor": 1.0
}, },
"Q88296091": { "Q88296091": {
"si_name": "Q25517", "si_name": "Q25517",
@@ -7714,11 +7834,6 @@
"symbol": "bu (UK)", "symbol": "bu (UK)",
"to_si_factor": 0.03636872 "to_si_factor": 0.03636872
}, },
"Q89662131": {
"si_name": "Q25517",
"symbol": "pt (UK)",
"to_si_factor": 0.00056826125
},
"Q89992008": { "Q89992008": {
"si_name": "Q89992008", "si_name": "Q89992008",
"symbol": "F⁻¹", "symbol": "F⁻¹",
@@ -7745,9 +7860,9 @@
"to_si_factor": null "to_si_factor": null
}, },
"Q9048643": { "Q9048643": {
"si_name": null, "si_name": "Q25517",
"symbol": "nl", "symbol": "nl",
"to_si_factor": null "to_si_factor": 1e-12
}, },
"Q905912": { "Q905912": {
"si_name": "Q281096", "si_name": "Q281096",
+9 -7
View File
@@ -8,8 +8,8 @@
There is a command line for developer purposes and for deeper analysis. Here is There is a command line for developer purposes and for deeper analysis. Here is
an example in which the command line is called in the development environment:: an example in which the command line is called in the development environment::
$ ./manage pyenv.cmd bash --norc --noprofile $ ./manage dev.env
(py3) python -m searx.enginelib --help (dev.env)$ python -m searx.enginelib --help
.. hint:: .. hint::
@@ -46,6 +46,7 @@ ENGINES_CACHE: ExpireCacheSQLite = ExpireCacheSQLite.build_cache(
name="ENGINES_CACHE", name="ENGINES_CACHE",
MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days
MAINTENANCE_PERIOD=60 * 60, # 2h MAINTENANCE_PERIOD=60 * 60, # 2h
MAX_VALUE_LEN=1024 * 1024 * 1024, # 1MB
) )
) )
"""Global :py:obj:`searx.cache.ExpireCacheSQLite` instance where the cached """Global :py:obj:`searx.cache.ExpireCacheSQLite` instance where the cached
@@ -71,9 +72,9 @@ def state():
@app.command() @app.command()
def maintenance(force: bool = True): def maintenance(force: bool = True, truncate: bool = False):
"""Carry out maintenance on cache of the engines.""" """Carry out maintenance on cache of the engines."""
ENGINES_CACHE.maintenance(force=force) ENGINES_CACHE.maintenance(force=force, truncate=truncate)
class EngineCache: class EngineCache:
@@ -111,8 +112,8 @@ class EngineCache:
For introspection of the DB, jump into developer environment and run command to For introspection of the DB, jump into developer environment and run command to
show cache state:: show cache state::
$ ./manage pyenv.cmd bash --norc --noprofile $ ./manage dev.env
(py3) python -m searx.enginelib cache state (dev.env)$ python -m searx.enginelib cache state
cache tables and key/values cache tables and key/values
=========================== ===========================
@@ -159,7 +160,8 @@ class EngineCache:
def __init__(self, engine_name: str, expire: int | None = None): def __init__(self, engine_name: str, expire: int | None = None):
self.expire: int = expire or ENGINES_CACHE.cfg.MAXHOLD_TIME self.expire: int = expire or ENGINES_CACHE.cfg.MAXHOLD_TIME
_valid = "-_." + string.ascii_letters + string.digits _valid = "-_." + string.ascii_letters + string.digits
self.table_name: str = "".join([c if c in _valid else "_" for c in engine_name]) # engine_name is a table and SQL table names must start with a letter
self.table_name: str = "eng_" + "".join([c if c in _valid else "_" for c in engine_name])
def set(self, key: str, value: t.Any, expire: int | None = None) -> bool: def set(self, key: str, value: t.Any, expire: int | None = None) -> bool:
return ENGINES_CACHE.set( return ENGINES_CACHE.set(
-13
View File
@@ -116,19 +116,6 @@ class EngineTraits:
return self.all_locale return self.all_locale
return locales.get_engine_locale(searxng_locale, self.regions, default=default) return locales.get_engine_locale(searxng_locale, self.regions, default=default)
def is_locale_supported(self, searxng_locale: str) -> bool:
"""A *locale* (SearXNG's internal representation) is considered to be
supported by the engine if the *region* or the *language* is supported
by the engine.
For verification the functions :py:func:`EngineTraits.get_region` and
:py:func:`EngineTraits.get_language` are used.
"""
if self.data_type == "traits_v1":
return bool(self.get_region(searxng_locale) or self.get_language(searxng_locale))
raise TypeError("engine traits of type %s is unknown" % self.data_type)
def copy(self): def copy(self):
"""Create a copy of the dataclass object.""" """Create a copy of the dataclass object."""
return EngineTraits(**dataclasses.asdict(self)) return EngineTraits(**dataclasses.asdict(self))
+4
View File
@@ -84,6 +84,10 @@ def request(query, params):
def response(resp): def response(resp):
# sometimes 360search returns empty response when called from non-chinese ips
if not resp.text or not resp.text.strip():
return []
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
results = [] results = []
+137
View File
@@ -0,0 +1,137 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=invalid-name
"""500px_ is a online network for photographers with millions of members
worldwide. Photographers come to 500px to discover and share incredible photos,
gain meaningful exposure, compete in photo contests, and license their photos
through our exclusive distribution partners.
.. _500px: https://500px.com
"""
import typing as t
import codecs
import random
import string
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineParams
# about
about = {
"website": "https://500px.com",
"wikidata_id": "Q354894",
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": "JSON",
}
base_url = "https://500px.com"
api_url = "https://api.500px.com"
categories = ["images"]
paging = True
results_per_page = 30
"""Number of results to return in the request.
The default was taken from the WEB UI, where the GraphQL query sets the value to
*static*: ``first: 30``.
"""
SXNG_query = """query PhotoSearchPaginationContainerQuery(
$first: Int, $cursor: String, $search: String!, $sort: PhotoSort, $filters: [PhotoSearchFilter!], $nlp: Boolean
) {
...SXNG_query
}
fragment SXNG_query on Query {
photoSearch(sort: $sort, first: $first, after: $cursor, search: $search, filters: $filters, nlp: $nlp) {
edges {
node {
id
canonicalPath
name
description
width
height
photographer: uploader {
displayName
}
images(sizes: [35, 33]) {
size
url
jpegUrl
webpUrl
id
}
}
cursor
}
}
}
"""
def setup(_) -> bool:
global SXNG_query # pylint: disable=global-statement
rand_str: str = "".join(random.choice(string.ascii_letters) for _ in range(5))
SXNG_query = SXNG_query.replace("SXNG_query", "PhotoSearchPaginationContainer_query_1" + rand_str)
return True
def request(query: str, params: "OnlineParams") -> None:
# cursor is the base64 hash of the string "pos-<offset-1>", e.g. "pos-29" -> "cG9zLTI5"
offset = ((params["pageno"] - 1) * results_per_page) - 1
cursor = codecs.encode(f"pos-{offset}".encode("utf-8"), "base64").decode("utf-8")
params["url"] = f"{api_url}/graphql"
params["method"] = "POST"
params["json"] = {
"operationName": "PhotoSearchPaginationContainerQuery",
"variables": {
"first": results_per_page,
"cursor": cursor,
"search": query,
"sort": "RELEVANCE",
"filters": [],
"nlp": False,
},
"query": SXNG_query,
}
def response(resp: "SXNG_Response"):
res = EngineResults()
json_data = resp.json()["data"]["photoSearch"]
for edge in json_data["edges"]:
node = edge["node"] # pyright: ignore[reportAny]
if not node["images"]:
continue
images: list[dict[str, str]] = sorted(node["images"], key=lambda i: i["size"])
thumbnail_src = images[0]["url"]
img_src = images[-1]["url"]
res.add(
res.types.LegacyResult(
{
"template": "images.html",
"url": base_url + node["canonicalPath"],
"thumbnail_src": thumbnail_src,
"img_src": img_src,
"title": node["name"],
"content": node["description"],
"author": node["photographer"]["displayName"],
"resolution": f"{node['width']}x{node['height']}",
}
)
)
return res
-9
View File
@@ -39,7 +39,6 @@ url_xpath = './h4/a/@href'
title_xpath = './h4/a[1]' title_xpath = './h4/a[1]'
content_xpath = './/p[1]' content_xpath = './/p[1]'
correction_xpath = '//*[@id="didYouMean"]//a' correction_xpath = '//*[@id="didYouMean"]//a'
number_of_results_xpath = '//*[@id="totalResults"]'
name_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@name' name_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@name'
value_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@value' value_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@value'
@@ -107,14 +106,6 @@ def response(resp):
for correction in eval_xpath_list(dom, correction_xpath): for correction in eval_xpath_list(dom, correction_xpath):
results.append({'correction': extract_text(correction)}) results.append({'correction': extract_text(correction)})
# get number of results
number_of_results = eval_xpath(dom, number_of_results_xpath)
if number_of_results:
try:
results.append({'number_of_results': int(extract_text(number_of_results))})
except: # pylint: disable=bare-except
pass
# Update the tokens to the newest ones # Update the tokens to the newest ones
token_str = _get_tokens(dom) token_str = _get_tokens(dom)
CACHE.set('ahmia-tokens', token_str, expire=60 * 60) CACHE.set('ahmia-tokens', token_str, expire=60 * 60)
+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"):
+2 -2
View File
@@ -21,7 +21,7 @@ about = {
categories = ['images'] categories = ['images']
paging = True paging = True
nb_per_page = 20 page_size = 20
search_api = 'https://api.artic.edu/api/v1/artworks/search?' search_api = 'https://api.artic.edu/api/v1/artworks/search?'
image_api = 'https://www.artic.edu/iiif/2/' image_api = 'https://www.artic.edu/iiif/2/'
@@ -34,7 +34,7 @@ def request(query, params):
'q': query, 'q': query,
'page': params['pageno'], 'page': params['pageno'],
'fields': 'id,title,artist_display,medium_display,image_id,date_display,dimensions,artist_titles', 'fields': 'id,title,artist_display,medium_display,image_id,date_display,dimensions,artist_titles',
'limit': nb_per_page, 'limit': page_size,
} }
) )
params['url'] = search_api + args params['url'] = search_api + args
-75
View File
@@ -1,75 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Ask.com"""
from urllib.parse import urlencode
import dateutil
from lxml import html
from searx import utils
# Metadata
about = {
"website": "https://www.ask.com/",
"wikidata_id": 'Q847564',
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": "HTML",
}
# Engine Configuration
categories = ['general']
paging = True
max_page = 5
"""Ask.com has at max 5 pages."""
# Base URL
base_url = "https://www.ask.com/web"
def request(query, params):
query_params = {
"q": query,
"page": params["pageno"],
}
params["url"] = f"{base_url}?{urlencode(query_params)}"
return params
def response(resp):
start_tag = 'window.MESON.initialState = {'
end_tag = '}};'
dom = html.fromstring(resp.text)
script = utils.eval_xpath_getindex(dom, '//script', 0, default=None).text
pos = script.index(start_tag) + len(start_tag) - 1
script = script[pos:]
pos = script.index(end_tag) + len(end_tag) - 1
script = script[:pos]
json_resp = utils.js_obj_str_to_python(script)
results = []
for item in json_resp['search']['webResults']['results']:
pubdate_original = item.get('pubdate_original')
if pubdate_original:
pubdate_original = dateutil.parser.parse(pubdate_original)
metadata = [item.get(field) for field in ['category_l1', 'catsy'] if item.get(field)]
results.append(
{
"url": item['url'].split('&ueid')[0],
"title": item['title'],
"content": item['abstract'],
"publishedDate": pubdate_original,
# "thumbnail": item.get('image_url') or None, # these are not thumbs / to large
"metadata": ' | '.join(metadata),
}
)
return results
+3 -3
View File
@@ -26,7 +26,7 @@ base_url = (
# engine dependent config # engine dependent config
paging = True paging = True
number_of_results = 10 page_size = 10
# shortcuts for advanced search # shortcuts for advanced search
shortcut_dict = { shortcut_dict = {
@@ -57,12 +57,12 @@ def request(query, params):
query = re.sub(key, val, query) query = re.sub(key, val, query)
# basic search # basic search
offset = (params['pageno'] - 1) * number_of_results offset = (params['pageno'] - 1) * page_size
string_args = { string_args = {
'query': urlencode({'query': query}), 'query': urlencode({'query': query}),
'offset': offset, 'offset': offset,
'hits': number_of_results, 'hits': page_size,
} }
params['url'] = base_url.format(**string_args) params['url'] = base_url.format(**string_args)
+3 -16
View File
@@ -13,7 +13,6 @@ implementations are shared by other engines:
""" """
import base64 import base64
import re
import typing as t import typing as t
from urllib.parse import parse_qs, urlencode, urlparse from urllib.parse import parse_qs, urlencode, urlparse
@@ -48,7 +47,7 @@ _safesearch_map: dict[int, str] = {
} }
"""Filter results. 0: None, 1: Moderate, 2: Strict""" """Filter results. 0: None, 1: Moderate, 2: Strict"""
base_url = "https://www.bing.com/search" base_url = "https://www.bing.com"
"""Bing-Web search URL""" """Bing-Web search URL"""
@@ -94,7 +93,7 @@ def override_accept_language(params: "OnlineParams", engine_region: str | None)
params["headers"]["Accept-Language"] = f"{engine_region},{lang};q=0.9" params["headers"]["Accept-Language"] = f"{engine_region},{lang};q=0.9"
def request(query: str, params: "OnlineParams") -> "OnlineParams": def request(query: str, params: "OnlineParams"):
"""Assemble a Bing-Web request.""" """Assemble a Bing-Web request."""
engine_region = traits.get_region(params["searxng_locale"], traits.all_locale) engine_region = traits.get_region(params["searxng_locale"], traits.all_locale)
@@ -110,13 +109,7 @@ def request(query: str, params: "OnlineParams") -> "OnlineParams":
if locale_params: if locale_params:
query_params.update(locale_params) query_params.update(locale_params)
params["url"] = f"{base_url}?{urlencode(query_params)}" params["url"] = f"{base_url}/search?{urlencode(query_params)}"
# in some regions where geoblocking is employed (e.g. China),
# www.bing.com redirects to the regional version of Bing
params["allow_redirects"] = True
return params
def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]: def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
@@ -159,12 +152,6 @@ def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
results.append({"url": href, "title": title, "content": content}) results.append({"url": href, "title": title, "content": content})
if results:
result_len_container = "".join(eval_xpath(dom, '//span[@class="sb_count"]//text()'))
result_len_container = re.sub(r"[^0-9]", "", result_len_container)
if result_len_container:
results.append({"number_of_results": int(result_len_container)})
return results return results
+2 -4
View File
@@ -34,7 +34,7 @@ time_map = {
"year": 60 * 24 * 365, "year": 60 * 24 * 365,
} }
base_url = "https://www.bing.com/images/async" base_url = "https://www.bing.com"
"""Bing-Image search URL""" """Bing-Image search URL"""
@@ -64,9 +64,7 @@ def request(query, params):
if params["time_range"]: if params["time_range"]:
query_params["qft"] = "filterui:age-lt%s" % time_map[params["time_range"]] query_params["qft"] = "filterui:age-lt%s" % time_map[params["time_range"]]
params["url"] = base_url + "?" + urlencode(query_params) params["url"] = base_url + "/images/async?" + urlencode(query_params)
return params
def response(resp): def response(resp):
+2 -4
View File
@@ -44,7 +44,7 @@ time_map = {
difference of *last day* and *last week* in the result list is just marginally. difference of *last day* and *last week* in the result list is just marginally.
Bing does not have news range ``year`` / we use ``month`` instead.""" Bing does not have news range ``year`` / we use ``month`` instead."""
base_url = "https://www.bing.com/news/infinitescrollajax" base_url = "https://www.bing.com"
"""Bing (News) search URL""" """Bing (News) search URL"""
@@ -74,9 +74,7 @@ def request(query, params):
if params["time_range"]: if params["time_range"]:
query_params["qft"] = time_map.get(params["time_range"], 'interval="9"') query_params["qft"] = time_map.get(params["time_range"], 'interval="9"')
params["url"] = base_url + "?" + urlencode(query_params) params["url"] = base_url + "/news/infinitescrollajax?" + urlencode(query_params)
return params
def response(resp): def response(resp):
+2 -2
View File
@@ -29,7 +29,7 @@ paging = True
safesearch = True safesearch = True
time_range_support = True time_range_support = True
base_url = "https://www.bing.com/videos/asyncv2" base_url = "https://www.bing.com"
"""Bing-Video search URL""" """Bing-Video search URL"""
@@ -60,7 +60,7 @@ def request(query, params):
query_params["form"] = "VRFLTR" query_params["form"] = "VRFLTR"
query_params["qft"] = " filterui:videoage-lt%s" % time_map[params["time_range"]] query_params["qft"] = " filterui:videoage-lt%s" % time_map[params["time_range"]]
params["url"] = base_url + "?" + urlencode(query_params) params["url"] = base_url + "/videos/asyncv2?" + urlencode(query_params)
return params return params
+85
View File
@@ -0,0 +1,85 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=invalid-name
"""Cara_ is a social media and portfolio-sharing platform for artists and art
enthusiasts.
With the widespread use of generative AI, Cara_ decided to build a place that
filters out gen AI images so that people searching for authentic creatives and
images can do so easily.
.. _Cara: https://cara.app/about
"""
from urllib.parse import urlencode
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://cara.app",
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": "JSON",
}
base_url = "https://cara.app"
images_url = "https://images.cara.app"
categories = ["images"]
paging = True
results_per_page = 24
# if using HTTP2, we get blocked immediately
enable_http2 = False
def request(query: str, params: "OnlineParams") -> None:
args = {
"q": query,
"sortBy": "Top",
"take": results_per_page,
"skip": (params["pageno"] - 1) * results_per_page,
}
params["url"] = f"{base_url}/api/search/portfolio-posts?{urlencode(args)}"
def response(resp: "SXNG_Response"):
res = EngineResults()
json_data: list[dict[str, t.Any]] = resp.json()
for result in json_data:
thumbnail, img = None, None
i: dict[str, str]
for i in result["images"]:
if thumbnail is None or i["isCoverImg"]:
thumbnail = i
if img is None or not i["isCoverImg"]:
img = i
if not thumbnail or not img:
continue
res.add(
res.types.LegacyResult(
{
"template": "images.html",
"url": f"{base_url}/post/{result['id']}",
"thumbnail_src": f"{images_url}/{thumbnail['src']}?height=256",
"img_src": f"{images_url}/{img['src']}",
"title": result["title"],
"content": result["content"],
"author": result["name"],
}
)
)
return res
+2 -2
View File
@@ -16,7 +16,7 @@ about = {
paging = True paging = True
categories = [] categories = []
number_of_results = 20 page_size = 20
skip_premium = True skip_premium = True
@@ -25,7 +25,7 @@ thumbnail_format = "crop-240x300"
def request(query, params): def request(query, params):
args = {'query': query, 'limit': number_of_results, 'offset': (params['pageno'] - 1) * number_of_results} args = {'query': query, 'limit': page_size, 'offset': (params['pageno'] - 1) * page_size}
params['url'] = f"{base_url}/v2/search-gateway/recipes?{urlencode(args)}" params['url'] = f"{base_url}/v2/search-gateway/recipes?{urlencode(args)}"
return params return params
+3 -3
View File
@@ -56,7 +56,7 @@ the API key in the engine :ref:`core engine config`."""
categories = ["science", "scientific publications"] categories = ["science", "scientific publications"]
paging = True paging = True
nb_per_page = 10 page_size = 10
base_url = "https://api.core.ac.uk/v3/search/works/" base_url = "https://api.core.ac.uk/v3/search/works/"
@@ -77,8 +77,8 @@ def request(query: str, params: "OnlineParams") -> None:
# API v3 uses different parameters # API v3 uses different parameters
search_params = { search_params = {
"q": query, "q": query,
"offset": (params["pageno"] - 1) * nb_per_page, "offset": (params["pageno"] - 1) * page_size,
"limit": nb_per_page, "limit": page_size,
"sort": "relevance", "sort": "relevance",
} }
+2 -2
View File
@@ -38,7 +38,7 @@ about = {
# engine dependent config # engine dependent config
categories = ["videos"] categories = ["videos"]
paging = True paging = True
number_of_results = 10 page_size = 10
time_range_support = True time_range_support = True
time_delta_dict = { time_delta_dict = {
@@ -113,7 +113,7 @@ def request(query, params):
"password_protected": "false", "password_protected": "false",
"private": "false", "private": "false",
"sort": "relevance", "sort": "relevance",
"limit": number_of_results, "limit": page_size,
"fields": ",".join(result_fields), "fields": ",".join(result_fields),
} }
-1
View File
@@ -109,7 +109,6 @@ def search(query: str, params: "RequestParams") -> EngineResults:
kvmap=kvmap, kvmap=kvmap,
) )
) )
res.add(res.types.LegacyResult(number_of_results=count))
# cache counter value for 20sec # cache counter value for 20sec
CACHE.set("count", count, expire=20) CACHE.set("count", count, expire=20)
-2
View File
@@ -176,6 +176,4 @@ def response(resp):
results.append(result) results.append(result)
results.append({'number_of_results': len(json_data['topics'])})
return results return results
-1
View File
@@ -21,7 +21,6 @@ about = {
# engine dependent config # engine dependent config
categories = ['general'] # 'images', 'music', 'videos', 'files' categories = ['general'] # 'images', 'music', 'videos', 'files'
paging = False paging = False
number_of_results = 5
# search-url # search-url
# Doku is OpenSearch compatible # Doku is OpenSearch compatible
-8
View File
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Duden""" """Duden"""
import re
from urllib.parse import quote, urljoin from urllib.parse import quote, urljoin
from lxml import html from lxml import html
from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
@@ -51,13 +50,6 @@ def response(resp):
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
number_of_results_element = eval_xpath_getindex(
dom, '//a[@class="active" and contains(@href,"/suchen/dudenonline")]/span/text()', 0, default=None
)
if number_of_results_element is not None:
number_of_results_string = re.sub('[^0-9]', '', number_of_results_element)
results.append({'number_of_results': int(number_of_results_string)})
for result in eval_xpath_list(dom, '//section[not(contains(@class, "essay"))]'): for result in eval_xpath_list(dom, '//section[not(contains(@class, "essay"))]'):
url = eval_xpath_getindex(result, './/h2/a', 0).get('href') url = eval_xpath_getindex(result, './/h2/a', 0).get('href')
url = urljoin(base_url, url) url = urljoin(base_url, url)
+67
View File
@@ -0,0 +1,67 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Flaticon_ is a database for icons.
.. _Flaticon: https://www.flaticon.com
"""
from urllib.parse import urlencode
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.flaticon.com",
"wikidata_id": "Q105283791",
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": "JSON",
}
base_url = "https://www.flaticon.com"
categories = ["images", "icons"]
paging = True
def request(query: str, params: "OnlineParams") -> None:
args = {
"word": query,
}
params["headers"].update(
{
# important: query term is not URL encoded in the referer string
"Referer": f"{base_url}/search?word={query}",
"X-Requested-With": "XMLHttpRequest",
}
)
params["url"] = f"{base_url}/ajax/search/{params['pageno']}?{urlencode(args)}"
def _fix_url(url: str) -> str:
return url.replace(r"\/", "/")
def response(resp: "SXNG_Response"):
res = EngineResults()
result: dict[str, str] # TBH: dict[str, t.Any]
for result in resp.json()["items"]:
res.add(
res.types.Image(
title=result["name"],
content=", ".join([tag["tag"] for tag in result["tags"]]), # pyright: ignore[reportArgumentType]
url=_fix_url(result["slug"]),
thumbnail_src=_fix_url(result["png"]),
img_src=_fix_url(result["png512"]),
author=result["team_name"],
)
)
return res
+3 -3
View File
@@ -20,7 +20,7 @@ about = {
categories = ['images'] categories = ['images']
nb_per_page = 15 page_size = 15
paging = True paging = True
api_key = None api_key = None
@@ -29,7 +29,7 @@ url = (
'https://api.flickr.com/services/rest/?method=flickr.photos.search' 'https://api.flickr.com/services/rest/?method=flickr.photos.search'
+ '&api_key={api_key}&{text}&sort=relevance' + '&api_key={api_key}&{text}&sort=relevance'
+ '&extras=description%2C+owner_name%2C+url_o%2C+url_n%2C+url_z' + '&extras=description%2C+owner_name%2C+url_o%2C+url_n%2C+url_z'
+ '&per_page={nb_per_page}&format=json&nojsoncallback=1&page={page}' + '&per_page={page_size}&format=json&nojsoncallback=1&page={page}'
) )
photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}' photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
@@ -42,7 +42,7 @@ def build_flickr_url(user_id, photo_id):
def request(query, params): def request(query, params):
params['url'] = url.format( params['url'] = url.format(
text=urlencode({'text': query}), api_key=api_key, nb_per_page=nb_per_page, page=params['pageno'] text=urlencode({'text': query}), api_key=api_key, page_size=page_size, page=params['pageno']
) )
return params return params
+31 -24
View File
@@ -1,15 +1,23 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Fyyd (podcasts)""" """Fyyd (podcasts)"""
import typing as t
from datetime import datetime from datetime import datetime
from urllib.parse import urlencode from urllib.parse import urlencode
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineParams
about = { about = {
'website': 'https://fyyd.de', "website": "https://fyyd.de",
'official_api_documentation': 'https://github.com/eazyliving/fyyd-api', "official_api_documentation": "https://github.com/eazyliving/fyyd-api",
'use_official_api': True, "use_official_api": True,
'require_api_key': False, "require_api_key": False,
'results': 'JSON', "results": "JSON",
} }
categories = [] categories = []
paging = True paging = True
@@ -18,31 +26,30 @@ base_url = "https://api.fyyd.de"
page_size = 10 page_size = 10
def request(query, params): def request(query: str, params: "OnlineParams") -> None:
args = { args = {
'term': query, "term": query,
'count': page_size, "count": page_size,
'page': params['pageno'] - 1, "page": params["pageno"] - 1,
} }
params['url'] = f"{base_url}/0.2/search/podcast?{urlencode(args)}" params["url"] = f"{base_url}/0.2/search/podcast?{urlencode(args)}"
return params
def response(resp): def response(resp: "SXNG_Response"):
results = [] res = EngineResults()
json_results = resp.json()['data'] json_results: list[dict[str, str]] = resp.json()["data"] # pyright: ignore[reportAny]
for result in json_results: for result in json_results:
results.append( res.add(
{ res.types.MainResult(
'url': result['htmlURL'], url=result["htmlURL"],
'title': result['title'], title=result["title"],
'content': result['description'], content=result["description"],
'thumbnail': result['smallImageURL'], thumbnail=result["smallImageURL"],
'publishedDate': datetime.strptime(result['status_since'], '%Y-%m-%d %H:%M:%S'), publishedDate=datetime.strptime(result["status_since"], "%Y-%m-%d %H:%M:%S"),
'metadata': f"Rank: {result['rank']} || {result['episode_count']} episodes", metadata=f"Rank: {result['rank']} || {result['episode_count']} episodes",
} )
) )
return results return res
+19 -1
View File
@@ -278,10 +278,28 @@ def get_google_info(params: "OnlineParams", eng_traits: EngineTraits) -> dict[st
return ret_val return ret_val
def detect_google_sorry(resp): def detect_google_sorry(resp: "SXNG_Response"):
"""Detect Google's bot-protection responses (CAPTCHA / sorry pages).
Google may block requests in several ways:
1. Redirect to sorry.google.com (standard CAPTCHA).
2. HTTP 302 redirect to ``/sorry/index?...`` on the same host -- when the
HTTP client doesn't follow the redirect, the response body is a short
HTML stub with a link to the sorry page.
3. Short HTML response (<2000 bytes) containing "/sorry/" -- a meta-refresh
or JS redirect variant.
"""
if resp.url.host == "sorry.google.com" or resp.url.path.startswith("/sorry"): if resp.url.host == "sorry.google.com" or resp.url.path.startswith("/sorry"):
raise SearxEngineCaptchaException() raise SearxEngineCaptchaException()
if resp.status_code == 302:
raise SearxEngineCaptchaException()
if len(resp.text) < 2000 and "/sorry/" in resp.text:
raise SearxEngineCaptchaException()
def request(query: str, params: "OnlineParams") -> None: def request(query: str, params: "OnlineParams") -> None:
"""Google search request""" """Google search request"""
+188 -159
View File
@@ -23,9 +23,11 @@ The google news API ignores some parameters from the common :ref:`google API`:
.. _num: https://developers.google.com/custom-search/docs/xml_results#numsp .. _num: https://developers.google.com/custom-search/docs/xml_results#numsp
.. _save: https://developers.google.com/custom-search/docs/xml_results#safesp .. _save: https://developers.google.com/custom-search/docs/xml_results#safesp
""" """
import typing as t
from urllib.parse import urlencode import json
import base64 import base64
from urllib.parse import urlencode
from lxml import html from lxml import html
import babel import babel
@@ -44,18 +46,24 @@ from searx.engines.google import (
) )
from searx.enginelib.traits import EngineTraits from searx.enginelib.traits import EngineTraits
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineParams
# about # about
about = { about = {
"website": 'https://news.google.com', "website": "https://news.google.com",
"wikidata_id": 'Q12020', "wikidata_id": "Q12020",
"official_api_documentation": 'https://developers.google.com/custom-search', "official_api_documentation": "https://developers.google.com/custom-search",
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
"results": 'HTML', "results": "HTML",
} }
# engine dependent config # engine dependent config
categories = ['news'] categories = ["news"]
paging = False paging = False
time_range_support = False time_range_support = False
@@ -64,231 +72,252 @@ time_range_support = False
# #
# safesearch : results are identical for safesearch=0 and safesearch=2 # safesearch : results are identical for safesearch=0 and safesearch=2
safesearch = True safesearch = True
base_url: str = "https://news.google.com"
def request(query, params): def request(query: str, params: "OnlineParams") -> None:
"""Google-News search request""" """Google-News search request"""
sxng_locale = params.get('searxng_locale', 'en-US') sxng_locale = params.get("searxng_locale", "en-US")
ceid = locales.get_engine_locale(sxng_locale, traits.custom['ceid'], default='US:en') ceid: str = locales.get_engine_locale(
sxng_locale, traits.custom["ceid"], default="US:en"
) # pyright: ignore[reportAssignmentType]
google_info = get_google_info(params, traits) google_info = get_google_info(params, traits)
google_info['subdomain'] = 'news.google.com' # google news has only one domain google_info["subdomain"] = "news.google.com" # google news has only one domain
ceid_region, ceid_lang = ceid.split(':') ceid_region, ceid_lang = ceid.split(":")
ceid_lang, ceid_suffix = ( ceid_lang, ceid_suffix = (
ceid_lang.split('-') ceid_lang.split(":")
+ [ + [
None, "",
] ]
)[:2] )[:2]
google_info['params']['hl'] = ceid_lang google_info["params"]["hl"] = ceid_lang
if ceid_suffix and ceid_suffix not in ['Hans', 'Hant']: if ceid_suffix and ceid_suffix not in ["Hans", "Hant"]:
if ceid_region.lower() == ceid_lang: if ceid_region.lower() == ceid_lang:
google_info['params']['hl'] = ceid_lang + '-' + ceid_region google_info["params"]["hl"] = ceid_lang + "-" + ceid_region
else: else:
google_info['params']['hl'] = ceid_lang + '-' + ceid_suffix google_info["params"]["hl"] = ceid_lang + "-" + ceid_suffix
elif ceid_region.lower() != ceid_lang: elif ceid_region.lower() != ceid_lang:
if ceid_region in ['AT', 'BE', 'CH', 'IL', 'SA', 'IN', 'BD', 'PT']: if ceid_region in ["AT", "BE", "CH", "IL", "SA", "IN", "BD", "PT"]:
google_info['params']['hl'] = ceid_lang google_info["params"]["hl"] = ceid_lang
else: else:
google_info['params']['hl'] = ceid_lang + '-' + ceid_region google_info["params"]["hl"] = ceid_lang + "-" + ceid_region
google_info['params']['lr'] = 'lang_' + ceid_lang.split('-')[0] google_info["params"]["lr"] = "lang_" + ceid_lang.split("-")[0]
google_info['params']['gl'] = ceid_region google_info["params"]["gl"] = ceid_region
query_url = ( query_url = (
'https://' "https://"
+ google_info['subdomain'] + google_info["subdomain"]
+ "/search?" + "/search?"
+ urlencode( + urlencode(
{ {"q": query, **google_info["params"]},
'q': query,
**google_info['params'],
}
) )
# ceid includes a ':' character which must not be urlencoded # ceid includes a ':' character which must not be urlencoded
+ ('&ceid=%s' % ceid) + ("&ceid=%s" % ceid)
) )
params['url'] = query_url params["url"] = query_url
params['cookies'] = google_info['cookies'] params["cookies"] = google_info["cookies"]
params['headers'].update(google_info['headers']) params["headers"].update(google_info["headers"])
return params
def response(resp): def response(resp: "SXNG_Response") -> EngineResults:
"""Get response from google's search request""" """Get response from google's search request"""
results = []
res = EngineResults()
detect_google_sorry(resp) detect_google_sorry(resp)
# convert the text to dom # convert the text to dom
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
for result in eval_xpath_list(dom, '//div[@class="xrnccd"]'): for result in eval_xpath_list(dom, "//div[@jslog and @data-n-tid and @jsdata]"):
# The first <a> tag in the <article> contains the link to the article url: str = eval_xpath_getindex(result, "./a[@target='_blank']/@href", 0, default=0)
# The href attribute of the <a> tag is a google internal link, we have if not url:
# to decode continue
if url.startswith("./"):
url = base_url + url[1:]
href = eval_xpath_getindex(result, './article/a/@href', 0) # The real URL is often encoded in the "jslog" attribute
href = href.split('?')[0] jslog: str | None = eval_xpath_getindex(result, "./a[@target='_blank']/@jslog", 0, default=None)
href = href.split('/')[-1]
href = base64.urlsafe_b64decode(href + '====')
href = href[href.index(b'http') :].split(b'\xd2')[0]
href = href.decode()
title = extract_text(eval_xpath(result, './article/h3[1]')) # Try to extract the real URL from jslog
real_url: str | None = None
if jslog:
# jslog format is usually: "95014; 5:<base64>; track:click,vis". We
# want the second part (index 1) after splitting by ";"
parts: list[str] = jslog.split(";")
if len(parts) > 1:
b64_data: str = parts[1].split(":")[-1].strip()
# Pad base64 if necessary
b64_data += "=" * (-len(b64_data) % 4)
decoded_data: list[str | None] = json.loads(base64.b64decode(b64_data).decode("utf-8"))
# The URL is typically the last element in the decoded array
if (
isinstance(decoded_data, list)
and isinstance(decoded_data[-1], str)
and decoded_data[-1].startswith("http")
):
real_url = decoded_data[-1]
if real_url:
url = real_url
else:
logger.error(f"no real-url found: {url}")
continue
# The pub_date is mostly a string like 'yesterday', not a real title = extract_text(eval_xpath(result, "./h4")) or ""
# timezone date or time. Therefore we can't use publishedDate.
pub_date = extract_text(eval_xpath(result, './article//time'))
pub_origin = extract_text(eval_xpath(result, './article//a[@data-n-tid]'))
content = ' / '.join([x for x in [pub_origin, pub_date] if x]) # The pub_date is mostly a string like 'yesterday', not a real timezone
# date or time. Therefore we can't use publishedDate and place the
# *pub* sting into the content.
# The image URL is located in a preceding sibling <img> tag, e.g.: pub_date = extract_text(eval_xpath(result, ".//time"))
# "https://lh3.googleusercontent.com/DjhQh7DMszk.....z=-p-h100-w100" pub_origin = extract_text(eval_xpath(result, ".//div[contains(@class, 'vr1PYe')]"))
# These URL are long but not personalized (double checked via tor). content = " / ".join([x for x in [pub_origin, pub_date] if x])
thumbnail = extract_text(result.xpath('preceding-sibling::a/figure/img/@src')) thumbnail: str = eval_xpath_getindex(result, ".//figure/img/@src", 0, default="")
if thumbnail and thumbnail.startswith("/"):
thumbnail = base_url + thumbnail
results.append( res.add(
{ res.types.MainResult(
'url': href, url=url,
'title': title, title=title,
'content': content, content=content,
'thumbnail': thumbnail, thumbnail=thumbnail,
} )
) )
# return results return res
return results
ceid_list = [ ceid_list = [
'AE:ar', "AE:ar",
'AR:es-419', "AR:es-419",
'AT:de', "AT:de",
'AU:en', "AU:en",
'BD:bn', "BD:bn",
'BE:fr', "BE:fr",
'BE:nl', "BE:nl",
'BG:bg', "BG:bg",
'BR:pt-419', "BR:pt-419",
'BW:en', "BW:en",
'CA:en', "CA:en",
'CA:fr', "CA:fr",
'CH:de', "CH:de",
'CH:fr', "CH:fr",
'CL:es-419', "CL:es-419",
'CN:zh-Hans', "CN:zh-Hans",
'CO:es-419', "CO:es-419",
'CU:es-419', "CU:es-419",
'CZ:cs', "CZ:cs",
'DE:de', "DE:de",
'EG:ar', "EE:et",
'ES:es', "EG:ar",
'ET:en', "ES:ca",
'FR:fr', "ES:es",
'GB:en', "ET:en",
'GH:en', "FI:fi",
'GR:el', "FR:fr",
'HK:zh-Hant', "GB:en",
'HU:hu', "GH:en",
'ID:en', "GR:el",
'ID:id', "HK:zh-Hant",
'IE:en', "HU:hu",
'IL:en', "ID:en",
'IL:he', "ID:id",
'IN:bn', "IE:en",
'IN:en', "IL:en",
'IN:hi', "IL:he",
'IN:ml', "IN:bn",
'IN:mr', "IN:en",
'IN:ta', "IN:gu",
'IN:te', "IN:hi",
'IT:it', "IN:ml",
'JP:ja', "IN:mr",
'KE:en', "IN:pa",
'KR:ko', "IN:ta",
'LB:ar', "IN:te",
'LT:lt', "IT:it",
'LV:en', "JP:ja",
'LV:lv', "KE:en",
'MA:fr', "KR:ko",
'MX:es-419', "LB:ar",
'MY:en', "LT:lt",
'NA:en', "LV:en",
'NG:en', "LV:lv",
'NL:nl', "MA:fr",
'NO:no', "MY:en",
'NZ:en', "MY:ms",
'PE:es-419', "NA:en",
'PH:en', "NG:en",
'PK:en', "NL:nl",
'PL:pl', "NO:no",
'PT:pt-150', "NZ:en",
'RO:ro', "PH:en",
'RS:sr', "PK:en",
'RU:ru', "PL:pl",
'SA:ar', "RO:ro",
'SE:sv', "RS:sr",
'SG:en', "RU:ru",
'SI:sl', "SA:ar",
'SK:sk', "SE:sv",
'SN:fr', "SG:en",
'TH:th', "SI:sl",
'TR:tr', "SK:sk",
'TW:zh-Hant', "SN:fr",
'TZ:en', "TH:th",
'UA:ru', "TR:tr",
'UA:uk', "TZ:en",
'UG:en', "UA:ru",
'US:en', "UA:uk",
'US:es-419', "UG:en",
'VE:es-419', "US:en",
'VN:vi', "VN:vi",
'ZA:en', "ZA:en",
'ZW:en', "ZW:en",
] ]
"""List of region/language combinations supported by Google News. Values of the """List of region/language combinations supported by Google News. Values of the
``ceid`` argument of the Google News REST API.""" ``ceid`` argument of the Google News REST API."""
_skip_values = [ _skip_values = [
'ET:en', # english (ethiopia) "ET:en", # english (ethiopia)
'ID:en', # english (indonesia) "ID:en", # english (indonesia)
'LV:en', # english (latvia) "LV:en", # english (latvia)
] ]
_ceid_locale_map = {'NO:no': 'nb-NO'} _ceid_locale_map = {"NO:no": "nb-NO"}
def fetch_traits(engine_traits: EngineTraits): def fetch_traits(engine_traits: EngineTraits):
_fetch_traits(engine_traits, add_domains=False) _fetch_traits(engine_traits, add_domains=False)
engine_traits.custom['ceid'] = {} engine_traits.custom["ceid"] = {}
for ceid in ceid_list: for ceid in ceid_list:
if ceid in _skip_values: if ceid in _skip_values:
continue continue
region, lang = ceid.split(':') region, lang = ceid.split(":")
x = lang.split('-') x = lang.split("-")
if len(x) > 1: if len(x) > 1:
if x[1] not in ['Hant', 'Hans']: if x[1] not in ["Hant", "Hans"]:
lang = x[0] lang = x[0]
sxng_locale = _ceid_locale_map.get(ceid, lang + '-' + region) sxng_locale = _ceid_locale_map.get(ceid, lang + "-" + region)
try: try:
locale = babel.Locale.parse(sxng_locale, sep='-') locale = babel.Locale.parse(sxng_locale, sep="-")
except babel.UnknownLocaleError: except babel.UnknownLocaleError:
print("ERROR: %s -> %s is unknown by babel" % (ceid, sxng_locale)) print("ERROR: %s -> %s is unknown by babel" % (ceid, sxng_locale))
continue continue
engine_traits.custom['ceid'][locales.region_tag(locale)] = ceid engine_traits.custom["ceid"][locales.region_tag(locale)] = ceid
+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)
+3 -3
View File
@@ -51,7 +51,7 @@ about = {
# engine dependent config # engine dependent config
categories = ['general'] categories = ['general']
paging = True paging = True
number_of_results = 5 page_size = 5
search_type: str = 'nearmatch' search_type: str = 'nearmatch'
"""Which type of search to perform. One of the following values: ``nearmatch``, """Which type of search to perform. One of the following values: ``nearmatch``,
@@ -110,7 +110,7 @@ def request(query, params):
params['language'] = params['language'].split('-')[0] params['language'] = params['language'].split('-')[0]
api_url = f"{base_url.rstrip('/')}/{api_path}?".format(language=params['language']) api_url = f"{base_url.rstrip('/')}/{api_path}?".format(language=params['language'])
offset = (params['pageno'] - 1) * number_of_results offset = (params['pageno'] - 1) * page_size
args = { args = {
'action': 'query', 'action': 'query',
@@ -118,7 +118,7 @@ def request(query, params):
'format': 'json', 'format': 'json',
'srsearch': query, 'srsearch': query,
'sroffset': offset, 'sroffset': offset,
'srlimit': number_of_results, 'srlimit': page_size,
'srwhat': search_type, 'srwhat': search_type,
'srprop': srprop, 'srprop': srprop,
'srsort': srsort, 'srsort': srsort,
+3 -3
View File
@@ -14,7 +14,7 @@ about = {
} }
# engine dependent config # engine dependent config
number_of_results = 20 # Don't put this over 5000 page_size = 20 # Don't put this over 5000
categories = ["it", "packages"] categories = ["it", "packages"]
disabled = True disabled = True
shortcut = "cpan" shortcut = "cpan"
@@ -43,7 +43,7 @@ query_data_template = {
{"date": {"order": "desc"}}, {"date": {"order": "desc"}},
], ],
'_source': ['documentation', "abstract"], '_source': ['documentation', "abstract"],
'size': number_of_results, 'size': page_size,
} }
search_url = urlunparse(["https", "fastapi.metacpan.org", "/v1/file/_search", "", "", ""]) search_url = urlunparse(["https", "fastapi.metacpan.org", "/v1/file/_search", "", "", ""])
@@ -53,7 +53,7 @@ def request(query, params):
params["method"] = "POST" params["method"] = "POST"
query_data = query_data_template query_data = query_data_template
query_data["query"]["multi_match"]["query"] = query query_data["query"]["multi_match"]["query"] = query
query_data["from"] = (params["pageno"] - 1) * number_of_results query_data["from"] = (params["pageno"] - 1) * page_size
params["json"] = query_data params["json"] = query_data
return params return params
+4 -2
View File
@@ -59,8 +59,6 @@ def request(query, params):
args = { args = {
"q": query, "q": query,
"safe": min(params["safesearch"], 1), "safe": min(params["safesearch"], 1),
language_param: traits.get_language(params["searxng_locale"], traits.custom["language_all"]),
region_param: traits.get_region(params["searxng_locale"], traits.custom["region_all"]),
} }
if search_type: if search_type:
@@ -76,6 +74,10 @@ def request(query, params):
logger.debug(args["since"]) logger.debug(args["since"])
params["url"] = f"{base_url}/search?{urlencode(args)}" params["url"] = f"{base_url}/search?{urlencode(args)}"
params["cookies"] = {
language_param: traits.get_language(params["searxng_locale"], traits.custom["language_all"]),
region_param: traits.get_region(params["searxng_locale"], traits.custom["region_all"]),
}
return params return params
-1
View File
@@ -93,7 +93,6 @@ def search(query, params) -> EngineResults:
query = _client.find({key: q}).skip((params['pageno'] - 1) * results_per_page).limit(results_per_page) query = _client.find({key: q}).skip((params['pageno'] - 1) * results_per_page).limit(results_per_page)
res.add(res.types.LegacyResult(number_of_results=query.count()))
for row in query: for row in query:
del row['_id'] del row['_id']
kvmap = {str(k): str(v) for k, v in row.items()} kvmap = {str(k): str(v) for k, v in row.items()}
+3 -3
View File
@@ -21,15 +21,15 @@ about = {
categories = ['images'] categories = ['images']
paging = True paging = True
nb_per_page = 20 page_size = 20
base_url = 'https://api.openverse.org/v1/images/' base_url = 'https://api.openverse.org/v1/images/'
search_string = '?page={page}&page_size={nb_per_page}&format=json&{query}' search_string = '?page={page}&page_size={page_size}&format=json&{query}'
def request(query, params): def request(query, params):
search_path = search_string.format(query=urlencode({'q': query}), nb_per_page=nb_per_page, page=params['pageno']) search_path = search_string.format(query=urlencode({'q': query}), page_size=page_size, page=params['pageno'])
params['url'] = base_url + search_path params['url'] = base_url + search_path
+2 -2
View File
@@ -20,7 +20,7 @@ about = {
# engine dependent config # engine dependent config
categories = ['map'] categories = ['map']
paging = False paging = False
number_of_results = 10 page_size = 10
# search-url # search-url
base_url = 'https://photon.komoot.io/' base_url = 'https://photon.komoot.io/'
@@ -33,7 +33,7 @@ supported_languages = ['de', 'en', 'fr', 'it']
# do search-request # do search-request
def request(query, params): def request(query, params):
params['url'] = base_url + search_string.format(query=urlencode({'q': query}), limit=number_of_results) params['url'] = base_url + search_string.format(query=urlencode({'q': query}), limit=page_size)
if params['language'] != 'all': if params['language'] != 'all':
language = params['language'].split('_')[0] language = params['language'].split('_')[0]
-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 -3
View File
@@ -57,7 +57,7 @@ categories = ["science", "scientific publications"]
eutils_api = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils" eutils_api = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
# engine dependent config # engine dependent config
number_of_results = 10 page_size = 10
pubmed_url = "https://www.ncbi.nlm.nih.gov/pubmed/" pubmed_url = "https://www.ncbi.nlm.nih.gov/pubmed/"
@@ -67,8 +67,8 @@ def request(query: str, params: "OnlineParams") -> None:
{ {
"db": "pubmed", "db": "pubmed",
"term": query, "term": query,
"retstart": (params["pageno"] - 1) * number_of_results, "retstart": (params["pageno"] - 1) * page_size,
"hits": number_of_results, "hits": page_size,
} }
) )
esearch_url = f"{eutils_api}/esearch.fcgi?{args}" esearch_url = f"{eutils_api}/esearch.fcgi?{args}"
+112 -148
View File
@@ -6,7 +6,6 @@ engineered by reading the network log of https://www.qwant.com/ queries.
For Qwant's *web-search* two alternatives are implemented: For Qwant's *web-search* two alternatives are implemented:
- ``web``: uses the :py:obj:`api_url` which returns a JSON structure - ``web``: uses the :py:obj:`api_url` which returns a JSON structure
- ``web-lite``: uses the :py:obj:`web_lite_url` which returns a HTML page
Configuration Configuration
@@ -22,7 +21,7 @@ This implementation is used by different qwant engines in the :ref:`settings.yml
.. code:: yaml .. code:: yaml
- name: qwant - name: qwant
qwant_categ: web-lite # alternatively use 'web' qwant_categ: web
... ...
- name: qwant news - name: qwant news
qwant_categ: news qwant_categ: news
@@ -39,6 +38,8 @@ Implementations
""" """
import typing as t
from datetime import ( from datetime import (
datetime, datetime,
timedelta, timedelta,
@@ -47,8 +48,7 @@ from json import loads
from urllib.parse import urlencode from urllib.parse import urlencode
import babel import babel
import lxml from flask_babel import gettext # pyright: ignore[reportUnknownVariableType]
from flask_babel import gettext
from searx.enginelib.traits import EngineTraits from searx.enginelib.traits import EngineTraits
from searx.exceptions import ( from searx.exceptions import (
@@ -59,11 +59,13 @@ from searx.exceptions import (
) )
from searx.network import raise_for_httperror from searx.network import raise_for_httperror
from searx.utils import ( from searx.utils import (
eval_xpath,
eval_xpath_list,
extract_text,
get_embeded_stream_url, get_embeded_stream_url,
) )
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from searx.search.processors import OnlineParams
from searx.extended_types import SXNG_Response
# about # about
about = { about = {
@@ -82,113 +84,66 @@ max_page = 5
"""5 pages maximum (``&p=5``): Trying to do more just results in an improper """5 pages maximum (``&p=5``): Trying to do more just results in an improper
redirect""" redirect"""
qwant_categ = None qwant_categ: str = None # pyright: ignore[reportAssignmentType]
"""One of ``web-lite`` (or ``web``), ``news``, ``images`` or ``videos``""" """One of ``web``, ``news``, ``images`` or ``videos``"""
safesearch = True safesearch = True
# safe_search_map = {0: '&safesearch=0', 1: '&safesearch=1', 2: '&safesearch=2'}
# fmt: off # fmt: off
qwant_news_locales = [ qwant_news_locales = [
'ca_ad', 'ca_es', 'ca_fr', 'co_fr', 'de_at', 'de_ch', 'de_de', 'en_au', "ca_ad", "ca_es", "ca_fr", "co_fr", "de_at", "de_ch", "de_de", "en_au",
'en_ca', 'en_gb', 'en_ie', 'en_my', 'en_nz', 'en_us', 'es_ad', 'es_ar', "en_ca", "en_gb", "en_ie", "en_my", "en_nz", "en_us", "es_ad", "es_ar",
'es_cl', 'es_co', 'es_es', 'es_mx', 'es_pe', 'eu_es', 'eu_fr', 'fc_ca', "es_cl", "es_co", "es_es", "es_mx", "es_pe", "eu_es", "eu_fr", "fc_ca",
'fr_ad', 'fr_be', 'fr_ca', 'fr_ch', 'fr_fr', 'it_ch', 'it_it', 'nl_be', "fr_ad", "fr_be", "fr_ca", "fr_ch", "fr_fr", "it_ch", "it_it", "nl_be",
'nl_nl', 'pt_ad', 'pt_pt', "nl_nl", "pt_ad", "pt_pt",
] ]
# fmt: on # fmt: on
# search-url
api_url = "https://api.qwant.com/v3/search/" api_url = "https://api.qwant.com/v3/search/"
"""URL of Qwant's API (JSON)""" """URL of Qwant's API (JSON)"""
web_lite_url = "https://lite.qwant.com/"
"""URL of Qwant-Lite (HTML)"""
def request(query: str, params: "OnlineParams") -> None:
def request(query, params):
"""Qwant search request""" """Qwant search request"""
if not query: if not query:
return None return
q_locale = traits.get_region(params["searxng_locale"], default="en_US") q_locale = traits.get_region(params["searxng_locale"], default="en_US")
url = api_url + f"{qwant_categ}?" results_per_page = 10
args = {"q": query} if qwant_categ == "images":
results_per_page = 50
args = {
"q": query,
"count": results_per_page,
"locale": q_locale,
"offset": (params["pageno"] - 1) * results_per_page,
"device": "desktop",
"safesearch": params["safesearch"],
"tgp": 1,
"display": True,
"llm": True,
}
params["raise_for_httperror"] = False params["raise_for_httperror"] = False
if qwant_categ == "web-lite": params["url"] = f"{api_url}{qwant_categ}?{urlencode(args)}"
url = web_lite_url + "?"
args["locale"] = q_locale.lower()
args["l"] = q_locale.split("_")[0]
args["s"] = params["safesearch"]
args["p"] = params["pageno"]
params["raise_for_httperror"] = True
elif qwant_categ == "images":
args["count"] = 50
args["locale"] = q_locale
args["safesearch"] = params["safesearch"]
args["tgp"] = 3
args["offset"] = (params["pageno"] - 1) * args["count"]
else: # web, news, videos
args["count"] = 10
args["locale"] = q_locale
args["safesearch"] = params["safesearch"]
args["llm"] = "false"
args["tgp"] = 3
args["offset"] = (params["pageno"] - 1) * args["count"]
params["url"] = url + urlencode(args)
return params
def response(resp): def response(resp: "SXNG_Response") -> EngineResults:
if qwant_categ == "web-lite":
return parse_web_lite(resp)
return parse_web_api(resp)
def parse_web_lite(resp):
"""Parse results from Qwant-Lite"""
results = []
dom = lxml.html.fromstring(resp.text)
for item in eval_xpath_list(dom, "//section/article"):
if eval_xpath(item, "./span[contains(@class, 'tooltip')]"):
# ignore randomly interspersed advertising adds
continue
results.append(
{
"url": extract_text(eval_xpath(item, "./span[contains(@class, 'url partner')]")),
"title": extract_text(eval_xpath(item, "./h2/a")),
"content": extract_text(eval_xpath(item, "./p")),
}
)
return results
def parse_web_api(resp):
"""Parse results from Qwant's API""" """Parse results from Qwant's API"""
# pylint: disable=too-many-locals, too-many-branches, too-many-statements # pylint: disable=too-many-locals, too-many-branches, too-many-statements
results = [] res = EngineResults()
# Try to load JSON result # Try to load JSON result
search_results: dict[str, t.Any] = {}
try: try:
search_results = loads(resp.text) search_results = resp.json()
except ValueError: except ValueError:
search_results = {} pass
data = search_results.get("data", {}) data: dict[str, t.Any] = search_results.get("data", {}) # pyright: ignore[reportAny]
# check for an API error # check for an API error
if search_results.get("status") != "success": if search_results.get("status") != "success":
@@ -207,13 +162,13 @@ def parse_web_api(resp):
if qwant_categ == "web": if qwant_categ == "web":
# The WEB query contains a list named 'mainline'. This list can contain # The WEB query contains a list named 'mainline'. This list can contain
# different result types (e.g. mainline[0]['type'] returns type of the # different result types (e.g. mainline[0]["type"] returns type of the
# result items in mainline[0]['items'] # result items in mainline[0]["items"]
mainline = data.get("result", {}).get("items", {}).get("mainline", {}) mainline = data.get("result", {}).get("items", {}).get("mainline", {})
else: else:
# Queries on News, Images and Videos do not have a list named 'mainline' # Queries on News, Images and Videos do not have a list named 'mainline'
# in the response. The result items are directly in the list # in the response. The result items are directly in the list
# result['items']. # result["items"].
mainline = data.get("result", {}).get("items", []) mainline = data.get("result", {}).get("items", [])
mainline = [ mainline = [
{"type": qwant_categ, "items": mainline}, {"type": qwant_categ, "items": mainline},
@@ -221,8 +176,9 @@ def parse_web_api(resp):
# return empty array if there are no results # return empty array if there are no results
if not mainline: if not mainline:
return [] return res
row: dict[str, t.Any]
for row in mainline: for row in mainline:
mainline_type = row.get("type", "web") mainline_type = row.get("type", "web")
if mainline_type != qwant_categ: if mainline_type != qwant_categ:
@@ -232,90 +188,98 @@ def parse_web_api(resp):
# ignore adds # ignore adds
continue continue
mainline_items = row.get("items", []) mainline_items: list[dict[str, t.Any]] = row.get("items", [])
for item in mainline_items: for item in mainline_items:
title = item.get("title", None)
res_url = item.get("url", None) title: str = item.get("title", "")
res_url: str = item.get("url", "")
pub_date: datetime | None = None
thumbnail: str = ""
content: str = item.get("desc", "")
_date: float | None = item.get("date")
if _date:
try:
pub_date = datetime.fromtimestamp(_date)
except ValueError:
# news' date value milli seconds
pub_date = datetime.fromtimestamp(_date / 1000)
if mainline_type == "web": if mainline_type == "web":
content = item["desc"] res.add(
results.append( res.types.MainResult(
{ title=title,
"title": title, url=res_url,
"url": res_url, content=content,
"content": content, )
}
) )
elif mainline_type == "news": elif mainline_type == "news":
pub_date = item["date"]
if pub_date is not None:
pub_date = datetime.fromtimestamp(pub_date)
news_media = item.get("media", []) news_media = item.get("media", [])
thumbnail = None
if news_media: if news_media:
thumbnail = news_media[0].get("pict", {}).get("url", None) thumbnail = news_media[0].get("pict", {}).get("url", "")
results.append(
{ res.add(
"title": title, res.types.MainResult(
"url": res_url, title=title,
"publishedDate": pub_date, content=content,
"thumbnail": thumbnail, url=res_url,
} publishedDate=pub_date,
thumbnail=thumbnail,
)
) )
elif mainline_type == "images": elif mainline_type == "images":
thumbnail = item["thumbnail"] res.add(
img_src = item["media"] res.types.LegacyResult(
results.append( title=title,
{ url=res_url,
"title": title, template="images.html",
"url": res_url, thumbnail_src=item["thumbnail"] or "",
"template": "images.html", img_src=item["media"] or "",
"thumbnail_src": thumbnail, resolution=f"{item['width']} x {item['height']}",
"img_src": img_src, img_format=item.get("thumb_type"),
"resolution": f"{item['width']} x {item['height']}", )
"img_format": item.get("thumb_type"),
}
) )
elif mainline_type == "videos": elif mainline_type == "videos":
# some videos do not have a description: while qwant-video # some videos do not have a description: while qwant-video
# returns an empty string, such video from a qwant-web query # returns an empty string, such video from a qwant-web query
# miss the 'desc' key. # miss the 'desc' key.
d, s, c = item.get("desc"), item.get("source"), item.get("channel")
content_parts = [] d: str = item.get("desc", "")
s: str = item.get("source", "")
c: str = item.get("channel", "")
content_parts: list[str] = []
if d: if d:
content_parts.append(d) content_parts.append(f"{d}")
if s: if s:
content_parts.append("%s: %s " % (gettext("Source"), s)) content_parts.append(f"{gettext('Source')}: {s} ")
if c: if c:
content_parts.append("%s: %s " % (gettext("Channel"), c)) content_parts.append(f"{gettext('Channel')}: {c} ")
content = " // ".join(content_parts) content = " // ".join(content_parts)
length = item["duration"]
if length is not None: length = timedelta(milliseconds=(item["duration"] or 0))
length = timedelta(milliseconds=length) thumbnail = item["thumbnail"] or ""
pub_date = item["date"]
if pub_date is not None:
pub_date = datetime.fromtimestamp(pub_date)
thumbnail = item["thumbnail"]
# from some locations (DE and others?) the s2 link do # from some locations (DE and others?) the s2 link do
# response a 'Please wait ..' but does not deliver the thumbnail # response a 'Please wait ..' but does not deliver the thumbnail
thumbnail = thumbnail.replace("https://s2.qwant.com", "https://s1.qwant.com", 1) thumbnail = thumbnail.replace("https://s2.qwant.com", "https://s1.qwant.com", 1)
results.append(
{ res.add(
"title": title, res.types.LegacyResult(
"url": res_url, title=title,
"content": content, url=res_url,
"iframe_src": get_embeded_stream_url(res_url), content=content,
"publishedDate": pub_date, iframe_src=get_embeded_stream_url(res_url),
"thumbnail": thumbnail, publishedDate=pub_date,
"template": "videos.html", thumbnail=thumbnail,
"length": length, template="videos.html",
} length=length,
)
) )
return results return res
def fetch_traits(engine_traits: EngineTraits): def fetch_traits(engine_traits: EngineTraits):
@@ -326,7 +290,7 @@ def fetch_traits(engine_traits: EngineTraits):
from searx.utils import extr from searx.utils import extr
resp = get( resp = get(
about["website"], about["website"], # pyright: ignore[reportArgumentType]
timeout=5, timeout=5,
) )
if not resp.ok: if not resp.ok:
@@ -336,7 +300,7 @@ def fetch_traits(engine_traits: EngineTraits):
q_initial_props = loads(json_string) q_initial_props = loads(json_string)
q_locales = q_initial_props.get("locales") q_locales = q_initial_props.get("locales")
eng_tag_list = set() eng_tag_list: set[str] = set()
for country, v in q_locales.items(): for country, v in q_locales.items():
for lang in v["langs"]: for lang in v["langs"]:
+3 -3
View File
@@ -28,7 +28,7 @@ about = {
paging = True paging = True
categories = ["music", "radio"] categories = ["music", "radio"]
number_of_results = 10 page_size = 10
station_filters = [] # ['countrycode', 'language'] station_filters = [] # ['countrycode', 'language']
"""A list of filters to be applied to the search of radio stations. By default """A list of filters to be applied to the search of radio stations. By default
@@ -100,8 +100,8 @@ def request(query, params):
args = { args = {
"name": query, "name": query,
"order": "votes", "order": "votes",
"offset": (params["pageno"] - 1) * number_of_results, "offset": (params["pageno"] - 1) * page_size,
"limit": number_of_results, "limit": page_size,
"hidebroken": "true", "hidebroken": "true",
"reverse": "true", "reverse": "true",
} }
-2
View File
@@ -54,6 +54,4 @@ def response(resp):
results.extend({'suggestion': s} for s in response_json['suggestions']) results.extend({'suggestion': s} for s in response_json['suggestions'])
results.append({'number_of_results': response_json['number_of_results']})
return results return results
+3 -3
View File
@@ -74,7 +74,7 @@ about = {
categories = ["science", "scientific publications"] categories = ["science", "scientific publications"]
paging = True paging = True
nb_per_page = 10 page_size = 10
"""Number of results to return in the request, see `Pagination and Limits`_ for """Number of results to return in the request, see `Pagination and Limits`_ for
more details. more details.
@@ -109,8 +109,8 @@ def request(query: str, params: "OnlineParams") -> None:
args = { args = {
"api_key": api_key, "api_key": api_key,
"q": query, "q": query,
"s": nb_per_page * (params["pageno"] - 1), "s": page_size * (params["pageno"] - 1),
"p": nb_per_page, "p": page_size,
} }
params["url"] = f"{base_url}?{urlencode(args)}" params["url"] = f"{base_url}?{urlencode(args)}"
# For example, the ``year:`` filter requires a *Premium Plan* subscription. # For example, the ``year:`` filter requires a *Premium Plan* subscription.
+3 -2
View File
@@ -134,7 +134,7 @@ time_range_support = True
safesearch = True safesearch = True
time_range_dict = {"day": "d", "week": "w", "month": "m", "year": "y"} time_range_dict = {"day": "d", "week": "w", "month": "m", "year": "y"}
safesearch_dict = {0: "1", 1: "0", 2: "0"} safesearch_dict = {0: "none", 1: "moderate", 2: "heavy"}
# search-url # search-url
base_url = "https://www.startpage.com" base_url = "https://www.startpage.com"
@@ -251,9 +251,10 @@ def request(query, params):
"t": "device", "t": "device",
"sc": get_sc_code(params), "sc": get_sc_code(params),
"with_date": time_range_dict.get(params["time_range"], ""), "with_date": time_range_dict.get(params["time_range"], ""),
"abp": "1",
"abd": "1", "abd": "1",
"abe": "1", "abe": "1",
"qsr": "all",
"qadf": safesearch_dict[params["safesearch"]],
} }
if engine_language: if engine_language:
-4
View File
@@ -211,8 +211,4 @@ def response(resp) -> EngineResults:
# append number of results # append number of results
number_of_results = json_data.get('num_matches')
if number_of_results:
results.append({'number_of_results': number_of_results})
return results return results
+35 -18
View File
@@ -1,56 +1,73 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""MyMemory Translated""" """MyMemory Translated"""
import typing as t
import urllib.parse import urllib.parse
from searx.utils import html_to_text
from searx.result_types import EngineResults from searx.result_types import EngineResults
if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineDictParams
#
# about # about
about = { about = {
"website": 'https://mymemory.translated.net/', "website": "https://mymemory.translated.net/",
"wikidata_id": None, "wikidata_id": None,
"official_api_documentation": 'https://mymemory.translated.net/doc/spec.php', "official_api_documentation": "https://mymemory.translated.net/doc/spec.php",
"use_official_api": True, "use_official_api": True,
"require_api_key": False, "require_api_key": False,
"results": 'JSON', "results": "JSON",
} }
engine_type = 'online_dictionary' engine_type = "online_dictionary"
categories = ['general', 'translate'] categories = ["general", "translate"]
api_url = "https://api.mymemory.translated.net" api_url = "https://api.mymemory.translated.net"
web_url = "https://mymemory.translated.net" web_url = "https://mymemory.translated.net"
weight = 100 weight = 100
api_key = '' api_key = ""
def request(query, params): # pylint: disable=unused-argument def request(_: str, params: "OnlineDictParams") -> None:
args = {"q": params["query"], "langpair": f"{params['from_lang'][1]}|{params['to_lang'][1]}"} args = {
"q": params["query"],
"langpair": f"{params['from_lang'][1]}|{params['to_lang'][1]}",
}
if api_key: if api_key:
args["key"] = api_key args["key"] = api_key
params['url'] = f"{api_url}/get?{urllib.parse.urlencode(args)}" params['url'] = f"{api_url}/get?{urllib.parse.urlencode(args)}"
return params
def response(resp) -> EngineResults: def response(resp: "SXNG_Response") -> EngineResults:
results = EngineResults() results = EngineResults()
data = resp.json() data: dict[str, t.Any] = resp.json()
params: "OnlineDictParams" = resp.search_params # pyright: ignore[reportAssignmentType]
args = { args = {
"q": resp.search_params["query"], "q": params["query"],
"lang": resp.search_params.get("searxng_locale", "en"), # ui language "lang": params.get("searxng_locale", "en"), # ui language
"sl": resp.search_params['from_lang'][1], "sl": params["from_lang"][1],
"tl": resp.search_params['to_lang'][1], "tl": params["to_lang"][1],
} }
link = f"{web_url}/search.php?{urllib.parse.urlencode(args)}" link = f"{web_url}/search.php?{urllib.parse.urlencode(args)}"
text = data['responseData']['translatedText'] text: str = html_to_text(data["responseData"]["translatedText"])
examples = [f"{m['segment']} : {m['translation']}" for m in data['matches'] if m['translation'] != text] examples: set[str] = set()
match: dict[str, str]
for match in data["matches"]:
_text = html_to_text(match["translation"])
if _text != text:
_seg = html_to_text(match["segment"])
examples.add(f"{_seg} : {_text}")
item = results.types.Translations.Item(text=text, examples=examples) item = results.types.Translations.Item(text=text, examples=list(examples))
results.add(results.types.Translations(translations=[item], url=link)) results.add(results.types.Translations(translations=[item], url=link))
return results return results
+3 -3
View File
@@ -62,7 +62,7 @@ about = {
categories: list[str] = [] categories: list[str] = []
paging = True paging = True
number_of_results = 10 page_size = 10
wc_api_url = "https://commons.wikimedia.org/w/api.php" wc_api_url = "https://commons.wikimedia.org/w/api.php"
wc_search_type: str = "" wc_search_type: str = ""
@@ -107,8 +107,8 @@ def request(query: str, params: "OnlineParams") -> None:
"generator": "search", "generator": "search",
"gsrnamespace": "6", # https://www.mediawiki.org/wiki/Help:Namespaces#Renaming_namespaces "gsrnamespace": "6", # https://www.mediawiki.org/wiki/Help:Namespaces#Renaming_namespaces
"gsrprop": "snippet", "gsrprop": "snippet",
"gsrlimit": number_of_results, "gsrlimit": page_size,
"gsroffset": number_of_results * (params["pageno"] - 1), "gsroffset": page_size * (params["pageno"] - 1),
"gsrsearch": f"filetype:{filetype} {query}", "gsrsearch": f"filetype:{filetype} {query}",
# imageinfo: https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bimageinfo # imageinfo: https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bimageinfo
"iiprop": "url|size|mime", "iiprop": "url|size|mime",
+116 -73
View File
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""This module implements the Wikidata engine. Some implementations are shared """This module implements the Wikidata engine.
from :ref:`wikipedia engine`.
Some implementations are shared from :ref:`wikipedia engine`.
""" """
# pylint: disable=missing-class-docstring # pylint: disable=missing-class-docstring
@@ -14,6 +14,7 @@ from json import loads
from dateutil.parser import isoparse from dateutil.parser import isoparse
from babel.dates import format_datetime, format_date, format_time, get_datetime_format from babel.dates import format_datetime, format_date, format_time, get_datetime_format
from searx.enginelib import EngineCache
from searx.data import WIKIDATA_UNITS from searx.data import WIKIDATA_UNITS
from searx.network import post, get from searx.network import post, get
from searx.utils import searxng_useragent, get_string_replaces_function from searx.utils import searxng_useragent, get_string_replaces_function
@@ -44,11 +45,15 @@ display_type = ["infobox"]
one will add a hit to the result list. The first one will show a hit in the one will add a hit to the result list. The first one will show a hit in the
info box. Both values can be set, or one of the two can be set.""" info box. Both values can be set, or one of the two can be set."""
CACHE: EngineCache
"""Persistent (SQLite) key/value cache that deletes its values after ``expire``
seconds."""
# SPARQL # SPARQL
SPARQL_ENDPOINT_URL = "https://query.wikidata.org/sparql" SPARQL_ENDPOINT_URL = "https://query.wikidata.org/sparql"
SPARQL_EXPLAIN_URL = "https://query.wikidata.org/bigdata/namespace/wdq/sparql?explain" SPARQL_EXPLAIN_URL = "https://query.wikidata.org/bigdata/namespace/wdq/sparql?explain"
WIKIDATA_PROPERTIES: dict[str | tuple[str, str], str] = { WDPType = dict[str | tuple[str, str], str]
WIKIDATA_PROPERTIES: WDPType = {
"P434": "MusicBrainz", "P434": "MusicBrainz",
"P435": "MusicBrainz", "P435": "MusicBrainz",
"P436": "MusicBrainz", "P436": "MusicBrainz",
@@ -140,7 +145,6 @@ replace_http_by_https = get_string_replaces_function({"http:": "https:"})
class WDAttribute: class WDAttribute:
__slots__ = ("name",)
def __init__(self, name: str): def __init__(self, name: str):
self.name: str = name self.name: str = name
@@ -154,13 +158,13 @@ class WDAttribute:
def get_where(self): def get_where(self):
return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace("{name}", self.name) return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace("{name}", self.name)
def get_wikibase_label(self): def get_wikibase_label(self) -> str:
return "" return ""
def get_group_by(self): def get_group_by(self) -> str:
return "" return ""
def get_str(self, result: dict[str, t.Any], language: str): # pylint: disable=unused-argument def get_str(self, result: dict[str, t.Any], language: str) -> str | None: # pylint: disable=unused-argument
return result.get(self.name + "s") return result.get(self.name + "s")
def __repr__(self): def __repr__(self):
@@ -168,7 +172,7 @@ class WDAttribute:
class WDAmountAttribute(WDAttribute): class WDAmountAttribute(WDAttribute):
def get_select(self): def get_select(self) -> str:
return "?{name} ?{name}Unit".replace("{name}", self.name) return "?{name} ?{name}Unit".replace("{name}", self.name)
def get_where(self): def get_where(self):
@@ -178,21 +182,21 @@ class WDAmountAttribute(WDAttribute):
'{name}', self.name '{name}', self.name
) )
def get_group_by(self): def get_group_by(self) -> str:
return self.get_select() return self.get_select()
def get_str(self, result: dict[str, t.Any], language: str): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
value = result.get(self.name) value: str | None = result.get(self.name)
unit = result.get(self.name + "Unit") unit: str | None = result.get(self.name + "Unit")
if unit is not None: if unit is not None:
unit = unit.replace("http://www.wikidata.org/entity/", "") unit = unit.replace("http://www.wikidata.org/entity/", "")
return value + " " + get_label_for_entity(unit, language) return str(value) + " " + get_label_for_entity(unit, language)
return value return value
class WDArticle(WDAttribute): class WDArticle(WDAttribute):
def __init__(self, language: str, kwargs=None): def __init__(self, language: str, kwargs: dict[str, t.Any] | None = None):
super().__init__("wikipedia") super().__init__("wikipedia")
self.language: str = language self.language: str = language
self.kwargs: dict[str, t.Any] = kwargs or {} self.kwargs: dict[str, t.Any] = kwargs or {}
@@ -215,7 +219,7 @@ class WDArticle(WDAttribute):
def get_group_by(self): def get_group_by(self):
return self.get_select() return self.get_select()
def get_str(self, result, language: str): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
key = "article{language}".replace("{language}", self.language) key = "article{language}".replace("{language}", self.language)
return result.get(key) return result.get(key)
@@ -227,16 +231,16 @@ class WDLabelAttribute(WDAttribute):
def get_where(self): def get_where(self):
return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace("{name}", self.name) return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace("{name}", self.name)
def get_wikibase_label(self): def get_wikibase_label(self) -> str:
return "?{name} rdfs:label ?{name}Label .".replace("{name}", self.name) return "?{name} rdfs:label ?{name}Label .".replace("{name}", self.name)
def get_str(self, result, language): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
return result.get(self.name + "Labels") return result.get(self.name + "Labels")
class WDURLAttribute(WDAttribute): class WDURLAttribute(WDAttribute):
HTTP_WIKIMEDIA_IMAGE = "http://commons.wikimedia.org/wiki/Special:FilePath/" HTTP_WIKIMEDIA_IMAGE: str = "http://commons.wikimedia.org/wiki/Special:FilePath/"
def __init__( def __init__(
self, self,
@@ -265,12 +269,12 @@ class WDURLAttribute(WDAttribute):
""" """
super().__init__(name) super().__init__(name)
self.url_id = url_id self.url_id: str | None = url_id
self.url_path_prefix = url_path_prefix self.url_path_prefix: str | None = url_path_prefix
self.kwargs = kwargs self.kwargs: dict[str, t.Any] = kwargs or {}
def get_str(self, result, language: str): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
value = result.get(self.name + "s") value: str | None = result.get(self.name + "s")
if not value: if not value:
return None return None
@@ -306,16 +310,16 @@ class WDGeoAttribute(WDAttribute):
def get_group_by(self): def get_group_by(self):
return self.get_select() return self.get_select()
def get_str(self, result, language: str): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
latitude = result.get(self.name + "Lat") latitude: str | None = result.get(self.name + "Lat")
longitude = result.get(self.name + "Long") longitude: str | None = result.get(self.name + "Long")
if latitude and longitude: if latitude and longitude:
return latitude + " " + longitude return latitude + " " + longitude
return None return None
def get_geo_url(self, result, osm_zoom=19): def get_geo_url(self, result: dict[str, t.Any], osm_zoom: int = 19) -> str | None:
latitude = result.get(self.name + "Lat") latitude: str | None = result.get(self.name + "Lat")
longitude = result.get(self.name + "Long") longitude: str | None = result.get(self.name + "Long")
if latitude and longitude: if latitude and longitude:
return get_earth_coordinates_url(latitude, longitude, osm_zoom) return get_earth_coordinates_url(latitude, longitude, osm_zoom)
return None return None
@@ -323,9 +327,9 @@ class WDGeoAttribute(WDAttribute):
class WDImageAttribute(WDURLAttribute): class WDImageAttribute(WDURLAttribute):
def __init__(self, name, url_id=None, priority=100): def __init__(self, name: str, url_id: str | None = None, priority: int = 100):
super().__init__(name, url_id) super().__init__(name, url_id)
self.priority = priority self.priority: int = priority
class WDDateAttribute(WDAttribute): class WDDateAttribute(WDAttribute):
@@ -349,11 +353,11 @@ class WDDateAttribute(WDAttribute):
def get_group_by(self): def get_group_by(self):
return self.get_select() return self.get_select()
def format_8(self, value, locale: str): # pylint: disable=unused-argument def format_8(self, value: str, locale: str) -> str: # pylint: disable=unused-argument
# precision: less than a year # precision: less than a year
return value return value
def format_9(self, value, locale: str): def format_9(self, value: str, locale: str) -> str:
year = int(value) year = int(value)
# precision: year # precision: year
if year < 1584: if year < 1584:
@@ -363,17 +367,17 @@ class WDDateAttribute(WDAttribute):
timestamp = isoparse(value) timestamp = isoparse(value)
return format_date(timestamp, format="yyyy", locale=locale) return format_date(timestamp, format="yyyy", locale=locale)
def format_10(self, value, locale: str): def format_10(self, value: str, locale: str) -> str:
# precision: month # precision: month
timestamp = isoparse(value) timestamp = isoparse(value)
return format_date(timestamp, format="MMMM y", locale=locale) return format_date(timestamp, format="MMMM y", locale=locale)
def format_11(self, value, locale: str): def format_11(self, value: str, locale: str) -> str:
# precision: day # precision: day
timestamp = isoparse(value) timestamp = isoparse(value)
return format_date(timestamp, format="full", locale=locale) return format_date(timestamp, format="full", locale=locale)
def format_13(self, value, locale: str): def format_13(self, value: str, locale: str) -> str:
timestamp = isoparse(value) timestamp = isoparse(value)
# precision: minute # precision: minute
return ( return (
@@ -383,11 +387,11 @@ class WDDateAttribute(WDAttribute):
.replace("{1}", format_date(timestamp, "short", locale=locale)) .replace("{1}", format_date(timestamp, "short", locale=locale))
) )
def format_14(self, value, locale): def format_14(self, value: str, locale: str) -> str:
# precision: second. # precision: second.
return format_datetime(isoparse(value), format="full", locale=locale) return format_datetime(isoparse(value), format="full", locale=locale)
DATE_FORMAT = { DATE_FORMAT: dict[str, tuple[str, int]] = {
"0": ("format_8", 1000000000), "0": ("format_8", 1000000000),
"1": ("format_8", 100000000), "1": ("format_8", 100000000),
"2": ("format_8", 10000000), "2": ("format_8", 10000000),
@@ -405,15 +409,15 @@ class WDDateAttribute(WDAttribute):
"14": ("format_14", 0), # second "14": ("format_14", 0), # second
} }
def get_str(self, result, language): def get_str(self, result: dict[str, t.Any], language: str) -> str | None:
value = result.get(self.name) value: str | None = result.get(self.name)
if value == "" or value is None: if value == "" or value is None:
return None return None
precision = result.get(self.name + "timePrecision") _p: str = result.get(self.name + "timePrecision") or "1"
date_format = WDDateAttribute.DATE_FORMAT.get(precision) date_format = WDDateAttribute.DATE_FORMAT.get(_p)
if date_format is not None: if date_format is not None:
format_method = getattr(self, date_format[0]) format_method = getattr(self, date_format[0])
precision = date_format[1] precision: int = date_format[1]
try: try:
if precision >= 1: if precision >= 1:
_t = value.split("-") _t = value.split("-")
@@ -427,9 +431,25 @@ class WDDateAttribute(WDAttribute):
return value return value
WDAttrType = (
WDAttribute
| WDAmountAttribute
| WDArticle
| WDLabelAttribute
| WDURLAttribute
| WDGeoAttribute
| WDImageAttribute
| WDDateAttribute
)
WDAttrList = list[WDAttrType]
def get_headers() -> dict[str, str]: def get_headers() -> dict[str, str]:
# user agent: https://www.mediawiki.org/wiki/Wikidata_Query_Service/User_Manual#Query_limits # user agent: https://www.mediawiki.org/wiki/Wikidata_Query_Service/User_Manual#Query_limits
return {"Accept": "application/sparql-results+json", "User-Agent": searxng_useragent()} return {
"Accept": "application/sparql-results+json",
"User-Agent": f"wikidata engine - {searxng_useragent()}",
}
def get_label_for_entity(entity_id: str, language: str) -> str: def get_label_for_entity(entity_id: str, language: str) -> str:
@@ -445,7 +465,7 @@ def get_label_for_entity(entity_id: str, language: str) -> str:
return name return name
def send_wikidata_query(query: str, method="GET", **kwargs) -> dict[str, t.Any]: def send_wikidata_query(query: str, method: str = "GET", **kwargs: dict[str, t.Any]) -> dict[str, t.Any]:
if method == "GET": if method == "GET":
# query will be cached by wikidata # query will be cached by wikidata
http_response = get(SPARQL_ENDPOINT_URL + "?" + urlencode({"query": query}), headers=get_headers(), **kwargs) http_response = get(SPARQL_ENDPOINT_URL + "?" + urlencode({"query": query}), headers=get_headers(), **kwargs)
@@ -461,15 +481,17 @@ def send_wikidata_query(query: str, method="GET", **kwargs) -> dict[str, t.Any]:
def request(query: str, params: "OnlineParams") -> None: def request(query: str, params: "OnlineParams") -> None:
attributes: tuple[str, list[WDAttribute | WDAmountAttribute | WDLabelAttribute | WDImageAttribute]] attributes: WDAttrList
eng_tag, _wiki_netloc = get_wiki_params(params["searxng_locale"], traits) eng_tag, _wiki_netloc = get_wiki_params(params["searxng_locale"], traits)
query, attributes = get_query(query, eng_tag) query, attributes = get_query(query, eng_tag or "en")
logger.debug("request --> language %s // len(attributes): %s", eng_tag, len(attributes)) logger.debug("request --> language %s // len(attributes): %s", eng_tag, len(attributes))
params["method"] = "POST" params["method"] = "POST"
params["url"] = SPARQL_ENDPOINT_URL params["url"] = SPARQL_ENDPOINT_URL
params["data"] = {"query": query} params["data"] = {"query": query}
params["headers"] = get_headers() params["headers"] = get_headers()
# additional parameters (not a part of OnlineParams)
params["language"] = eng_tag # type: ignore params["language"] = eng_tag # type: ignore
params["attributes"] = attributes # type: ignore params["attributes"] = attributes # type: ignore
@@ -479,14 +501,16 @@ def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
results: list[dict[str, t.Any]] = [] results: list[dict[str, t.Any]] = []
jsonresponse = loads(resp.content.decode()) jsonresponse = loads(resp.content.decode())
# additional parameters ..
language: str = resp.search_params["language"] # type: ignore language: str = resp.search_params["language"] # type: ignore
attributes = resp.search_params["attributes"] # type: ignore attributes: WDAttrList = resp.search_params["attributes"] # type: ignore
logger.debug("request --> language %s // len(attributes): %s", language, len(attributes)) logger.debug("request --> language %s // len(attributes): %s", language, len(attributes))
seen_entities: set[str] = set() seen_entities: set[str] = set()
for result in jsonresponse.get("results", {}).get("bindings", []): for result in jsonresponse.get("results", {}).get("bindings", []):
attribute_result = {key: value["value"] for key, value in result.items()} attribute_result = {key: value["value"] for key, value in result.items()}
entity_url = attribute_result["item"] entity_url: str = attribute_result["item"]
if entity_url not in seen_entities and entity_url not in DUMMY_ENTITY_URLS: if entity_url not in seen_entities and entity_url not in DUMMY_ENTITY_URLS:
seen_entities.add(entity_url) seen_entities.add(entity_url)
results += get_results(attribute_result, attributes, language) results += get_results(attribute_result, attributes, language)
@@ -500,7 +524,7 @@ _IMG_SRC_DEFAULT_URL_PREFIX = "https://commons.wikimedia.org/wiki/Special:FilePa
_IMG_SRC_NEW_URL_PREFIX = "https://upload.wikimedia.org/wikipedia/commons/thumb/" _IMG_SRC_NEW_URL_PREFIX = "https://upload.wikimedia.org/wikipedia/commons/thumb/"
def get_thumbnail(img_src: str) -> str: def get_thumbnail(img_src: str | None) -> str | None:
"""Get Thumbnail image from wikimedia commons """Get Thumbnail image from wikimedia commons
Images from commons.wikimedia.org are (HTTP) redirected to Images from commons.wikimedia.org are (HTTP) redirected to
@@ -539,53 +563,58 @@ def get_thumbnail(img_src: str) -> str:
return img_src return img_src
def get_results(attribute_result: dict[str, t.Any], attributes, language): def get_results(
attribute_result: dict[str, t.Any],
attributes: WDAttrList,
language: str,
):
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
results = [] results: list[dict[str, t.Any]] = []
infobox_title = attribute_result.get("itemLabel") infobox_title: str = attribute_result.get("itemLabel") # pyright: ignore[reportAssignmentType]
infobox_id = attribute_result["item"] infobox_id = attribute_result["item"]
infobox_id_lang = None infobox_id_lang: str | None = None
infobox_urls = [] infobox_urls: list[dict[str, str]] = []
infobox_attributes = [] infobox_attributes: list[dict[str, str]] = []
infobox_content = attribute_result.get("itemDescription", []) infobox_content = attribute_result.get("itemDescription", [])
img_src = None img_src: str | None = None
img_src_priority = 0 img_src_priority = 0
for attribute in attributes: for attribute in attributes:
value = attribute.get_str(attribute_result, language) value: str | None = attribute.get_str(attribute_result, language)
if value is not None and value != "": if value is not None and value != "":
attribute_type = type(attribute)
if attribute_type in (WDURLAttribute, WDArticle): if isinstance(attribute, (WDURLAttribute, WDArticle)):
# get_select() method : there is group_concat(distinct ...;separator=", ") # get_select() method : there is group_concat(distinct ...;separator=", ")
# split the value here # split the value here
for url in value.split(", "): for url in value.split(", "):
infobox_urls.append({"title": attribute.get_label(language), "url": url, **attribute.kwargs}) infobox_urls.append({"title": attribute.get_label(language), "url": url, **attribute.kwargs})
# "normal" results (not infobox) include official website and Wikipedia links. # "normal" results (not infobox) include official website and Wikipedia links.
if "list" in display_type and (attribute.kwargs.get("official") or attribute_type == WDArticle): if "list" in display_type and (
attribute.kwargs.get("official") or isinstance(attribute, WDArticle)
):
results.append({"title": infobox_title, "url": url, "content": infobox_content}) results.append({"title": infobox_title, "url": url, "content": infobox_content})
# update the infobox_id with the wikipedia URL # update the infobox_id with the wikipedia URL
# first the local wikipedia URL, and as fallback the english wikipedia URL # first the local wikipedia URL, and as fallback the english wikipedia URL
if attribute_type == WDArticle and ( if isinstance(attribute, WDArticle) and (
(attribute.language == "en" and infobox_id_lang is None) or attribute.language != "en" (attribute.language == "en" and infobox_id_lang is None) or attribute.language != "en"
): ):
infobox_id_lang = attribute.language infobox_id_lang = attribute.language
infobox_id = url infobox_id = url
elif attribute_type == WDImageAttribute: elif isinstance(attribute, WDImageAttribute):
# this attribute is an image. # this attribute is an image.
# replace the current image only the priority is lower # replace the current image only the priority is lower
# (the infobox contain only one image). # (the infobox contain only one image).
if attribute.priority > img_src_priority: if attribute.priority > img_src_priority:
img_src = get_thumbnail(value) img_src = get_thumbnail(value)
img_src_priority = attribute.priority img_src_priority = attribute.priority
elif attribute_type == WDGeoAttribute: elif isinstance(attribute, WDGeoAttribute):
# geocoordinate link # geocoordinate link
# use the area to get the OSM zoom # use the area to get the OSM zoom
# Note: ignore the unit (must be km² otherwise the calculation is wrong) # Note: ignore the unit (must be km² otherwise the calculation is wrong)
# Should use normalized value p:P2046/psn:P2046/wikibase:quantityAmount # Should use normalized value p:P2046/psn:P2046/wikibase:quantityAmount
area = attribute_result.get("P2046") area = attribute_result.get("P2046")
osm_zoom = area_to_osm_zoom(area) if area else 19 osm_zoom: int = area_to_osm_zoom(area) if area else 19
url = attribute.get_geo_url(attribute_result, osm_zoom=osm_zoom) url = attribute.get_geo_url(attribute_result, osm_zoom=osm_zoom)
if url: if url:
infobox_urls.append({"title": attribute.get_label(language), "url": url, "entity": attribute.name}) infobox_urls.append({"title": attribute.get_label(language), "url": url, "entity": attribute.name})
@@ -622,9 +651,7 @@ def get_results(attribute_result: dict[str, t.Any], attributes, language):
return results return results
def get_query( def get_query(query: str, language: str) -> tuple[str, WDAttrList]:
query: str, language: str
) -> tuple[str, list[WDAttribute | WDAmountAttribute | WDLabelAttribute | WDImageAttribute]]:
attributes = get_attributes(language) attributes = get_attributes(language)
select = [a.get_select() for a in attributes] select = [a.get_select() for a in attributes]
where = list(filter(lambda s: len(s) > 0, [a.get_where() for a in attributes])) where = list(filter(lambda s: len(s) > 0, [a.get_where() for a in attributes]))
@@ -643,7 +670,7 @@ def get_query(
def get_attributes(language: str): def get_attributes(language: str):
# pylint: disable=too-many-statements # pylint: disable=too-many-statements
attributes: list[WDAttribute | WDAmountAttribute | WDLabelAttribute | WDImageAttribute] = [] attributes: WDAttrList = []
def add_value(name: str): def add_value(name: str):
attributes.append(WDAttribute(name)) attributes.append(WDAttribute(name))
@@ -654,7 +681,7 @@ def get_attributes(language: str):
def add_label(name: str): def add_label(name: str):
attributes.append(WDLabelAttribute(name)) attributes.append(WDLabelAttribute(name))
def add_url(name: str, url_id: str | None = None, url_path_prefix: str | None = None, **kwargs): def add_url(name: str, url_id: str | None = None, url_path_prefix: str | None = None, **kwargs: dict[str, t.Any]):
attributes.append(WDURLAttribute(name, url_id, url_path_prefix, kwargs)) attributes.append(WDURLAttribute(name, url_id, url_path_prefix, kwargs))
def add_image(name: str, url_id: str | None = None, priority: int = 1): def add_image(name: str, url_id: str | None = None, priority: int = 1):
@@ -749,7 +776,8 @@ def get_attributes(language: str):
add_value("P498") # currency code (ISO 4217) add_value("P498") # currency code (ISO 4217)
# URL # URL
add_url("P856", official=True) # official website kwargs: dict[str, t.Any] = {"official": True}
add_url("P856", **kwargs) # official website
attributes.append(WDArticle(language)) # wikipedia (user language) attributes.append(WDArticle(language)) # wikipedia (user language)
if not language.startswith("en"): if not language.startswith("en"):
attributes.append(WDArticle("en")) # wikipedia (english) attributes.append(WDArticle("en")) # wikipedia (english)
@@ -796,7 +824,19 @@ def debug_explain_wikidata_query(query: str, method: str = "GET"):
return http_response.content return http_response.content
def init(engine_settings=None): # pylint: disable=unused-argument def init(_):
global CACHE # pylint: disable=global-statement
CACHE = EngineCache("wikidata")
init_wikidata_properties()
def init_wikidata_properties():
global WIKIDATA_PROPERTIES # pylint: disable=global-statement
p: WDPType = CACHE.get(key="WIKIDATA_PROPERTIES")
if p:
WIKIDATA_PROPERTIES = p
return
# WIKIDATA_PROPERTIES : add unit symbols # WIKIDATA_PROPERTIES : add unit symbols
for k, v in WIKIDATA_UNITS.items(): for k, v in WIKIDATA_UNITS.items():
WIKIDATA_PROPERTIES[k] = v["symbol"] WIKIDATA_PROPERTIES[k] = v["symbol"]
@@ -808,7 +848,8 @@ def init(engine_settings=None): # pylint: disable=unused-argument
if attribute.name not in WIKIDATA_PROPERTIES: if attribute.name not in WIKIDATA_PROPERTIES:
wikidata_property_names.append("wd:" + attribute.name) wikidata_property_names.append("wd:" + attribute.name)
query = QUERY_PROPERTY_NAMES.replace("%ATTRIBUTES%", " ".join(wikidata_property_names)) query = QUERY_PROPERTY_NAMES.replace("%ATTRIBUTES%", " ".join(wikidata_property_names))
jsonresponse = send_wikidata_query(query, timeout=20) kwargs: dict[str, t.Any] = {"timeout": 20}
jsonresponse = send_wikidata_query(query, **kwargs)
for result in jsonresponse.get("results", {}).get("bindings", {}): for result in jsonresponse.get("results", {}).get("bindings", {}):
name_field = result.get("name") name_field = result.get("name")
if not name_field: if not name_field:
@@ -818,6 +859,8 @@ def init(engine_settings=None): # pylint: disable=unused-argument
entity_id = result["item"]["value"].replace("http://www.wikidata.org/entity/", "") entity_id = result["item"]["value"].replace("http://www.wikidata.org/entity/", "")
WIKIDATA_PROPERTIES[(entity_id, lang)] = name.capitalize() WIKIDATA_PROPERTIES[(entity_id, lang)] = name.capitalize()
CACHE.set(key="WIKIDATA_PROPERTIES", value=WIKIDATA_PROPERTIES)
def fetch_traits(engine_traits: EngineTraits): def fetch_traits(engine_traits: EngineTraits):
"""Uses languages evaluated from :py:obj:`wikipedia.fetch_wikimedia_traits """Uses languages evaluated from :py:obj:`wikipedia.fetch_wikimedia_traits
+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))
+3 -3
View File
@@ -75,7 +75,7 @@ about = {
# engine dependent config # engine dependent config
categories = ['general'] categories = ['general']
paging = True paging = True
number_of_results = 10 page_size = 10
http_digest_auth_user = "" http_digest_auth_user = ""
"""HTTP digest user for the local YACY instance""" """HTTP digest user for the local YACY instance"""
http_digest_auth_pass = "" http_digest_auth_pass = ""
@@ -125,11 +125,11 @@ def _base_url() -> str:
def request(query, params): def request(query, params):
offset = (params['pageno'] - 1) * number_of_results offset = (params['pageno'] - 1) * page_size
args = { args = {
'query': query, 'query': query,
'startRecord': offset, 'startRecord': offset,
'maximumRecords': number_of_results, 'maximumRecords': page_size,
'contentdom': search_type, 'contentdom': search_type,
'resource': search_mode, 'resource': search_mode,
} }
+12 -11
View File
@@ -87,7 +87,6 @@ def request(query, params):
def response(resp): def response(resp):
if search_type == 'web': if search_type == 'web':
catch_bad_response(resp) catch_bad_response(resp)
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
@@ -106,7 +105,6 @@ def response(resp):
return results return results
if search_type == 'images': if search_type == 'images':
catch_bad_response(resp) catch_bad_response(resp)
html_data = html.fromstring(resp.text) html_data = html.fromstring(resp.text)
@@ -127,22 +125,25 @@ def response(resp):
for _, item_data in json_resp['initialState']['serpList']['items']['entities'].items(): for _, item_data in json_resp['initialState']['serpList']['items']['entities'].items():
title = item_data['snippet']['title'] title = item_data['snippet']['title']
source = item_data['snippet']['url'] source = item_data['snippet']['url']
thumb = item_data['image']
fullsize_image = item_data['viewerData']['dups'][0]['url'] image_source = item_data["viewerData"]["thumb"]
height = item_data['viewerData']['dups'][0]['h'] for i in item_data['viewerData']['dups'] + item_data['viewerData']['preview']:
width = item_data['viewerData']['dups'][0]['w'] if i["h"] > image_source["h"]:
filesize = item_data['viewerData']['dups'][0]['fileSizeInBytes'] image_source = i
humanized_filesize = humanize_bytes(filesize)
humanized_filesize = None
if image_source.get("fileSizeInBytes"):
humanized_filesize = humanize_bytes(image_source["fileSizeInBytes"])
results.append( results.append(
{ {
'title': title, 'title': title,
'url': source, 'url': source,
'img_src': fullsize_image, 'img_src': image_source["url"],
'filesize': humanized_filesize, 'filesize': humanized_filesize,
'thumbnail_src': thumb, 'thumbnail_src': item_data["image"],
'template': 'images.html', 'template': 'images.html',
'resolution': f'{width} x {height}', 'resolution': f'{image_source["w"]} x {image_source["h"]}',
} }
) )
+82 -2
View File
@@ -1,14 +1,17 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Yep (general, images, news)""" """Yep (general, images, news)"""
import re
import typing as t import typing as t
from urllib.parse import urlencode from urllib.parse import urlencode
from searx.result_types import EngineResults from searx.result_types import EngineResults
from searx.utils import html_to_text from searx.utils import html_to_text, eval_xpath_getindex, extract_text
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
from searx.enginelib.traits import EngineTraits
from searx.extended_types import SXNG_Response from searx.extended_types import SXNG_Response
from searx.search.processors import OnlineParams from searx.search.processors import OnlineParams
@@ -29,10 +32,18 @@ enable_http2 = False
results_per_page = 20 results_per_page = 20
_IMPORT_RE = re.compile(r"import\"(.*?)\";")
_LANGUAGE_RE = re.compile(r"\{english:\".*?\",code_string:\"(.*?)\",code:\".*?\"\}")
def request(query: str, params: 'OnlineParams') -> None: def request(query: str, params: 'OnlineParams') -> None:
args = {'query': query, 'safeSearch': safesearch_map[params['safesearch']], 'limit': results_per_page} args = {'query': query, 'safeSearch': safesearch_map[params['safesearch']], 'limit': results_per_page}
params['url'] = f"{base_url}/fs/2/search?{urlencode(args)}"
engine_language: str = traits.get_language(params["searxng_locale"])
if engine_language:
args["hl"] = engine_language
params['url'] = f"{base_url}/search?{urlencode(args)}"
params['headers']['Referer'] = 'https://yep.com/' params['headers']['Referer'] = 'https://yep.com/'
params['headers']['Origin'] = 'https://yep.com' params['headers']['Origin'] = 'https://yep.com'
@@ -50,3 +61,72 @@ def response(resp: 'SXNG_Response') -> EngineResults:
) )
return res return res
def fetch_traits(engine_traits: 'EngineTraits'):
"""Fetch :ref:`languages <yep languages>` and :ref:`regions <yep
regions>` from Yep.
The language options are very well hidden on Yep. To get it, we have to do the following:
- Load the yep.com mainpage and extract the URL of the JavaScript app
- Load the JavaScript source code and extract the URL of all imported modules from it
- Load the imported modules to search for the right one that contains the languages
"""
# pylint: disable=import-outside-toplevel, too-many-branches
from lxml import html
import babel
from searx.locales import language_tag
from searx.network import get # see https://github.com/searxng/searxng/issues/762
from searx.utils import gen_useragent
web_base_url = "https://yep.com"
headers = {
"User-Agent": gen_useragent(),
"Referer": f"{web_base_url}/",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
}
resp = get(web_base_url, headers=headers, timeout=5)
if not resp.ok:
raise RuntimeError("Response from Yep languages is not OK.")
doc = html.fromstring(resp.text)
url = eval_xpath_getindex(doc, "//script[contains(@src, 'PageApp')]/@src", index=0)
resp = get("https:" + extract_text(url), headers=headers, timeout=5)
if not resp.ok:
raise RuntimeError("Response from Yep languages is not OK.")
language_codes = []
for script_path in _IMPORT_RE.findall(resp.text):
resp = get(f"{web_base_url}{script_path}", headers=headers, timeout=5)
if not resp.ok:
raise RuntimeError("Response from Yep languages is not OK.")
for match in _LANGUAGE_RE.findall(resp.text):
language_codes.append(match)
if language_codes:
break
for language_code in language_codes:
try:
sxng_tag = language_tag(babel.Locale.parse(language_code, sep="-"))
except babel.UnknownLocaleError:
# silently ignore unknown languages
continue
# print("%-20s: %s <-- %s" % (extract_text(option), country_tag, sxng_tag))
conflict = engine_traits.languages.get(sxng_tag)
if conflict:
if conflict != sxng_tag:
print("CONFLICT: babel %s --> %s, %s" % (sxng_tag, conflict, language_code))
continue
engine_traits.languages[sxng_tag] = language_code
+2 -2
View File
@@ -30,7 +30,7 @@ import httpx
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
import searx.preferences import searx.preferences
import searx.results import searx.results
from searx.search.processors import OnlineParamTypes from searx.search.processors import OnlineParamTypes, OnlineDictParams, OnlineCurrenciesParams
class SXNG_Request(flask.Request): class SXNG_Request(flask.Request):
@@ -83,4 +83,4 @@ class SXNG_Response(httpx.Response):
""" """
ok: bool ok: bool
search_params: "OnlineParamTypes" search_params: "OnlineParamTypes | OnlineDictParams | OnlineCurrenciesParams"
+3 -3
View File
@@ -16,12 +16,12 @@ IMDB_PREFIX_TO_URL_ID = {
HTTP_WIKIMEDIA_IMAGE = 'http://commons.wikimedia.org/wiki/Special:FilePath/' HTTP_WIKIMEDIA_IMAGE = 'http://commons.wikimedia.org/wiki/Special:FilePath/'
def get_imdb_url_id(imdb_item_id): def get_imdb_url_id(imdb_item_id: str):
id_prefix = imdb_item_id[:2] id_prefix = imdb_item_id[:2]
return IMDB_PREFIX_TO_URL_ID.get(id_prefix) return IMDB_PREFIX_TO_URL_ID.get(id_prefix)
def get_wikimedia_image_id(url): def get_wikimedia_image_id(url: str):
if url.startswith(HTTP_WIKIMEDIA_IMAGE): if url.startswith(HTTP_WIKIMEDIA_IMAGE):
return url[len(HTTP_WIKIMEDIA_IMAGE) :] return url[len(HTTP_WIKIMEDIA_IMAGE) :]
if url.startswith('File:'): if url.startswith('File:'):
@@ -29,7 +29,7 @@ def get_wikimedia_image_id(url):
return url return url
def get_external_url(url_id, item_id, alternative="default"): def get_external_url(url_id: str, item_id: str | None, alternative: str = "default") -> str | None:
"""Return an external URL or None if url_id is not found. """Return an external URL or None if url_id is not found.
url_id can take value from data/external_urls.json url_id can take value from data/external_urls.json
+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
+19 -4
View File
@@ -19,6 +19,7 @@
__all__ = ["Result"] __all__ = ["Result"]
import typing as t import typing as t
import types
import re import re
import urllib.parse import urllib.parse
@@ -29,7 +30,9 @@ from collections.abc import Callable
import msgspec import msgspec
from searx import logger as log from searx import logger
log = logger.getChild("result_types")
WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U) WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
UNSET = object() UNSET = object()
@@ -125,8 +128,20 @@ def _filter_urls(
if not url_src: if not url_src:
continue continue
new_url = filter_func(result, field_name, url_src) try:
# log.debug("filter_urls: filter_func(result, %s) '%s' -> '%s'", field_name, field_value, new_url) new_url = filter_func(result, field_name, url_src)
except Exception as exc: # pylint: disable=broad-exception-caught
# pylint: disable=no-member
_tb: types.TracebackType = exc.__traceback__.tb_next.tb_next # type: ignore
log.error(
"filter_urls (field '%s'): ignore %s from callback %s:%s",
field_name,
repr(exc),
_tb.tb_frame.f_code.co_filename,
_tb.tb_lineno,
)
continue
if isinstance(new_url, bool): if isinstance(new_url, bool):
if new_url: if new_url:
# log.debug("filter_urls: unchanged field %s URL %s", field_name, field_value) # log.debug("filter_urls: unchanged field %s URL %s", field_name, field_value)
@@ -529,7 +544,7 @@ class LegacyResult(dict[str, t.Any]):
# the img_src are equal. # the img_src are equal.
return hash(f"{self.template}|{self.url}|{self.img_src}") return hash(f"{self.template}|{self.url}|{self.img_src}")
if not any(cls in self for cls in ["suggestion", "correction", "infobox", "number_of_results", "engine_data"]): if not any(cls in self for cls in ["suggestion", "correction", "infobox", "engine_data"]):
# Ordinary url-results are equal if their values for template, # Ordinary url-results are equal if their values for template,
# parsed_url (without schema) and img_src` are equal. # parsed_url (without schema) and img_src` are equal.
+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)."""
-25
View File
@@ -69,7 +69,6 @@ class ResultContainer:
self.answers = AnswerSet() self.answers = AnswerSet()
self.corrections = set() self.corrections = set()
self._number_of_results: list[int] = []
self.engine_data: dict[str, dict[str, str]] = defaultdict(dict) self.engine_data: dict[str, dict[str, str]] = defaultdict(dict)
self._closed: bool = False self._closed: bool = False
self.paging: bool = False self.paging: bool = False
@@ -135,11 +134,6 @@ class ResultContainer:
self._merge_infobox(result) self._merge_infobox(result)
continue continue
if "number_of_results" in result:
if self.on_result(result):
self._number_of_results.append(result["number_of_results"])
continue
if "engine_data" in result: if "engine_data" in result:
if self.on_result(result): if self.on_result(result):
if result.engine: if result.engine:
@@ -252,25 +246,6 @@ class ResultContainer:
self._main_results_sorted = gresults self._main_results_sorted = gresults
return self._main_results_sorted return self._main_results_sorted
@property
def number_of_results(self) -> int:
"""Returns the average of results number, returns zero if the average
result number is smaller than the actual result count."""
if not self._closed:
log.error("call to ResultContainer.number_of_results before ResultContainer.close")
return 0
with self._lock:
resultnum_sum = sum(self._number_of_results)
if not resultnum_sum or not self._number_of_results:
return 0
average = int(resultnum_sum / len(self._number_of_results))
if average < len(self.get_ordered_results()):
average = 0
return average
def add_unresponsive_engine(self, engine_name: str, error_type: str, suspended: bool = False): def add_unresponsive_engine(self, engine_name: str, error_type: str, suspended: bool = False):
with self._lock: with self._lock:
if self._closed: if self._closed:
+29 -11
View File
@@ -302,7 +302,7 @@ engines:
- name: 360search - name: 360search
engine: 360search engine: 360search
shortcut: 360so shortcut: 360so
timeout: 10.0 timeout: 20.0
disabled: true disabled: true
- name: 360search videos - name: 360search videos
@@ -310,6 +310,12 @@ engines:
shortcut: 360sov shortcut: 360sov
disabled: true disabled: true
- name: 500px
engine: 500px
shortcut: "500"
disabled: true
timeout: 5
- name: 9gag - name: 9gag
engine: 9gag engine: 9gag
shortcut: 9g shortcut: 9g
@@ -469,11 +475,6 @@ engines:
engine: arxiv engine: arxiv
shortcut: arx shortcut: arx
- name: ask
engine: ask
shortcut: ask
disabled: true
- name: azure - name: azure
engine: azure engine: azure
shortcut: az shortcut: az
@@ -530,18 +531,22 @@ engines:
engine: bing engine: bing
shortcut: bi shortcut: bi
disabled: true disabled: true
# base_url: https://cn.bing.com # for instances hosted in China
- name: bing images - name: bing images
engine: bing_images engine: bing_images
shortcut: bii shortcut: bii
# base_url: https://cn.bing.com # for instances hosted in China
- name: bing news - name: bing news
engine: bing_news engine: bing_news
shortcut: bin shortcut: bin
# base_url: https://cn.bing.com # for instances hosted in China
- name: bing videos - name: bing videos
engine: bing_videos engine: bing_videos
shortcut: biv shortcut: biv
# base_url: https://cn.bing.com # for instances hosted in China
- name: bitchute - name: bitchute
engine: bitchute engine: bitchute
@@ -600,6 +605,11 @@ engines:
shortcut: cos shortcut: cos
disabled: true disabled: true
- name: cara
engine: cara
shortcut: ca
disabled: true
- name: chefkoch - name: chefkoch
engine: chefkoch engine: chefkoch
shortcut: chef shortcut: chef
@@ -880,6 +890,11 @@ engines:
shortcut: ftm shortcut: ftm
disabled: true disabled: true
- name: flaticon
engine: flaticon
shortcut: fli
disabled: true
- name: flickr - name: flickr
categories: images categories: images
shortcut: fl shortcut: fl
@@ -1203,6 +1218,7 @@ engines:
categories: [general, web] categories: [general, web]
search_type: web search_type: web
shortcut: ka shortcut: ka
inactive: true
- name: karmasearch images - name: karmasearch images
engine: karmasearch engine: karmasearch
@@ -1210,18 +1226,21 @@ engines:
search_type: images search_type: images
shortcut: kai shortcut: kai
paging: false paging: false
inactive: true
- name: karmasearch videos - name: karmasearch videos
engine: karmasearch engine: karmasearch
categories: [videos, web] categories: [videos, web]
search_type: videos search_type: videos
shortcut: kav shortcut: kav
inactive: true
- name: karmasearch news - name: karmasearch news
engine: karmasearch engine: karmasearch
categories: [news, web] categories: [news, web]
search_type: news search_type: news
shortcut: kan shortcut: kan
inactive: true
- name: kickass - name: kickass
engine: kickass engine: kickass
@@ -1381,7 +1400,6 @@ engines:
engine: metacpan engine: metacpan
shortcut: cpan shortcut: cpan
disabled: true disabled: true
number_of_results: 20
# https://docs.searxng.org/dev/engines/offline/search-indexer-engines.html#module-searx.engines.meilisearch # https://docs.searxng.org/dev/engines/offline/search-indexer-engines.html#module-searx.engines.meilisearch
# - name: meilisearch # - name: meilisearch
@@ -1679,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
@@ -1788,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
@@ -1795,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
@@ -1802,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
@@ -2149,6 +2166,7 @@ engines:
- name: yahoo news - name: yahoo news
engine: yahoo_news engine: yahoo_news
shortcut: yhn shortcut: yhn
inactive: true
- name: youtube - name: youtube
shortcut: yt shortcut: yt
@@ -1 +1 @@
{"version":3,"file":"BnP4vIuG.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/search.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../toolkit.ts\";\nimport { getElement } from \"../util/getElement.ts\";\n\nconst searchForm: HTMLFormElement = getElement<HTMLFormElement>(\"search\");\nconst searchInput: HTMLInputElement = getElement<HTMLInputElement>(\"q\");\nconst searchReset: HTMLButtonElement = getElement<HTMLButtonElement>(\"clear_search\");\n\nconst isMobile: boolean = window.matchMedia(\"(max-width: 50em)\").matches;\nconst isResultsPage: boolean = document.querySelector(\"main\")?.id === \"main_results\";\n\nconst categoryButtons: HTMLButtonElement[] = Array.from(\n document.querySelectorAll<HTMLButtonElement>(\"#categories_container button.category\")\n);\n\nif (searchInput.value.length === 0) {\n searchReset.classList.add(\"empty\");\n}\n\n// focus search input on large screens\nif (!(isMobile || isResultsPage)) {\n searchInput.focus();\n}\n\n// On mobile, move cursor to the end of the input on focus\nif (isMobile) {\n listen(\"focus\", searchInput, () => {\n // Defer cursor move until the next frame to prevent a visual jump\n requestAnimationFrame(() => {\n const end = searchInput.value.length;\n searchInput.setSelectionRange(end, end);\n searchInput.scrollLeft = searchInput.scrollWidth;\n });\n });\n}\n\nlisten(\"input\", searchInput, () => {\n searchReset.classList.toggle(\"empty\", searchInput.value.length === 0);\n});\n\nlisten(\"click\", searchReset, (event: MouseEvent) => {\n event.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n searchReset.classList.add(\"empty\");\n});\n\nfor (const button of categoryButtons) {\n listen(\"click\", button, (event: MouseEvent) => {\n if (event.shiftKey) {\n event.preventDefault();\n button.classList.toggle(\"selected\");\n return;\n }\n\n // deselect all other categories\n for (const categoryButton of categoryButtons) {\n categoryButton.classList.toggle(\"selected\", categoryButton === button);\n }\n });\n}\n\nif (document.querySelector(\"div.search_filters\")) {\n const safesearchElement = document.getElementById(\"safesearch\");\n if (safesearchElement) {\n listen(\"change\", safesearchElement, () => searchForm.submit());\n }\n\n const timeRangeElement = document.getElementById(\"time_range\");\n if (timeRangeElement) {\n listen(\"change\", timeRangeElement, () => searchForm.submit());\n }\n\n const languageElement = document.getElementById(\"language\");\n if (languageElement) {\n listen(\"change\", languageElement, () => searchForm.submit());\n }\n}\n\n// override searchForm submit event\nlisten(\"submit\", searchForm, (event: Event) => {\n event.preventDefault();\n\n if (categoryButtons.length > 0) {\n const searchCategories = getElement<HTMLInputElement>(\"selected-categories\");\n searchCategories.value = categoryButtons\n .filter((button) => button.classList.contains(\"selected\"))\n .map((button) => button.name.replace(\"category_\", \"\"))\n .join(\",\");\n }\n\n searchForm.submit();\n});\n"],"mappings":"yEAKA,IAAM,EAA8B,EAA4B,SAAS,CACnE,EAAgC,EAA6B,IAAI,CACjE,EAAiC,EAA8B,eAAe,CAE9E,EAAoB,OAAO,WAAW,oBAAoB,CAAC,QAC3D,EAAyB,SAAS,cAAc,OAAO,EAAE,KAAO,eAEhE,EAAuC,MAAM,KACjD,SAAS,iBAAoC,wCAAwC,CACtF,CAEG,EAAY,MAAM,SAAW,GAC/B,EAAY,UAAU,IAAI,QAAQ,CAI9B,GAAY,GAChB,EAAY,OAAO,CAIjB,GACF,EAAO,QAAS,MAAmB,CAEjC,0BAA4B,CAC1B,IAAM,EAAM,EAAY,MAAM,OAC9B,EAAY,kBAAkB,EAAK,EAAI,CACvC,EAAY,WAAa,EAAY,aACrC,EACF,CAGJ,EAAO,QAAS,MAAmB,CACjC,EAAY,UAAU,OAAO,QAAS,EAAY,MAAM,SAAW,EAAE,EACrE,CAEF,EAAO,QAAS,EAAc,GAAsB,CAClD,EAAM,gBAAgB,CACtB,EAAY,MAAQ,GACpB,EAAY,OAAO,CACnB,EAAY,UAAU,IAAI,QAAQ,EAClC,CAEF,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,EAAS,GAAsB,CAC7C,GAAI,EAAM,SAAU,CAClB,EAAM,gBAAgB,CACtB,EAAO,UAAU,OAAO,WAAW,CACnC,OAIF,IAAK,IAAM,KAAkB,EAC3B,EAAe,UAAU,OAAO,WAAY,IAAmB,EAAO,EAExE,CAGJ,GAAI,SAAS,cAAc,qBAAqB,CAAE,CAChD,IAAM,EAAoB,SAAS,eAAe,aAAa,CAC3D,GACF,EAAO,SAAU,MAAyB,EAAW,QAAQ,CAAC,CAGhE,IAAM,EAAmB,SAAS,eAAe,aAAa,CAC1D,GACF,EAAO,SAAU,MAAwB,EAAW,QAAQ,CAAC,CAG/D,IAAM,EAAkB,SAAS,eAAe,WAAW,CACvD,GACF,EAAO,SAAU,MAAuB,EAAW,QAAQ,CAAC,CAKhE,EAAO,SAAU,EAAa,GAAiB,CAG7C,GAFA,EAAM,gBAAgB,CAElB,EAAgB,OAAS,EAAG,CAC9B,IAAM,EAAmB,EAA6B,sBAAsB,CAC5E,EAAiB,MAAQ,EACtB,OAAQ,GAAW,EAAO,UAAU,SAAS,WAAW,CAAC,CACzD,IAAK,GAAW,EAAO,KAAK,QAAQ,YAAa,GAAG,CAAC,CACrD,KAAK,IAAI,CAGd,EAAW,QAAQ,EACnB"} {"version":3,"file":"BnP4vIuG.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/search.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../toolkit.ts\";\nimport { getElement } from \"../util/getElement.ts\";\n\nconst searchForm: HTMLFormElement = getElement<HTMLFormElement>(\"search\");\nconst searchInput: HTMLInputElement = getElement<HTMLInputElement>(\"q\");\nconst searchReset: HTMLButtonElement = getElement<HTMLButtonElement>(\"clear_search\");\n\nconst isMobile: boolean = window.matchMedia(\"(max-width: 50em)\").matches;\nconst isResultsPage: boolean = document.querySelector(\"main\")?.id === \"main_results\";\n\nconst categoryButtons: HTMLButtonElement[] = Array.from(\n document.querySelectorAll<HTMLButtonElement>(\"#categories_container button.category\")\n);\n\nif (searchInput.value.length === 0) {\n searchReset.classList.add(\"empty\");\n}\n\n// focus search input on large screens\nif (!(isMobile || isResultsPage)) {\n searchInput.focus();\n}\n\n// On mobile, move cursor to the end of the input on focus\nif (isMobile) {\n listen(\"focus\", searchInput, () => {\n // Defer cursor move until the next frame to prevent a visual jump\n requestAnimationFrame(() => {\n const end = searchInput.value.length;\n searchInput.setSelectionRange(end, end);\n searchInput.scrollLeft = searchInput.scrollWidth;\n });\n });\n}\n\nlisten(\"input\", searchInput, () => {\n searchReset.classList.toggle(\"empty\", searchInput.value.length === 0);\n});\n\nlisten(\"click\", searchReset, (event: MouseEvent) => {\n event.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n searchReset.classList.add(\"empty\");\n});\n\nfor (const button of categoryButtons) {\n listen(\"click\", button, (event: MouseEvent) => {\n if (event.shiftKey) {\n event.preventDefault();\n button.classList.toggle(\"selected\");\n return;\n }\n\n // deselect all other categories\n for (const categoryButton of categoryButtons) {\n categoryButton.classList.toggle(\"selected\", categoryButton === button);\n }\n });\n}\n\nif (document.querySelector(\"div.search_filters\")) {\n const safesearchElement = document.getElementById(\"safesearch\");\n if (safesearchElement) {\n listen(\"change\", safesearchElement, () => searchForm.submit());\n }\n\n const timeRangeElement = document.getElementById(\"time_range\");\n if (timeRangeElement) {\n listen(\"change\", timeRangeElement, () => searchForm.submit());\n }\n\n const languageElement = document.getElementById(\"language\");\n if (languageElement) {\n listen(\"change\", languageElement, () => searchForm.submit());\n }\n}\n\n// override searchForm submit event\nlisten(\"submit\", searchForm, (event: Event) => {\n event.preventDefault();\n\n if (categoryButtons.length > 0) {\n const searchCategories = getElement<HTMLInputElement>(\"selected-categories\");\n searchCategories.value = categoryButtons\n .filter((button) => button.classList.contains(\"selected\"))\n .map((button) => button.name.replace(\"category_\", \"\"))\n .join(\",\");\n }\n\n searchForm.submit();\n});\n"],"mappings":"yEAKA,IAAM,EAA8B,EAA4B,QAAQ,EAClE,EAAgC,EAA6B,GAAG,EAChE,EAAiC,EAA8B,cAAc,EAE7E,EAAoB,OAAO,WAAW,mBAAmB,EAAE,QAC3D,EAAyB,SAAS,cAAc,MAAM,GAAG,KAAO,eAEhE,EAAuC,MAAM,KACjD,SAAS,iBAAoC,uCAAuC,CACtF,EAEI,EAAY,MAAM,SAAW,GAC/B,EAAY,UAAU,IAAI,OAAO,EAI7B,GAAY,GAChB,EAAY,MAAM,EAIhB,GACF,EAAO,QAAS,MAAmB,CAEjC,0BAA4B,CAC1B,IAAM,EAAM,EAAY,MAAM,OAC9B,EAAY,kBAAkB,EAAK,CAAG,EACtC,EAAY,WAAa,EAAY,WACvC,CAAC,CACH,CAAC,EAGH,EAAO,QAAS,MAAmB,CACjC,EAAY,UAAU,OAAO,QAAS,EAAY,MAAM,SAAW,CAAC,CACtE,CAAC,EAED,EAAO,QAAS,EAAc,GAAsB,CAClD,EAAM,eAAe,EACrB,EAAY,MAAQ,GACpB,EAAY,MAAM,EAClB,EAAY,UAAU,IAAI,OAAO,CACnC,CAAC,EAED,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,EAAS,GAAsB,CAC7C,GAAI,EAAM,SAAU,CAClB,EAAM,eAAe,EACrB,EAAO,UAAU,OAAO,UAAU,EAClC,MACF,CAGA,IAAK,IAAM,KAAkB,EAC3B,EAAe,UAAU,OAAO,WAAY,IAAmB,CAAM,CAEzE,CAAC,EAGH,GAAI,SAAS,cAAc,oBAAoB,EAAG,CAChD,IAAM,EAAoB,SAAS,eAAe,YAAY,EAC1D,GACF,EAAO,SAAU,MAAyB,EAAW,OAAO,CAAC,EAG/D,IAAM,EAAmB,SAAS,eAAe,YAAY,EACzD,GACF,EAAO,SAAU,MAAwB,EAAW,OAAO,CAAC,EAG9D,IAAM,EAAkB,SAAS,eAAe,UAAU,EACtD,GACF,EAAO,SAAU,MAAuB,EAAW,OAAO,CAAC,CAE/D,CAGA,EAAO,SAAU,EAAa,GAAiB,CAG7C,GAFA,EAAM,eAAe,EAEjB,EAAgB,OAAS,EAAG,CAC9B,IAAM,EAAmB,EAA6B,qBAAqB,EAC3E,EAAiB,MAAQ,EACtB,OAAQ,GAAW,EAAO,UAAU,SAAS,UAAU,CAAC,EACxD,IAAK,GAAW,EAAO,KAAK,QAAQ,YAAa,EAAE,CAAC,EACpD,KAAK,GAAG,CACb,CAEA,EAAW,OAAO,CACpB,CAAC"}
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
@@ -1 +1 @@
{"version":3,"file":"DH1EQbEY.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/assertElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\ntype AssertElement = <T>(element?: T | null) => asserts element is T;\nexport const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {\n if (!element) {\n throw new Error(\"DOM element not found\");\n }\n};\n"],"mappings":"AAGA,IAAa,EAAmC,GAA6C,CAC3F,GAAI,CAAC,EACH,MAAU,MAAM,wBAAwB"} {"version":3,"file":"DH1EQbEY.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/assertElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\ntype AssertElement = <T>(element?: T | null) => asserts element is T;\nexport const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {\n if (!element) {\n throw new Error(\"DOM element not found\");\n }\n};\n"],"mappings":"AAGA,IAAa,EAAmC,GAA6C,CAC3F,GAAI,CAAC,EACH,MAAU,MAAM,uBAAuB,CAE3C"}
@@ -1 +1 @@
{"version":3,"file":"DZidprJh.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../toolkit.ts\";\nimport { assertElement } from \"../util/assertElement.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n if (this.dataset.copiedText) {\n this.innerText = this.dataset.copiedText;\n }\n});\n"],"mappings":"4FAKA,IAAI,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MAAM,MADT,EAAK,MAAO,2BAA2B,EAC1B,MAAM,OAC9B,EAAO,CACd,QAAQ,MAAM,qCAAsC,EAAM,CAEvD,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,EAAmB,CAAE,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,wBAAwB,CAC5G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,KAKlC,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,IAKxB,EAA0C,SAAS,iBAA8B,qBAAqB,CAC5G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,EAAuB,CAG7D,IAAM,EAA8C,SAAS,iBAC3D,oDACD,CAEK,EAA4C,SAAS,iBAA8B,sBAAsB,CAC/G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,EAAc,CAAC,CAGnE,IAAM,EAA6C,SAAS,iBAA8B,uBAAuB,CACjH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,EAAc,CAAC,CAGpE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,MAAM,CAGvE,GAFA,EAAc,EAAO,CAEjB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,UAAU,KAChD,CACL,IAAM,EAAY,OAAO,cAAc,CACvC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,aAAa,CACpC,EAAM,mBAAmB,EAAO,CAChC,EAAU,iBAAiB,CAC3B,EAAU,SAAS,EAAM,CACzB,SAAS,YAAY,OAAO,EAI5B,KAAK,QAAQ,aACf,KAAK,UAAY,KAAK,QAAQ,aAEhC"} {"version":3,"file":"DZidprJh.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../toolkit.ts\";\nimport { assertElement } from \"../util/assertElement.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n if (this.dataset.copiedText) {\n this.innerText = this.dataset.copiedText;\n }\n});\n"],"mappings":"4FAKA,IAAI,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MAAM,MADT,EAAK,MAAO,0BAA0B,GACzB,KAAK,CACtC,OAAS,EAAO,CACd,QAAQ,MAAM,qCAAsC,CAAK,CAC3D,CACK,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,CAAkB,EAAG,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,uBAAuB,EAC3G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,CAEtC,CAVA,CAWF,EAEM,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,EAG9B,EAEM,EAA0C,SAAS,iBAA8B,oBAAoB,EAC3G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,CAAsB,EAG5D,IAAM,EAA8C,SAAS,iBAC3D,mDACF,EAEM,EAA4C,SAAS,iBAA8B,qBAAqB,EAC9G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,CAAa,CAAC,EAGlE,IAAM,EAA6C,SAAS,iBAA8B,sBAAsB,EAChH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,CAAa,CAAC,EAGnE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,KAAK,EAGtE,GAFA,EAAc,CAAM,EAEhB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,SAAS,MAC/C,CACL,IAAM,EAAY,OAAO,aAAa,EACtC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,YAAY,EACnC,EAAM,mBAAmB,CAAM,EAC/B,EAAU,gBAAgB,EAC1B,EAAU,SAAS,CAAK,EACxB,SAAS,YAAY,MAAM,CAC7B,CACF,CAEI,KAAK,QAAQ,aACf,KAAK,UAAY,KAAK,QAAQ,WAElC,CAAC"}
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
@@ -1 +1 @@
{"version":3,"file":"chlzpS6K.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/getElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement } from \"./assertElement.ts\";\n\ntype Options = {\n assert?: boolean;\n};\n\nexport function getElement<T>(id: string, options?: { assert: true }): T;\nexport function getElement<T>(id: string, options?: { assert: false }): T | null;\nexport function getElement<T>(id: string, options: Options = {}): T | null {\n options.assert ??= true;\n\n const element = document.getElementById(id) as T | null;\n\n if (options.assert) {\n assertElement(element);\n }\n\n return element;\n}\n"],"mappings":"sCAUA,SAAgB,EAAc,EAAY,EAAmB,EAAE,CAAY,CACzE,EAAQ,SAAW,GAEnB,IAAM,EAAU,SAAS,eAAe,EAAG,CAM3C,OAJI,EAAQ,QACV,EAAc,EAAQ,CAGjB"} {"version":3,"file":"chlzpS6K.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/getElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement } from \"./assertElement.ts\";\n\ntype Options = {\n assert?: boolean;\n};\n\nexport function getElement<T>(id: string, options?: { assert: true }): T;\nexport function getElement<T>(id: string, options?: { assert: false }): T | null;\nexport function getElement<T>(id: string, options: Options = {}): T | null {\n options.assert ??= true;\n\n const element = document.getElementById(id) as T | null;\n\n if (options.assert) {\n assertElement(element);\n }\n\n return element;\n}\n"],"mappings":"sCAUA,SAAgB,EAAc,EAAY,EAAmB,CAAC,EAAa,CACzE,EAAQ,SAAW,GAEnB,IAAM,EAAU,SAAS,eAAe,CAAE,EAM1C,OAJI,EAAQ,QACV,EAAc,CAAO,EAGhB,CACT"}
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
@@ -7,9 +7,7 @@
<title>SearXNG search: {{ q|e }}</title> <title>SearXNG search: {{ q|e }}</title>
<link>{{ url_for('search', _external=True) }}?q={{ q|e }}</link> <link>{{ url_for('search', _external=True) }}?q={{ q|e }}</link>
<description>Search results for "{{ q|e }}" - SearXNG</description> <description>Search results for "{{ q|e }}" - SearXNG</description>
<opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
<opensearch:startIndex>1</opensearch:startIndex> <opensearch:startIndex>1</opensearch:startIndex>
<opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
<atom:link rel="search" type="application/opensearchdescription+xml" href="{{ opensearch_url }}"/> <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ opensearch_url }}"/>
<opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" /> <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
{% if error_message %} {% if error_message %}
@@ -23,7 +23,6 @@
<th class="checkbox-col">{{- _("Allow") -}}</th>{{- '' -}} <th class="checkbox-col">{{- _("Allow") -}}</th>{{- '' -}}
<th class="name">{{- _("Engine name") -}}</th>{{- '' -}} <th class="name">{{- _("Engine name") -}}</th>{{- '' -}}
<th class="shortcut">{{ _("!bang") -}}</th>{{- '' -}} <th class="shortcut">{{ _("!bang") -}}</th>{{- '' -}}
<th>{{- _("Supports selected language") -}}</th>{{- '' -}}
<th>{{- _("SafeSearch") -}}</th>{{- '' -}} <th>{{- _("SafeSearch") -}}</th>{{- '' -}}
<th>{{- _("Time range") -}}</th>{{- '' -}} <th>{{- _("Time range") -}}</th>{{- '' -}}
<th>{{- _("Weight") }}</th> <th>{{- _("Weight") }}</th>
@@ -70,9 +69,6 @@
<td class="shortcut">{{- '' -}} <td class="shortcut">{{- '' -}}
<span class="bang">{{ '!' + shortcuts[search_engine.name] }}</span>{{- '' -}} <span class="bang">{{ '!' + shortcuts[search_engine.name] }}</span>{{- '' -}}
</td>{{- '' -}} </td>{{- '' -}}
<td>
{{- checkbox(None, supports[search_engine.name]['supports_selected_language'], true) -}}
</td>{{- '' -}}
<td> <td>
{{- checkbox(None, supports[search_engine.name]['safesearch'], true) -}} {{- checkbox(None, supports[search_engine.name]['safesearch'], true) -}}
</td>{{- '' -}} </td>{{- '' -}}
-4
View File
@@ -26,10 +26,6 @@
<div id="sidebar"> <div id="sidebar">
{%- if number_of_results != '0' -%}
<p id="result_count"><small>{{ _('Number of results') }}: {{ number_of_results }}</small></p>
{%- endif -%}
{%- if infoboxes -%} {%- if infoboxes -%}
<div id="infoboxes"> <div id="infoboxes">
<details open class="sidebar-collapsible"> <details open class="sidebar-collapsible">
Binary file not shown.
+42 -42
View File
@@ -16,16 +16,16 @@
# jestie <jestie@users.noreply.translate.codeberg.org>, 2025. # jestie <jestie@users.noreply.translate.codeberg.org>, 2025.
# APoniatowski <aponiatowski@users.noreply.translate.codeberg.org>, 2025. # APoniatowski <aponiatowski@users.noreply.translate.codeberg.org>, 2025.
# French <french@noreply.codeberg.org>, 2025. # French <french@noreply.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025. # return42 <return42@noreply.codeberg.org>, 2025, 2026.
# Raithlin <raithlin@noreply.codeberg.org>, 2025. # Raithlin <raithlin@noreply.codeberg.org>, 2025.
# grumpyoldtechie <grumpyoldtechie@noreply.codeberg.org>, 2025. # grumpyoldtechie <grumpyoldtechie@noreply.codeberg.org>, 2025, 2026.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-03-24 18:43+0000\n" "POT-Creation-Date: 2026-05-25 10:44+0000\n"
"PO-Revision-Date: 2025-10-22 02:09+0000\n" "PO-Revision-Date: 2026-05-25 10:44+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n" "Last-Translator: grumpyoldtechie <grumpyoldtechie@noreply.codeberg.org>\n"
"Language: af\n" "Language: af\n"
"Language-Team: Afrikaans " "Language-Team: Afrikaans "
"<https://translate.codeberg.org/projects/searxng/searxng/af/>\n" "<https://translate.codeberg.org/projects/searxng/searxng/af/>\n"
@@ -181,7 +181,7 @@ msgid "Uptime"
msgstr "bedryfstyd" msgstr "bedryfstyd"
#. BRAND_CUSTOM_LINKS['ABOUT'] #. BRAND_CUSTOM_LINKS['ABOUT']
#: searx/searxng.msg searx/templates/simple/base.html:46 #: searx/searxng.msg searx/templates/simple/base.html:47
msgid "About" msgid "About"
msgstr "Aangaande" msgstr "Aangaande"
@@ -546,28 +546,28 @@ msgstr "toe"
msgid "answered" msgid "answered"
msgstr "geantwoord" msgstr "geantwoord"
#: searx/webapp.py:327 #: searx/webapp.py:326
msgid "No item found" msgid "No item found"
msgstr "Geen item gevind" msgstr "Geen item gevind"
#: searx/engines/qwant.py:291 #: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:329 #: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:328
msgid "Source" msgid "Source"
msgstr "Bron" msgstr "Bron"
#: searx/webapp.py:331 #: searx/webapp.py:330
msgid "Error loading the next page" msgid "Error loading the next page"
msgstr "Fout met die laai van die volgende bladsy" msgstr "Fout met die laai van die volgende bladsy"
#: searx/webapp.py:480 searx/webapp.py:881 #: searx/webapp.py:479 searx/webapp.py:877
msgid "Invalid settings, please edit your preferences" msgid "Invalid settings, please edit your preferences"
msgstr "Ongeldige opstellings, redigeer asb jou voorkeure" msgstr "Ongeldige opstellings, redigeer asb jou voorkeure"
#: searx/webapp.py:496 #: searx/webapp.py:495
msgid "Invalid settings" msgid "Invalid settings"
msgstr "Ongeldige opstellings" msgstr "Ongeldige opstellings"
#: searx/webapp.py:573 searx/webapp.py:663 #: searx/webapp.py:571 searx/webapp.py:661
msgid "search error" msgid "search error"
msgstr "soekfout" msgstr "soekfout"
@@ -627,12 +627,12 @@ msgstr "bediener API fout"
msgid "Suspended" msgid "Suspended"
msgstr "Opgehef" msgstr "Opgehef"
#: searx/webutils.py:308 #: searx/webutils.py:307
#, python-brace-format #, python-brace-format
msgid "{minutes} minute(s) ago" msgid "{minutes} minute(s) ago"
msgstr "{minutes} minute gelede" msgstr "{minutes} minute gelede"
#: searx/webutils.py:309 #: searx/webutils.py:308
#, python-brace-format #, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago" msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} ure, {minutes} minute gelede" msgstr "{hours} ure, {minutes} minute gelede"
@@ -775,6 +775,8 @@ msgid ""
"Automatically loads the next page when scrolling to bottom of the current" "Automatically loads the next page when scrolling to bottom of the current"
" page" " page"
msgstr "" msgstr ""
"Laai die volgende bladsy outomaties wanneer na die onderkant van die "
"huidige bladsy geblaai word"
#: searx/plugins/oa_doi_rewrite.py:54 #: searx/plugins/oa_doi_rewrite.py:54
msgid "Open Access DOI rewrite" msgid "Open Access DOI rewrite"
@@ -875,45 +877,45 @@ msgstr "Gaan na %(search_page)s."
msgid "search page" msgid "search page"
msgstr "soekblad" msgstr "soekblad"
#: searx/templates/simple/base.html:50 #: searx/templates/simple/base.html:51
msgid "Donate" msgid "Donate"
msgstr "Skenk" msgstr "Skenk"
#: searx/templates/simple/base.html:55 searx/templates/simple/base.html:57 #: searx/templates/simple/base.html:56 searx/templates/simple/base.html:58
#: searx/templates/simple/preferences.html:152 #: searx/templates/simple/preferences.html:152
msgid "Preferences" msgid "Preferences"
msgstr "Voorkeure" msgstr "Voorkeure"
#: searx/templates/simple/base.html:68 #: searx/templates/simple/base.html:69
msgid "Powered by" msgid "Powered by"
msgstr "Aangedryf deur" msgstr "Aangedryf deur"
#: searx/templates/simple/base.html:68 #: searx/templates/simple/base.html:69
msgid "a privacy-respecting, open metasearch engine" msgid "a privacy-respecting, open metasearch engine"
msgstr "'n oop metasoekenjin wat privaatheid respekteer" msgstr "'n oop metasoekenjin wat privaatheid respekteer"
#: searx/templates/simple/base.html:69 #: searx/templates/simple/base.html:70
#: searx/templates/simple/result_templates/packages.html:59 #: searx/templates/simple/result_templates/packages.html:59
msgid "Source code" msgid "Source code"
msgstr "Bronkode" msgstr "Bronkode"
#: searx/templates/simple/base.html:70 #: searx/templates/simple/base.html:71
msgid "Issue tracker" msgid "Issue tracker"
msgstr "Probleem soeker" msgstr "Probleem soeker"
#: searx/templates/simple/base.html:71 searx/templates/simple/stats.html:17 #: searx/templates/simple/base.html:72 searx/templates/simple/stats.html:17
msgid "Engine stats" msgid "Engine stats"
msgstr "Enjin statistieke" msgstr "Enjin statistieke"
#: searx/templates/simple/base.html:73 #: searx/templates/simple/base.html:74
msgid "Public instances" msgid "Public instances"
msgstr "Openbare instansies" msgstr "Openbare instansies"
#: searx/templates/simple/base.html:76 #: searx/templates/simple/base.html:77
msgid "Privacy policy" msgid "Privacy policy"
msgstr "Privaatheidsbeleid" msgstr "Privaatheidsbeleid"
#: searx/templates/simple/base.html:79 #: searx/templates/simple/base.html:80
msgid "Contact instance maintainer" msgid "Contact instance maintainer"
msgstr "Kontak instansie onderhouer" msgstr "Kontak instansie onderhouer"
@@ -1026,23 +1028,19 @@ msgstr "Spesiale Navrae"
msgid "Cookies" msgid "Cookies"
msgstr "Koekies" msgstr "Koekies"
#: searx/templates/simple/results.html:30 #: searx/templates/simple/results.html:32
msgid "Number of results"
msgstr "Aantal resultate"
#: searx/templates/simple/results.html:36
msgid "Info" msgid "Info"
msgstr "Info" msgstr "Info"
#: searx/templates/simple/results.html:77 #: searx/templates/simple/results.html:73
msgid "Back to top" msgid "Back to top"
msgstr "Terug na bo" msgstr "Terug na bo"
#: searx/templates/simple/results.html:95 #: searx/templates/simple/results.html:91
msgid "Previous page" msgid "Previous page"
msgstr "Vorige bladsy" msgstr "Vorige bladsy"
#: searx/templates/simple/results.html:113 #: searx/templates/simple/results.html:109
msgid "Next page" msgid "Next page"
msgstr "Volgende bladsy" msgstr "Volgende bladsy"
@@ -1083,12 +1081,12 @@ msgid "Result count"
msgstr "Resultaattelling" msgstr "Resultaattelling"
#: searx/templates/simple/elements/engines_msg.html:7 #: searx/templates/simple/elements/engines_msg.html:7
#: searx/templates/simple/preferences/engines.html:31 #: searx/templates/simple/preferences/engines.html:30
#: searx/templates/simple/stats.html:27 #: searx/templates/simple/stats.html:27
msgid "Response time" msgid "Response time"
msgstr "Reaksietyd" msgstr "Reaksietyd"
#: searx/templates/simple/preferences/engines.html:35 #: searx/templates/simple/preferences/engines.html:34
#: searx/templates/simple/stats.html:28 #: searx/templates/simple/stats.html:28
msgid "Reliability" msgid "Reliability"
msgstr "Betroubaarheid" msgstr "Betroubaarheid"
@@ -1217,7 +1215,7 @@ msgstr "Outo-bespeur"
#: searx/templates/simple/filters/safesearch.html:2 #: searx/templates/simple/filters/safesearch.html:2
#: searx/templates/simple/filters/safesearch.html:3 #: searx/templates/simple/filters/safesearch.html:3
#: searx/templates/simple/filters/safesearch.html:4 #: searx/templates/simple/filters/safesearch.html:4
#: searx/templates/simple/preferences/engines.html:27 #: searx/templates/simple/preferences/engines.html:26
#: searx/templates/simple/preferences/safesearch.html:2 #: searx/templates/simple/preferences/safesearch.html:2
msgid "SafeSearch" msgid "SafeSearch"
msgstr "VeiligeSoek" msgstr "VeiligeSoek"
@@ -1238,7 +1236,7 @@ msgid "None"
msgstr "Geen" msgstr "Geen"
#: searx/templates/simple/filters/time_range.html:1 #: searx/templates/simple/filters/time_range.html:1
#: searx/templates/simple/preferences/engines.html:28 #: searx/templates/simple/preferences/engines.html:27
msgid "Time range" msgid "Time range"
msgstr "Tydreeks" msgstr "Tydreeks"
@@ -1436,15 +1434,11 @@ msgstr "Deaktiveer alles"
msgid "!bang" msgid "!bang"
msgstr "!bang" msgstr "!bang"
#: searx/templates/simple/preferences/engines.html:26 #: searx/templates/simple/preferences/engines.html:28
msgid "Supports selected language"
msgstr "Ondersteun gekose taal"
#: searx/templates/simple/preferences/engines.html:29
msgid "Weight" msgid "Weight"
msgstr "Gewig" msgstr "Gewig"
#: searx/templates/simple/preferences/engines.html:33 #: searx/templates/simple/preferences/engines.html:32
msgid "Max time" msgid "Max time"
msgstr "Maks tyd" msgstr "Maks tyd"
@@ -2159,3 +2153,9 @@ msgstr "versteek video"
#~ msgid "Submit a new issue on Github including the above information" #~ msgid "Submit a new issue on Github including the above information"
#~ msgstr "Dien 'n nuwe probleem in op GitHub insluitend die bogenoemde inligting" #~ msgstr "Dien 'n nuwe probleem in op GitHub insluitend die bogenoemde inligting"
#~ msgid "Supports selected language"
#~ msgstr "Ondersteun gekose taal"
#~ msgid "Number of results"
#~ msgstr "Aantal resultate"
Binary file not shown.
+124 -121
View File
@@ -24,30 +24,32 @@
# Rick1029 <rick1029@users.noreply.translate.codeberg.org>, 2025. # Rick1029 <rick1029@users.noreply.translate.codeberg.org>, 2025.
# Yahya-Lando <yahya-lando@users.noreply.translate.codeberg.org>, 2025. # Yahya-Lando <yahya-lando@users.noreply.translate.codeberg.org>, 2025.
# curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025. # curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025. # return42 <return42@noreply.codeberg.org>, 2025, 2026.
# DZDevelopers <dzdevelopers@noreply.codeberg.org>, 2025. # DZDevelopers <dzdevelopers@noreply.codeberg.org>, 2025.
# youtherthyf <youtherthyf@noreply.codeberg.org>, 2025. # youtherthyf <youtherthyf@noreply.codeberg.org>, 2025.
# mustafa-phd <mustafa-phd@noreply.codeberg.org>, 2026.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: searx\n" "Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-03-24 18:43+0000\n" "POT-Creation-Date: 2026-05-25 10:44+0000\n"
"PO-Revision-Date: 2025-10-22 02:09+0000\n" "PO-Revision-Date: 2026-05-27 12:07+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n" "Last-Translator: mustafa-phd <mustafa-phd@noreply.codeberg.org>\n"
"Language-Team: Arabic <https://translate.codeberg.org/projects/searxng/"
"searxng/ar/>\n"
"Language: ar\n" "Language: ar\n"
"Language-Team: Arabic "
"<https://translate.codeberg.org/projects/searxng/searxng/ar/>\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : "
"n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 2026.5\n"
"Generated-By: Babel 2.18.0\n" "Generated-By: Babel 2.18.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING'] #. CONSTANT_NAMES['NO_SUBGROUPING']
#: searx/searxng.msg #: searx/searxng.msg
msgid "without further subgrouping" msgid "without further subgrouping"
msgstr "بدون تقسيم" msgstr "بدون تقسيم إضافي"
#. CONSTANT_NAMES['DEFAULT_CATEGORY'] #. CONSTANT_NAMES['DEFAULT_CATEGORY']
#: searx/searxng.msg #: searx/searxng.msg
@@ -72,7 +74,7 @@ msgstr "موسيقى"
#. CATEGORY_NAMES['SOCIAL_MEDIA'] #. CATEGORY_NAMES['SOCIAL_MEDIA']
#: searx/searxng.msg #: searx/searxng.msg
msgid "social media" msgid "social media"
msgstr "شبكات التواصل الإجتماعي" msgstr "التواصل الإجتماعي"
#. CATEGORY_NAMES['IMAGES'] #. CATEGORY_NAMES['IMAGES']
#: searx/searxng.msg #: searx/searxng.msg
@@ -87,7 +89,7 @@ msgstr "ڤيديوهات"
#. CATEGORY_NAMES['RADIO'] #. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:146 searx/searxng.msg #: searx/engines/radio_browser.py:146 searx/searxng.msg
msgid "radio" msgid "radio"
msgstr "راديو" msgstr "إذاعة"
#. CATEGORY_NAMES['TV'] #. CATEGORY_NAMES['TV']
#: searx/searxng.msg #: searx/searxng.msg
@@ -97,7 +99,7 @@ msgstr "تلفاز"
#. CATEGORY_NAMES['IT'] #. CATEGORY_NAMES['IT']
#: searx/searxng.msg #: searx/searxng.msg
msgid "it" msgid "it"
msgstr "علوم التكنولوجيا" msgstr "علوم التقنية"
#. CATEGORY_NAMES['NEWS'] #. CATEGORY_NAMES['NEWS']
#: searx/searxng.msg #: searx/searxng.msg
@@ -112,7 +114,7 @@ msgstr "خريطة"
#. CATEGORY_NAMES['ONIONS'] #. CATEGORY_NAMES['ONIONS']
#: searx/searxng.msg #: searx/searxng.msg
msgid "onions" msgid "onions"
msgstr "برمجيات البصلة" msgstr "أونيون"
#. CATEGORY_NAMES['SCIENCE'] #. CATEGORY_NAMES['SCIENCE']
#: searx/searxng.msg #: searx/searxng.msg
@@ -152,12 +154,12 @@ msgstr "مستودعات"
#. CATEGORY_GROUPS['SOFTWARE_WIKIS'] #. CATEGORY_GROUPS['SOFTWARE_WIKIS']
#: searx/searxng.msg #: searx/searxng.msg
msgid "software wikis" msgid "software wikis"
msgstr "الموسوعات التشاركية للبرنامج" msgstr "موسوعات البرمجية"
#. CATEGORY_GROUPS['WEB'] #. CATEGORY_GROUPS['WEB']
#: searx/searxng.msg #: searx/searxng.msg
msgid "web" msgid "web"
msgstr "الشبكة العالمية" msgstr "الويب"
#. CATEGORY_GROUPS['SCIENTIFIC PUBLICATIONS'] #. CATEGORY_GROUPS['SCIENTIFIC PUBLICATIONS']
#: searx/searxng.msg #: searx/searxng.msg
@@ -190,19 +192,19 @@ msgid "Uptime"
msgstr "فترة التشغيل" msgstr "فترة التشغيل"
#. BRAND_CUSTOM_LINKS['ABOUT'] #. BRAND_CUSTOM_LINKS['ABOUT']
#: searx/searxng.msg searx/templates/simple/base.html:46 #: searx/searxng.msg searx/templates/simple/base.html:47
msgid "About" msgid "About"
msgstr "حَول" msgstr "حول"
#. WEATHER_TERMS['AVERAGE TEMP.'] #. WEATHER_TERMS['AVERAGE TEMP.']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Average temp." msgid "Average temp."
msgstr "متوسط الحرارة" msgstr "متوسط درجة الحرارة."
#. WEATHER_TERMS['CLOUD COVER'] #. WEATHER_TERMS['CLOUD COVER']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Cloud cover" msgid "Cloud cover"
msgstr "حالة الطقس" msgstr "نسبة الغيوم"
#. WEATHER_TERMS['CONDITION'] #. WEATHER_TERMS['CONDITION']
#: searx/searxng.msg #: searx/searxng.msg
@@ -212,7 +214,7 @@ msgstr "غائم"
#. WEATHER_TERMS['CURRENT CONDITION'] #. WEATHER_TERMS['CURRENT CONDITION']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Current condition" msgid "Current condition"
msgstr "الحالة الحالية" msgstr "حالة الطقس"
#. WEATHER_TERMS['EVENING'] #. WEATHER_TERMS['EVENING']
#: searx/searxng.msg #: searx/searxng.msg
@@ -232,12 +234,12 @@ msgstr "رطوبة"
#. WEATHER_TERMS['MAX TEMP.'] #. WEATHER_TERMS['MAX TEMP.']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Max temp." msgid "Max temp."
msgstr "الحرارة العظمى" msgstr "أقصى درجة حرارة."
#. WEATHER_TERMS['MIN TEMP.'] #. WEATHER_TERMS['MIN TEMP.']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Min temp." msgid "Min temp."
msgstr "الحرارة الدنيا" msgstr "أدنى درجة حرارة."
#. WEATHER_TERMS['MORNING'] #. WEATHER_TERMS['MORNING']
#: searx/searxng.msg #: searx/searxng.msg
@@ -247,12 +249,12 @@ msgstr "صباحا"
#. WEATHER_TERMS['NIGHT'] #. WEATHER_TERMS['NIGHT']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Night" msgid "Night"
msgstr "ليلا" msgstr "ليلاً"
#. WEATHER_TERMS['NOON'] #. WEATHER_TERMS['NOON']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Noon" msgid "Noon"
msgstr "ظهيرة" msgstr "ظهرا"
#. WEATHER_TERMS['PRESSURE'] #. WEATHER_TERMS['PRESSURE']
#: searx/searxng.msg searx/templates/simple/answer/weather.html:25 #: searx/searxng.msg searx/templates/simple/answer/weather.html:25
@@ -262,17 +264,17 @@ msgstr "الضغط"
#. WEATHER_TERMS['SUNRISE'] #. WEATHER_TERMS['SUNRISE']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sunrise" msgid "Sunrise"
msgstr "الشروق" msgstr "شروق"
#. WEATHER_TERMS['SUNSET'] #. WEATHER_TERMS['SUNSET']
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sunset" msgid "Sunset"
msgstr "الغروب" msgstr "غروب"
#. WEATHER_TERMS['TEMPERATURE'] #. WEATHER_TERMS['TEMPERATURE']
#: searx/searxng.msg searx/templates/simple/answer/weather.html:17 #: searx/searxng.msg searx/templates/simple/answer/weather.html:17
msgid "Temperature" msgid "Temperature"
msgstr "درجة الحرارة" msgstr "درجة حرارة"
#. WEATHER_TERMS['UV INDEX'] #. WEATHER_TERMS['UV INDEX']
#: searx/searxng.msg #: searx/searxng.msg
@@ -337,12 +339,12 @@ msgstr "مطر خفيف"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Rain and thunder" msgid "Rain and thunder"
msgstr "مطر و رعد" msgstr "مطر ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Rain showers and thunder" msgid "Rain showers and thunder"
msgstr "زخات مطر و رعد" msgstr "زخات مطر ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
@@ -357,12 +359,12 @@ msgstr "مطر"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy rain and thunder" msgid "Heavy rain and thunder"
msgstr "مطر غزير و رعد" msgstr "مطر غزير ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy rain showers and thunder" msgid "Heavy rain showers and thunder"
msgstr "رعد و زخات مطر شديدة" msgstr "رعد وزخات مطر شديدة"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
@@ -377,72 +379,72 @@ msgstr "مطر غزير"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light sleet and thunder" msgid "Light sleet and thunder"
msgstr "" msgstr "مطر خفيف مخلوط بثلج ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light sleet showers and thunder" msgid "Light sleet showers and thunder"
msgstr "" msgstr "زخات مطر خفيفة مصحوبة بثلج ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light sleet showers" msgid "Light sleet showers"
msgstr "" msgstr "زخات ثلجية خفيفة"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light sleet" msgid "Light sleet"
msgstr "" msgstr "تساقط ثلجي خفيف"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sleet and thunder" msgid "Sleet and thunder"
msgstr "" msgstr "ثلوج ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sleet showers and thunder" msgid "Sleet showers and thunder"
msgstr "" msgstr "زخات مطر ثلجي ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sleet showers" msgid "Sleet showers"
msgstr "" msgstr "زخات مطر ثلجي"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Sleet" msgid "Sleet"
msgstr "" msgstr "مطر ثلجي"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy sleet and thunder" msgid "Heavy sleet and thunder"
msgstr "" msgstr "مطر ثلجي غزير ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy sleet showers and thunder" msgid "Heavy sleet showers and thunder"
msgstr "" msgstr "زخات مطر ثلجي غزيرة ورعد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy sleet showers" msgid "Heavy sleet showers"
msgstr "" msgstr "زخات مطر ثلجي غزيرة"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy sleet" msgid "Heavy sleet"
msgstr "" msgstr "مطر ثلجي غزير"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light snow and thunder" msgid "Light snow and thunder"
msgstr "رعد و ثلج خفيف" msgstr "رعد وثلج خفيف"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Light snow showers and thunder" msgid "Light snow showers and thunder"
msgstr "رعد و زخات ثلج خفيفة" msgstr "رعد وزخات ثلج خفيفة"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
@@ -457,12 +459,12 @@ msgstr "ثلج خفيف"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Snow and thunder" msgid "Snow and thunder"
msgstr "رعد و ثلج" msgstr "رعد وثلج"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Snow showers and thunder" msgid "Snow showers and thunder"
msgstr "رعد و زخات ثلج" msgstr "رعد وزخات ثلج"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
@@ -477,12 +479,12 @@ msgstr "ثلج"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy snow and thunder" msgid "Heavy snow and thunder"
msgstr "رعد و ثلج شديد" msgstr "رعد وثلج شديد"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
msgid "Heavy snow showers and thunder" msgid "Heavy snow showers and thunder"
msgstr "رعد و زخات ثلج شديدة" msgstr "رعد وزخات ثلج شديدة"
#. WEATHER_CONDITIONS #. WEATHER_CONDITIONS
#: searx/searxng.msg #: searx/searxng.msg
@@ -497,12 +499,12 @@ msgstr "ثلج شديد"
#. SOCIAL_MEDIA_TERMS['SUBSCRIBERS'] #. SOCIAL_MEDIA_TERMS['SUBSCRIBERS']
#: searx/engines/lemmy.py:85 searx/searxng.msg #: searx/engines/lemmy.py:85 searx/searxng.msg
msgid "subscribers" msgid "subscribers"
msgstr "المشتركين" msgstr "مشتركين"
#. SOCIAL_MEDIA_TERMS['POSTS'] #. SOCIAL_MEDIA_TERMS['POSTS']
#: searx/engines/lemmy.py:86 searx/searxng.msg #: searx/engines/lemmy.py:86 searx/searxng.msg
msgid "posts" msgid "posts"
msgstr "المنشور" msgstr "منشورات"
#. SOCIAL_MEDIA_TERMS['ACTIVE USERS'] #. SOCIAL_MEDIA_TERMS['ACTIVE USERS']
#: searx/engines/lemmy.py:87 searx/searxng.msg #: searx/engines/lemmy.py:87 searx/searxng.msg
@@ -533,12 +535,12 @@ msgstr "النقاط"
#. SOCIAL_MEDIA_TERMS['TITLE'] #. SOCIAL_MEDIA_TERMS['TITLE']
#: searx/searxng.msg #: searx/searxng.msg
msgid "title" msgid "title"
msgstr "العنوان" msgstr "عنوان"
#. SOCIAL_MEDIA_TERMS['AUTHOR'] #. SOCIAL_MEDIA_TERMS['AUTHOR']
#: searx/engines/hackernews.py:86 searx/searxng.msg #: searx/engines/hackernews.py:86 searx/searxng.msg
msgid "author" msgid "author"
msgstr "الكاتب" msgstr "كاتب"
#. SOCIAL_MEDIA_TERMS['THREAD OPEN'] #. SOCIAL_MEDIA_TERMS['THREAD OPEN']
#: searx/engines/discourse.py:149 searx/searxng.msg #: searx/engines/discourse.py:149 searx/searxng.msg
@@ -553,36 +555,36 @@ msgstr "مغلق"
#. SOCIAL_MEDIA_TERMS['THREAD ANSWERED'] #. SOCIAL_MEDIA_TERMS['THREAD ANSWERED']
#: searx/engines/discourse.py:160 searx/searxng.msg #: searx/engines/discourse.py:160 searx/searxng.msg
msgid "answered" msgid "answered"
msgstr "أُجيبت" msgstr "تمت الإجابة"
#: searx/webapp.py:327 #: searx/webapp.py:326
msgid "No item found" msgid "No item found"
msgstr "تعذر العثور على عناصر" msgstr "لم يتم العثور على عنصر"
#: searx/engines/qwant.py:291 #: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:329 #: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:328
msgid "Source" msgid "Source"
msgstr "المصدر" msgstr "مصدر"
#: searx/webapp.py:331 #: searx/webapp.py:330
msgid "Error loading the next page" msgid "Error loading the next page"
msgstr "حدث خلل أثناء تحميل الصفحة التالية" msgstr "خطأ في تحميل الصفحة التالية"
#: searx/webapp.py:480 searx/webapp.py:881 #: searx/webapp.py:479 searx/webapp.py:877
msgid "Invalid settings, please edit your preferences" msgid "Invalid settings, please edit your preferences"
msgstr "إنّ الإعدادات خاطئة، يرجى تعديل خياراتك" msgstr "إعدادات غير صالحة، يرجى تعديل تفضيلاتك"
#: searx/webapp.py:496 #: searx/webapp.py:495
msgid "Invalid settings" msgid "Invalid settings"
msgstr "إعدادات غير صالحة" msgstr "إعدادات غير صالحة"
#: searx/webapp.py:573 searx/webapp.py:663 #: searx/webapp.py:571 searx/webapp.py:661
msgid "search error" msgid "search error"
msgstr "خطأ في البحث" msgstr "خطأ في البحث"
#: searx/webutils.py:36 #: searx/webutils.py:36
msgid "timeout" msgid "timeout"
msgstr "نفذ الوقت" msgstr "انتهت المهلة"
#: searx/webutils.py:37 #: searx/webutils.py:37
msgid "parsing error" msgid "parsing error"
@@ -614,7 +616,7 @@ msgstr "خطأ في اتصال HTTP"
#: searx/webutils.py:56 #: searx/webutils.py:56
msgid "proxy error" msgid "proxy error"
msgstr "خطأ في وكيل البروكسي" msgstr "خطأ في الخادم الوكيل"
#: searx/webutils.py:57 #: searx/webutils.py:57
msgid "CAPTCHA" msgid "CAPTCHA"
@@ -630,21 +632,21 @@ msgstr "الدخول مرفوض"
#: searx/webutils.py:60 #: searx/webutils.py:60
msgid "server API error" msgid "server API error"
msgstr "خطأ في API الخادم" msgstr "خطأ في واجهة برمجة التطبيقات الخادم"
#: searx/webutils.py:79 #: searx/webutils.py:79
msgid "Suspended" msgid "Suspended"
msgstr "معلق" msgstr "معلق"
#: searx/webutils.py:308 #: searx/webutils.py:307
#, python-brace-format #, python-brace-format
msgid "{minutes} minute(s) ago" msgid "{minutes} minute(s) ago"
msgstr "{minutes} minute(s) ago" msgstr "منذ {minutes} دقيقة"
#: searx/webutils.py:309 #: searx/webutils.py:308
#, python-brace-format #, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago" msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "قبل {hours} ساعات، {minutes} دقائق" msgstr "منذ {hours} ساعات، {minutes} دقائق"
#: searx/answerers/random.py:68 #: searx/answerers/random.py:68
msgid "Generate different random values" msgid "Generate different random values"
@@ -658,20 +660,20 @@ msgstr "حساب {func} من الحجج"
#: searx/engines/boardreader.py:107 #: searx/engines/boardreader.py:107
#, python-brace-format #, python-brace-format
msgid "Posted by {author}" msgid "Posted by {author}"
msgstr "" msgstr "منشور بواسطة {author}"
#: searx/engines/openstreetmap.py:155 #: searx/engines/openstreetmap.py:155
msgid "Show route in map .." msgid "Show route in map .."
msgstr "أظهِر الطريق على الخريطة .." msgstr "اظهار المسار على الخريطة .."
#: searx/engines/pdbe.py:96 #: searx/engines/pdbe.py:96
#, python-brace-format #, python-brace-format
msgid "{title} (OBSOLETE)" msgid "{title} (OBSOLETE)"
msgstr "{title} (قديما)" msgstr "{title} (قديم)"
#: searx/engines/pdbe.py:103 #: searx/engines/pdbe.py:103
msgid "This entry has been superseded by" msgid "This entry has been superseded by"
msgstr "هذا الإدخال تم استبداله بـ" msgstr "تم استبدال هذا الإدخال بـ"
#: searx/engines/qwant.py:293 #: searx/engines/qwant.py:293
msgid "Channel" msgid "Channel"
@@ -741,11 +743,11 @@ msgstr "قم بتصفية نتائج .onion التي تظهر في القائم
#: searx/plugins/calculator.py:25 #: searx/plugins/calculator.py:25
msgid "Calculator" msgid "Calculator"
msgstr "" msgstr "الآلة الحاسبة"
#: searx/plugins/calculator.py:26 #: searx/plugins/calculator.py:26
msgid "Parses and solves mathematical expressions." msgid "Parses and solves mathematical expressions."
msgstr "" msgstr "يقوم بتحليل وحل التعبيرات الرياضية."
#: searx/plugins/hash_plugin.py:33 #: searx/plugins/hash_plugin.py:33
msgid "Hash plugin" msgid "Hash plugin"
@@ -781,7 +783,7 @@ msgstr "تمرير الصفحات بلا حدود"
msgid "" msgid ""
"Automatically loads the next page when scrolling to bottom of the current" "Automatically loads the next page when scrolling to bottom of the current"
" page" " page"
msgstr "" msgstr "يحمل الصفحة التالية تلقائيًا عند التمرير إلى أسفل الصفحة الحالية"
#: searx/plugins/oa_doi_rewrite.py:54 #: searx/plugins/oa_doi_rewrite.py:54
msgid "Open Access DOI rewrite" msgid "Open Access DOI rewrite"
@@ -817,11 +819,11 @@ msgstr "وكيل المستخدم الخاص بك هو "
#: searx/plugins/time_zone.py:33 #: searx/plugins/time_zone.py:33
msgid "Timezones plugin" msgid "Timezones plugin"
msgstr "" msgstr "إضافة المناطق الزمنية"
#: searx/plugins/time_zone.py:34 #: searx/plugins/time_zone.py:34
msgid "Display the current time on different time zones." msgid "Display the current time on different time zones."
msgstr "" msgstr "اعرض الوقت الحالي في مناطق زمنية مختلفة."
#: searx/plugins/tor_check.py:41 #: searx/plugins/tor_check.py:41
msgid "Tor check plugin" msgid "Tor check plugin"
@@ -868,7 +870,7 @@ msgstr "التحويل بين الوحدات"
#: searx/result_types/answer.py:223 #: searx/result_types/answer.py:223
#, python-brace-format #, python-brace-format
msgid "{location}: {temperature}, {condition}" msgid "{location}: {temperature}, {condition}"
msgstr "{مكان}:{حرارة},{وضع}" msgstr "{location}:{temperature},{condition}"
#: searx/templates/simple/404.html:4 #: searx/templates/simple/404.html:4
msgid "Page not found" msgid "Page not found"
@@ -883,45 +885,45 @@ msgstr "إذهب إلى %(search_page)s."
msgid "search page" msgid "search page"
msgstr "صفحة البحث" msgstr "صفحة البحث"
#: searx/templates/simple/base.html:50 #: searx/templates/simple/base.html:51
msgid "Donate" msgid "Donate"
msgstr "تبرُّع" msgstr "تبرُّع"
#: searx/templates/simple/base.html:55 searx/templates/simple/base.html:57 #: searx/templates/simple/base.html:56 searx/templates/simple/base.html:58
#: searx/templates/simple/preferences.html:152 #: searx/templates/simple/preferences.html:152
msgid "Preferences" msgid "Preferences"
msgstr "التفضيلات" msgstr "التفضيلات"
#: searx/templates/simple/base.html:68 #: searx/templates/simple/base.html:69
msgid "Powered by" msgid "Powered by"
msgstr "مدعوم بواسطة" msgstr "مدعوم بواسطة"
#: searx/templates/simple/base.html:68 #: searx/templates/simple/base.html:69
msgid "a privacy-respecting, open metasearch engine" msgid "a privacy-respecting, open metasearch engine"
msgstr "الخصوصية ذو الاعتبار, محرك البحث عميق عُموميا" msgstr "الخصوصية ذو الاعتبار, محرك البحث عميق عُموميا"
#: searx/templates/simple/base.html:69 #: searx/templates/simple/base.html:70
#: searx/templates/simple/result_templates/packages.html:59 #: searx/templates/simple/result_templates/packages.html:59
msgid "Source code" msgid "Source code"
msgstr "شيفرة مصدرية" msgstr "شيفرة مصدرية"
#: searx/templates/simple/base.html:70 #: searx/templates/simple/base.html:71
msgid "Issue tracker" msgid "Issue tracker"
msgstr "تعقب القضايا" msgstr "تعقب القضايا"
#: searx/templates/simple/base.html:71 searx/templates/simple/stats.html:17 #: searx/templates/simple/base.html:72 searx/templates/simple/stats.html:17
msgid "Engine stats" msgid "Engine stats"
msgstr "إحصائيات المحرك" msgstr "إحصائيات المحرك"
#: searx/templates/simple/base.html:73 #: searx/templates/simple/base.html:74
msgid "Public instances" msgid "Public instances"
msgstr "نماذج الخوادم العمومية" msgstr "نماذج الخوادم العمومية"
#: searx/templates/simple/base.html:76 #: searx/templates/simple/base.html:77
msgid "Privacy policy" msgid "Privacy policy"
msgstr "سياسة الخصوصية" msgstr "سياسة الخصوصية"
#: searx/templates/simple/base.html:79 #: searx/templates/simple/base.html:80
msgid "Contact instance maintainer" msgid "Contact instance maintainer"
msgstr "اتصال بالمشرف المخدم النموذجي" msgstr "اتصال بالمشرف المخدم النموذجي"
@@ -991,14 +993,16 @@ msgid ""
"This is a preview of the settings used by the 'Search URL' you used to " "This is a preview of the settings used by the 'Search URL' you used to "
"get here." "get here."
msgstr "" msgstr ""
"هذه معاينة للإعدادات المستخدمة في 'عنوان URL للبحث' الذي استخدمته للوصول إلى "
"هنا."
#: searx/templates/simple/preferences.html:158 #: searx/templates/simple/preferences.html:158
msgid "Press save to copy these preferences to your browser." msgid "Press save to copy these preferences to your browser."
msgstr "" msgstr "اضغط على حفظ لنسخ هذه التفضيلات إلى متصفحك."
#: searx/templates/simple/preferences.html:159 #: searx/templates/simple/preferences.html:159
msgid "Click here to view your browser preferences instead:" msgid "Click here to view your browser preferences instead:"
msgstr "" msgstr "انقر هنا لعرض تفضيلات متصفحك بدلاً من ذلك:"
#: searx/templates/simple/preferences.html:169 #: searx/templates/simple/preferences.html:169
msgid "General" msgid "General"
@@ -1032,23 +1036,19 @@ msgstr "استفسارات خاصة"
msgid "Cookies" msgid "Cookies"
msgstr "كعكات الكوكيز" msgstr "كعكات الكوكيز"
#: searx/templates/simple/results.html:30 #: searx/templates/simple/results.html:32
msgid "Number of results"
msgstr "حصيلة نتائج البحث"
#: searx/templates/simple/results.html:36
msgid "Info" msgid "Info"
msgstr "معلومات" msgstr "معلومات"
#: searx/templates/simple/results.html:77 #: searx/templates/simple/results.html:73
msgid "Back to top" msgid "Back to top"
msgstr "العودة للأعلى" msgstr "العودة للأعلى"
#: searx/templates/simple/results.html:95 #: searx/templates/simple/results.html:91
msgid "Previous page" msgid "Previous page"
msgstr "الصفحة السابقة" msgstr "الصفحة السابقة"
#: searx/templates/simple/results.html:113 #: searx/templates/simple/results.html:109
msgid "Next page" msgid "Next page"
msgstr "الصفحة التالية" msgstr "الصفحة التالية"
@@ -1089,12 +1089,12 @@ msgid "Result count"
msgstr "نتيجة العد" msgstr "نتيجة العد"
#: searx/templates/simple/elements/engines_msg.html:7 #: searx/templates/simple/elements/engines_msg.html:7
#: searx/templates/simple/preferences/engines.html:31 #: searx/templates/simple/preferences/engines.html:30
#: searx/templates/simple/stats.html:27 #: searx/templates/simple/stats.html:27
msgid "Response time" msgid "Response time"
msgstr "مدة الإستجابة" msgstr "مدة الإستجابة"
#: searx/templates/simple/preferences/engines.html:35 #: searx/templates/simple/preferences/engines.html:34
#: searx/templates/simple/stats.html:28 #: searx/templates/simple/stats.html:28
msgid "Reliability" msgid "Reliability"
msgstr "إمكانية الإشتغال" msgstr "إمكانية الإشتغال"
@@ -1164,7 +1164,7 @@ msgstr "مرادفات"
#: searx/templates/simple/answer/weather.html:19 #: searx/templates/simple/answer/weather.html:19
msgid "Feels Like" msgid "Feels Like"
msgstr "" msgstr "درجة الحرارة ظاهريًا"
#: searx/templates/simple/elements/answers.html:2 #: searx/templates/simple/elements/answers.html:2
msgid "Answers" msgid "Answers"
@@ -1223,7 +1223,7 @@ msgstr "الاكتشاف التلقائي"
#: searx/templates/simple/filters/safesearch.html:2 #: searx/templates/simple/filters/safesearch.html:2
#: searx/templates/simple/filters/safesearch.html:3 #: searx/templates/simple/filters/safesearch.html:3
#: searx/templates/simple/filters/safesearch.html:4 #: searx/templates/simple/filters/safesearch.html:4
#: searx/templates/simple/preferences/engines.html:27 #: searx/templates/simple/preferences/engines.html:26
#: searx/templates/simple/preferences/safesearch.html:2 #: searx/templates/simple/preferences/safesearch.html:2
msgid "SafeSearch" msgid "SafeSearch"
msgstr "البحث المؤمَّن" msgstr "البحث المؤمَّن"
@@ -1244,7 +1244,7 @@ msgid "None"
msgstr "لا شيء" msgstr "لا شيء"
#: searx/templates/simple/filters/time_range.html:1 #: searx/templates/simple/filters/time_range.html:1
#: searx/templates/simple/preferences/engines.html:28 #: searx/templates/simple/preferences/engines.html:27
msgid "Time range" msgid "Time range"
msgstr "الفترة" msgstr "الفترة"
@@ -1294,7 +1294,7 @@ msgstr "حدث الصفحة."
#: searx/templates/simple/messages/no_results.html:20 #: searx/templates/simple/messages/no_results.html:20
msgid "Search for another query or select another category (above)." msgid "Search for another query or select another category (above)."
msgstr "ابحث عن استعلام آخر أو اختار فئة أخرى (أعلاه)" msgstr "ابحث عن استعلام آخر أو اختار فئة أخرى (أعلاه)."
#: searx/templates/simple/messages/no_results.html:21 #: searx/templates/simple/messages/no_results.html:21
msgid "Change the search engine used in the preferences:" msgid "Change the search engine used in the preferences:"
@@ -1302,7 +1302,7 @@ msgstr "قم بتغيير محرك البحث المستخدم في الإعدا
#: searx/templates/simple/messages/no_results.html:22 #: searx/templates/simple/messages/no_results.html:22
msgid "Switch to another instance:" msgid "Switch to another instance:"
msgstr "قم بتبديل SearxNG إلى نسخة أخرى:" msgstr "قم بتبديل SearXNG إلى نسخة أخرى:"
#: searx/templates/simple/messages/no_results.html:24 #: searx/templates/simple/messages/no_results.html:24
msgid "Search for another query or select another category." msgid "Search for another query or select another category."
@@ -1324,7 +1324,7 @@ msgstr "الكلمات الرئيسية (أول كلمة في الاستعلام
#: searx/templates/simple/preferences/answerers.html:6 #: searx/templates/simple/preferences/answerers.html:6
#: searx/templates/simple/result_templates/packages.html:7 #: searx/templates/simple/result_templates/packages.html:7
msgid "Name" msgid "Name"
msgstr "التسمية" msgstr "الإسم"
#: searx/templates/simple/preferences/answerers.html:7 #: searx/templates/simple/preferences/answerers.html:7
msgid "Description" msgid "Description"
@@ -1427,6 +1427,8 @@ msgid ""
"This tab does not exist in the user interface, but you can search with " "This tab does not exist in the user interface, but you can search with "
"these engines via !bangs." "these engines via !bangs."
msgstr "" msgstr ""
"لا توجد علامة التبويب هذه في واجهة المستخدم، ولكن يمكنك البحث باستخدام "
"هذه المحركات عبر !bangs."
#: searx/templates/simple/preferences/engines.html:15 #: searx/templates/simple/preferences/engines.html:15
msgid "Enable all" msgid "Enable all"
@@ -1440,15 +1442,11 @@ msgstr "عطّل الكل"
msgid "!bang" msgid "!bang"
msgstr "!bang" msgstr "!bang"
#: searx/templates/simple/preferences/engines.html:26 #: searx/templates/simple/preferences/engines.html:28
msgid "Supports selected language"
msgstr "يدعم اللغة المختارة"
#: searx/templates/simple/preferences/engines.html:29
msgid "Weight" msgid "Weight"
msgstr "وَزن" msgstr "وَزن"
#: searx/templates/simple/preferences/engines.html:33 #: searx/templates/simple/preferences/engines.html:32
msgid "Max time" msgid "Max time"
msgstr "أقصى مدّة" msgstr "أقصى مدّة"
@@ -1507,7 +1505,7 @@ msgstr "وكيل بروكسي الصور"
#: searx/templates/simple/preferences/image_proxy.html:14 #: searx/templates/simple/preferences/image_proxy.html:14
msgid "Proxy image results through SearXNG" msgid "Proxy image results through SearXNG"
msgstr "" msgstr "تمرير نتائج البحث عن الصور عبر بروكسي SearXNG"
#: searx/templates/simple/preferences/language.html:24 #: searx/templates/simple/preferences/language.html:24
msgid "What language do you prefer for search?" msgid "What language do you prefer for search?"
@@ -1539,11 +1537,11 @@ msgstr ""
#: searx/templates/simple/preferences/results_on_new_tab.html:2 #: searx/templates/simple/preferences/results_on_new_tab.html:2
msgid "Results in new tabs" msgid "Results in new tabs"
msgstr "" msgstr "النتائج في علامات تبويب جديدة"
#: searx/templates/simple/preferences/results_on_new_tab.html:14 #: searx/templates/simple/preferences/results_on_new_tab.html:14
msgid "Open result links in new browser tabs" msgid "Open result links in new browser tabs"
msgstr "" msgstr "افتح روابط النتائج في علامات تبويب جديدة في المتصفح"
#: searx/templates/simple/preferences/safesearch.html:20 #: searx/templates/simple/preferences/safesearch.html:20
msgid "Filter content" msgid "Filter content"
@@ -1668,7 +1666,7 @@ msgstr "عرض المصدر"
#: searx/templates/simple/result_templates/map.html:12 #: searx/templates/simple/result_templates/map.html:12
msgid "address" msgid "address"
msgstr "عنوان" msgstr "عنوان السكن"
#: searx/templates/simple/result_templates/map.html:43 #: searx/templates/simple/result_templates/map.html:43
msgid "show map" msgid "show map"
@@ -1765,7 +1763,7 @@ msgstr "الحاصد"
#: searx/templates/simple/result_templates/torrent.html:19 #: searx/templates/simple/result_templates/torrent.html:19
msgid "Number of Files" msgid "Number of Files"
msgstr "عدد الملفات" msgstr "عدد ملفات"
#: searx/templates/simple/result_templates/videos.html:6 #: searx/templates/simple/result_templates/videos.html:6
msgid "show video" msgid "show video"
@@ -2393,3 +2391,8 @@ msgstr "إخفاء الفيديو"
#~ msgid "Submit a new issue on Github including the above information" #~ msgid "Submit a new issue on Github including the above information"
#~ msgstr "قم بتقديم مشكلة جديدة على GitHub بالمعلومات الواردة أعلاه" #~ msgstr "قم بتقديم مشكلة جديدة على GitHub بالمعلومات الواردة أعلاه"
#~ msgid "Supports selected language"
#~ msgstr "يدعم اللغة المختارة"
#~ msgid "Number of results"
#~ msgstr "حصيلة نتائج البحث"
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More