Compare commits

...

72 Commits

Author SHA1 Message Date
Markus Heiser 0e69e9dcb3
Merge 3581e1b85f into 10d3af84b8 2024-11-20 02:15:16 +00:00
Markus Heiser 10d3af84b8 [fix] engine: duckduckgo - don't quote query string
The query string send to DDG must not be qouted.

The query string was URL-qouted in #4011, but the URL-qouted query string result
in unexpected *URL decoded* and other garbish results as reported in #4019
and #4020.  To test compare the results of a query like::

    !ddg Häuser und Straßen :de
    !ddg Häuser und Straßen :all
    !ddg 房屋和街道 :all
    !ddg 房屋和街道 :zh

Closed:

- [#4019] https://github.com/searxng/searxng/issues/4019
- [#4020] https://github.com/searxng/searxng/issues/4020

Related:

- [#4011] https://github.com/searxng/searxng/pull/4011

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-11-17 18:14:22 +01:00
dependabot[bot] 4b57bc3db1 [upd] pypi: Bump flask from 3.0.3 to 3.1.0
Bumps [flask](https://github.com/pallets/flask) from 3.0.3 to 3.1.0.
- [Release notes](https://github.com/pallets/flask/releases)
- [Changelog](https://github.com/pallets/flask/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/flask/compare/3.0.3...3.1.0)

---
updated-dependencies:
- dependency-name: flask
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-15 13:23:24 +01:00
searxng-bot a345cbbe51 [l10n] update translations from Weblate
0216898a3 - 2024-11-14 - KhietVo <KhietVo@users.noreply.translate.codeberg.org>
3293db4c8 - 2024-11-14 - return42 <return42@users.noreply.translate.codeberg.org>
574e0d683 - 2024-11-13 - lrnz2 <lrnz2@users.noreply.translate.codeberg.org>
a32d9d158 - 2024-11-08 - Aadniz <Aadniz@users.noreply.translate.codeberg.org>
2024-11-15 13:20:52 +01:00
Nicolas Dato abd9b271bc [fix] engine: duckduckgo - only uses first word of the search terms
during the revision in PR #3955 the query string was accidentally converted into
a list of words, further the query must be quoted before POSTed in the ``data``
field, see ``urllib.parse.quote_plus`` [1]

[1] https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote_plus

Closed: #4009
Co-Authored-by: @return42
2024-11-14 09:33:54 +01:00
Leo Liu dfaf5868e2 [fix] settings.yml - enabled_plugins: document to reflect default settings
Remove 'Autodetect search language', which is no longer valid, from settings,
and add 'Unit converter plugin', which is now default enabled, to settings.
2024-11-10 16:09:41 +01:00
Leo Liu b173f3a8b9 Fix scheduler.lua 2024-11-10 15:53:58 +01:00
dependabot[bot] 2fbf15eccb [upd] pypi: Bump typer-slim from 0.12.5 to 0.13.0
Bumps [typer-slim](https://github.com/fastapi/typer) from 0.12.5 to 0.13.0.
- [Release notes](https://github.com/fastapi/typer/releases)
- [Changelog](https://github.com/fastapi/typer/blob/master/docs/release-notes.md)
- [Commits](https://github.com/fastapi/typer/compare/0.12.5...0.13.0)

---
updated-dependencies:
- dependency-name: typer-slim
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-08 08:51:43 +01:00
searxng-bot 08c5f258d8 [l10n] update translations from Weblate
8d90a7e6d - 2024-11-06 - sahussawud <sahussawud@users.noreply.translate.codeberg.org>
41ee8bb0d - 2024-11-02 - laaknor <laaknor@users.noreply.translate.codeberg.org>
c1a30afab - 2024-11-02 - return42 <return42@users.noreply.translate.codeberg.org>
627ab7a8e - 2024-11-01 - zarlin <zarlin@users.noreply.translate.codeberg.org>
2024-11-08 08:45:07 +01:00
dependabot[bot] cd384a8a60 [upd] pypi: Bump selenium from 4.25.0 to 4.26.1
Bumps [selenium](https://github.com/SeleniumHQ/Selenium) from 4.25.0 to 4.26.1.
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/commits)

---
updated-dependencies:
- dependency-name: selenium
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-06 10:01:13 +01:00
Markus Heiser c4055e449f [fix] issues reported by `make test.yamllint`
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-11-06 08:16:21 +01:00
Markus Heiser 2fdbf2622b [mod] lint github YAML config files
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-11-06 08:16:21 +01:00
Bnyro b07c0ae39f [fix] annas archive: crash when no thumbnail, differing results, paging 2024-11-01 12:49:33 +01:00
Markus Heiser 56e3d72a76 [fix] CI: remove target test.coverage from python's test matrix
The test.coverage cause a lot of failed CI jobs for reasons that cannot be
explained.  As we do not monitor the coverage anyway, it is superfluous to run
this job, especially as it only has a disruptive effect on the CI.

BTW and the CI action upload-artifact@v3 is deprecated [1]

[1] https://github.com/actions/upload-artifact?tab=readme-ov-file#actionsupload-artifact

Related: https://github.com/searxng/searxng/issues/3983
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-11-01 10:14:57 +01:00
searxng-bot cc148a76b0 [l10n] update translations from Weblate
a4cdaaa26 - 2024-10-30 - Juno Takano <jutty@users.noreply.translate.codeberg.org>
46bad3a79 - 2024-10-29 - saltsnorter <saltsnorter@users.noreply.translate.codeberg.org>
6a4096da9 - 2024-10-27 - Eryk Michalak <gnu.ewm@protonmail.com>
64815d956 - 2024-10-28 - ljansen <ljansen@users.noreply.translate.codeberg.org>
851ae554d - 2024-10-26 - return42 <return42@users.noreply.translate.codeberg.org>
24f16d5e3 - 2024-10-26 - return42 <return42@users.noreply.translate.codeberg.org>
8278d1cb9 - 2024-10-26 - Atul_Eterno <Atul_Eterno@users.noreply.translate.codeberg.org>
2024-11-01 08:30:38 +01:00
uply23333 fa108c140f [fix] google: display every result when keyword is contained in content field 2024-10-31 13:21:32 +01:00
Markus Heiser fa4dfd4efe [fix] favicons: msgspec.ValidationError: Expected `Path`, got `str` - at `$.favicons.cache.db_url`
Closes: https://github.com/searxng/searxng/issues/3975
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-29 18:22:22 +01:00
Markus Heiser b183e620d8 [refactor] engine: duckduckgo - https://html.duckduckgo.com/html
The entire source code of the duckduckgo engine has been reengineered and
purified.

1. DDG used the URL https://html.duckduckgo.com/html for no-JS requests whose
   response is also easier to parse than the previous
   https://lite.duckduckgo.com/lite/ URL

2. the bot detection of DDG has so far caused problems and often led to a
   CAPTCHA, this can be circumvented using `'Sec-Fetch-Mode'] = “navigate”`

Closes: https://github.com/searxng/searxng/issues/3927
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-29 14:56:27 +01:00
Markus Heiser f63f97c56c Revert "Fix for broken docker builds"
This reverts commit 4ef1c706f8.
2024-10-29 13:50:38 +01:00
Markus Heiser 163031c394 Revert "[fix] typo in Dockerfile"
This reverts commit 038a2ff6bd.
2024-10-29 13:50:38 +01:00
Markus Heiser 3e5621e1af [refactor] replace pydantic by msgspec
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-29 13:50:38 +01:00
return42 e392892578 [data] update searx.data - update_firefox_version.py 2024-10-29 09:30:40 +01:00
return42 68ed8245da [data] update searx.data - update_ahmia_blacklist.py 2024-10-29 09:29:58 +01:00
return42 2d748d1d74 [data] update searx.data - update_currencies.py 2024-10-29 09:29:18 +01:00
return42 2985ece0ca [data] update searx.data - update_wikidata_units.py 2024-10-29 09:28:58 +01:00
return42 adc38c5800 [data] update searx.data - update_engine_traits.py 2024-10-29 09:28:28 +01:00
return42 a084436ff4 [data] update searx.data - update_engine_descriptions.py 2024-10-29 09:17:30 +01:00
Markus Heiser b176323e89 [fix] calculator: use locale from UI (not from selected language)
Closes: https://github.com/searxng/searxng/issues/3956
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-28 15:53:57 +01:00
Markus Heiser da28f5280b [fix] limiter: don't hard code settings folder to /etc/searxng
The location of the local settings depends on environment ``SEARXNG_SETTINGS_PATH``
and can be different from ``/etc/searxng``.  Issue was reported on Matrix [1].

To get the location function ``searx.settings_loader.get_user_cfg_folder()``
should be used.

[1] https://matrix.to/#/!vxScbLNEAmRvOraXBn:matrix.org/$_eLS0JpE9oVEWsiGJkqJnWcFWEeZClIMGDK6cWv_Q4g?via=matrix.org&via=tchncs.de&via=envs.net

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-28 12:51:05 +01:00
dependabot[bot] 543ab92fde [upd] pypi: Bump pallets-sphinx-themes from 2.2.0 to 2.3.0
Bumps [pallets-sphinx-themes](https://github.com/pallets/pallets-sphinx-themes) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/pallets/pallets-sphinx-themes/releases)
- [Changelog](https://github.com/pallets/pallets-sphinx-themes/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/pallets-sphinx-themes/compare/2.2.0...2.3.0)

---
updated-dependencies:
- dependency-name: pallets-sphinx-themes
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-27 10:48:26 +01:00
Markus Heiser e08ff05fff [fix] add missing tomli to the requirements.txt
Package ``tomli`` is needed for py < 3.11, BTW remove the no longer needed
pytomlpp package.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-26 15:59:42 +02:00
Markus Heiser a3921b5ed7 [mod] add test to check compat.py module
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-26 15:59:42 +02:00
Markus Heiser ae496e9dd0 [build] /static 2024-10-26 08:34:21 +02:00
JJ 9b01e3c9d6 [mod] shrink new favicons in search results 2024-10-26 08:34:21 +02:00
searxng-bot 446ee2dd25 [l10n] update translations from Weblate
21c063bf1 - 2024-10-20 - Priit Jõerüüt <jrtcdbrg@users.noreply.translate.codeberg.org>
2024-10-26 07:29:54 +02:00
Markus Heiser b14d885f23 [fix] favicons: don't hard code settings folder to /etc/searxng
The location of the local settings depends on environment ``SEARXNG_SETTINGS_PATH``
and can be different from ``/etc/searxng``.  Issue was reported on Matrix [1].

To get the location function ``searx.settings_loader.get_user_cfg_folder()``
should be used.

[1] https://matrix.to/#/!vxScbLNEAmRvOraXBn:matrix.org/$5xNMYvONGB-mPt2B3ttoL27QncRFhkjGkO-TISdmP08?via=matrix.org&via=tchncs.de&via=envs.net

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-23 05:29:15 +02:00
Markus Heiser 050451347b [fix] engine: duckduckgo - CAPTCHA detection
The previous implementation could not distinguish a CAPTCHA response from an
ordinary result list.  In the previous implementation a CAPTCHA was taken as a
result list where no items are in.

DDG does not block IPs.  Instead, a CAPTCHA wall is placed in front of request
on a dubious request.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-19 14:55:44 +02:00
dependabot[bot] 88caa1d7db [upd] pypi: Bump sphinx-issues from 4.1.0 to 5.0.0
Bumps [sphinx-issues](https://github.com/sloria/sphinx-issues) from 4.1.0 to 5.0.0.
- [Commits](https://github.com/sloria/sphinx-issues/compare/4.1.0...5.0.0)

---
updated-dependencies:
- dependency-name: sphinx-issues
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-19 11:18:42 +02:00
dependabot[bot] a0c704c860 [upd] pypi: Bump uvloop from 0.20.0 to 0.21.0
Bumps [uvloop](https://github.com/MagicStack/uvloop) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/MagicStack/uvloop/releases)
- [Commits](https://github.com/MagicStack/uvloop/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: uvloop
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-19 11:06:25 +02:00
dependabot[bot] 219040c766 [upd] pypi: Bump pallets-sphinx-themes from 2.1.3 to 2.2.0
Bumps [pallets-sphinx-themes](https://github.com/pallets/pallets-sphinx-themes) from 2.1.3 to 2.2.0.
- [Release notes](https://github.com/pallets/pallets-sphinx-themes/releases)
- [Changelog](https://github.com/pallets/pallets-sphinx-themes/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/pallets-sphinx-themes/compare/2.1.3...2.2.0)

---
updated-dependencies:
- dependency-name: pallets-sphinx-themes
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-19 11:05:55 +02:00
searxng-bot eeae3664c2 [l10n] update translations from Weblate
17f8bce27 - 2024-10-17 - hemie143 <hemie143@users.noreply.translate.codeberg.org>
fbeb82816 - 2024-10-15 - return42 <return42@users.noreply.translate.codeberg.org>
eacea331b - 2024-10-15 - return42 <return42@users.noreply.translate.codeberg.org>
36156687e - 2024-10-15 - return42 <return42@users.noreply.translate.codeberg.org>
61cb3375d - 2024-10-15 - return42 <return42@users.noreply.translate.codeberg.org>
2b3c92d37 - 2024-10-13 - Pedro_Tresp <Pedro_Tresp@users.noreply.translate.codeberg.org>
d4febbf8a - 2024-10-12 - stoychevww <stoychevww@users.noreply.translate.codeberg.org>
2024-10-19 11:01:32 +02:00
Markus Heiser 038a2ff6bd [fix] typo in Dockerfile
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-17 19:57:33 +02:00
rhee876527 4ef1c706f8 Fix for broken docker builds
Add temporary fix to broken docker builds in anticipation of yet to be released pydantic version v2.24.1
2024-10-17 19:14:33 +02:00
mrpaulblack cf7627557a [build] /static 2024-10-16 19:46:36 +02:00
mrpaulblack 2cacc560d6 [fix] simple theme: unset width for images on mobile layout
* fix fallout from 2fbedc4316
-> similar to 14fb187548
* change: set image height to 10rem
-> this is in my opinion more sensible size for mobile
2024-10-16 19:46:36 +02:00
Markus Heiser 058a072404 [build] /static 2024-10-15 15:54:58 +02:00
Markus Heiser 14fb187548 [fix] stretching pics in "pic search"
Issue is described in:

- https://github.com/searxng/searxng/issues/3915

The issue was caused bei merge of PR:

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

Solution:

- Unset `width` for objects of class `.result-images`.

Tested by `make run` and query a list of results, containing `.result` and
`.result-images` objects:

     !images !go bäder :de

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Closes: https://github.com/searxng/searxng/issues/3915
2024-10-15 15:54:58 +02:00
Markus Heiser c96ba25f5b [fix] online_currency.py: if more alternatives, use the last in the list
If there are more than one ISO 4217 numeric codes [1] for a currency use the
last one in the list of alternatives [2].

[1] https://en.wikipedia.org/wiki/ISO_4217#Active_codes_(list_one)
[2] https://en.wikipedia.org/wiki/ISO_4217#Historical_codes

Closes: https://github.com/searxng/searxng/issues/3713
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-15 15:20:39 +02:00
dependabot[bot] 2986681b31 [upd] pypi: Bump pylint from 3.2.7 to 3.3.1
Bumps [pylint](https://github.com/pylint-dev/pylint) from 3.2.7 to 3.3.1.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v3.2.7...v3.3.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 14:29:10 +02:00
Bnyro 9f48d5f84f [feat] engine: support for openlibrary 2024-10-15 13:06:00 +02:00
Grant Lanham 3e87354f0e [fix] float operations in calculator plugin
This patch adds an additional *isinstance* check within the ast parser to check
for float along with int, fixing the underlying issue.

Co-Authored: Markus Heiser <markus.heiser@darmarit.de>
2024-10-15 08:10:52 +02:00
Grant Lanham d448def1a6 [refactor] unit tests (continued) - plugins
This commit includes some refactoring in unit tests.  As we test more plugins,
it seems unweildy to include every test class in the test_plugins.py file.  This
patch split apart all of the test plugins to their own respective files,
including the new test_plugin_calculator.py file.
2024-10-15 08:10:52 +02:00
dependabot[bot] 8ba203c72b [upd] pypi: Bump sphinx-tabs from 3.4.5 to 3.4.7
Bumps [sphinx-tabs](https://github.com/executablebooks/sphinx-tabs) from 3.4.5 to 3.4.7.
- [Release notes](https://github.com/executablebooks/sphinx-tabs/releases)
- [Changelog](https://github.com/executablebooks/sphinx-tabs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/executablebooks/sphinx-tabs/compare/v3.4.5...v3.4.7)

---
updated-dependencies:
- dependency-name: sphinx-tabs
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 07:55:07 +02:00
Markus Heiser e275f8e18e [data] update searx.data - update_engine_traits.py
Patches generated by::

    make data.traits

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-15 06:37:01 +02:00
0xhtml 8b6a3f3e11 [enh] engine: mojeek - add language support
Improve region and language detection / all locale

Testing has shown the following behaviour for the different
default and empty values of Mojeeks parameters:

| param    | idx | value  | behaviour                 |
| -------- | --- | ------ | ------------------------- |
| region   |  0  | ''     | detect region based on IP |
| region   |  1  | 'none' | all regions               |
| language |  0  | ''     | all languages             |
2024-10-15 06:37:01 +02:00
Snoweuph 5b6f40414a [mod] engine gitea: compatible with modern gitea or forgejo
Without this patch the Gitea Search Engine is only partially compatible with
modern gitea or forgejo:

- Fixing some JSON Fields
- Using Repository Avatar when Available

To Verify My results you can look at the Modern API doc and results, its
available on all Gitea and Forgejo instance by Default.  Heres an Search API
result of Mine:

- https://git.euph.dev/api/v1/repos/search?q=ccna
2024-10-14 14:39:11 +02:00
Markus Heiser 7e8b330b3e [build] /static 2024-10-12 11:08:44 +02:00
Markus Heiser 2fbedc4316 [fix] simple theme: fix deformed result item
Setting ``box-sizing: border-box;`` [1] and ``width`` to fix deformed results
reported in [2].

[1] https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing
[2] https://github.com/searxng/searxng/issues/3892

Closes: #3892

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-12 11:08:44 +02:00
Émilien (perso) bafb92e646 reword join the searxng community 2024-10-12 11:08:00 +02:00
dependabot[bot] 1b8db63b33 [upd] pypi: Bump pydantic from 2.8.2 to 2.9.2
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.8.2 to 2.9.2.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.8.2...v2.9.2)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-11 18:39:35 +02:00
searxng-bot 5a32ee410b [l10n] update translations from Weblate
83a8d6627 - 2024-10-10 - Outbreak2096 <Outbreak2096@users.noreply.translate.codeberg.org>
c4474a60b - 2024-10-10 - return42 <return42@users.noreply.translate.codeberg.org>
a632dff03 - 2024-10-09 - bobolau <bobolau@users.noreply.translate.codeberg.org>
e8944e486 - 2024-10-08 - elek <elek@users.noreply.translate.codeberg.org>
2bd9c1229 - 2024-10-07 - AndersNordh <AndersNordh@users.noreply.translate.codeberg.org>
5cb441b60 - 2024-10-07 - return42 <return42@users.noreply.translate.codeberg.org>
aba724c87 - 2024-10-07 - return42 <return42@users.noreply.translate.codeberg.org>
92bea9f03 - 2024-10-08 - Linerly <Linerly@users.noreply.translate.codeberg.org>
f574a3a3f - 2024-10-08 - ghose <ghose@users.noreply.translate.codeberg.org>
4845ea7e2 - 2024-10-08 - Ricky-Tigg <Ricky-Tigg@users.noreply.translate.codeberg.org>
7d0b4d0d9 - 2024-10-07 - AndersNordh <AndersNordh@users.noreply.translate.codeberg.org>
6431436b2 - 2024-10-07 - AndersNordh <AndersNordh@users.noreply.translate.codeberg.org>
30e671f30 - 2024-10-07 - Fabio_Perri <Fabio_Perri@users.noreply.translate.codeberg.org>
6629d15cb - 2024-10-07 - AndersNordh <AndersNordh@users.noreply.translate.codeberg.org>
a3b4d53cc - 2024-10-05 - 0ko <0ko@users.noreply.translate.codeberg.org>
1763b224e - 2024-10-05 - Atul_Eterno <Atul_Eterno@users.noreply.translate.codeberg.org>
0ea2b49a8 - 2024-10-06 - return42 <return42@users.noreply.translate.codeberg.org>
837324445 - 2024-10-05 - Fjuro <fjuro@alius.cz>
eec53d3b1 - 2024-10-05 - SomeTr <SomeTr@users.noreply.translate.codeberg.org>
4cd76e531 - 2024-10-05 - Priit Jõerüüt <jrtcdbrg@users.noreply.translate.codeberg.org>
2024-10-11 18:33:17 +02:00
Allen 81aaca8f44 [mod] use quad9 dns for connectivity checks when lxc
https://www.quad9.net/about/
https://bgp.tools/as/42#whois
2024-10-10 07:05:46 +02:00
Markus Heiser f1f0dfd231 Revert "[fix] docker: alpine - install apk py3-pydantic-core"
This reverts commit 5332d3a0b8.
2024-10-07 13:18:54 +02:00
Markus Heiser 5332d3a0b8 [fix] docker: alpine - install apk py3-pydantic-core
Alpine Linux uses musl libc (instead of glibc). However, there is no pre-build
of the pydantic-core python package for musl lib on armv7.  Alternatively this
patch installs py3-pydantic-core from Alpine packages [1]

[1] https://pkgs.alpinelinux.org/package/edge/community/armv7/py3-pydantic-core

- closes: https://github.com/searxng/searxng/issues/3887

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-06 11:59:57 +02:00
Markus Heiser f00fa76eda [build] /static 2024-10-05 08:18:28 +02:00
Markus Heiser a631f77401 [mod] favicons: simplify RTL in template & CSS
Comes from a sughgestion in:

- https://github.com/searxng/searxng/pull/3727#issuecomment-2388998803

Suggested-by: Bnyro <bnyro@tutanota.com>
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-05 08:18:28 +02:00
Markus Heiser a7d02d4101 [doc] documentation of the favicons infrastructure
Run ``make docs.live`` and visit http://0.0.0.0:8000/admin/searx.favicons.html

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-05 08:18:28 +02:00
Markus Heiser 5ded9ada82 [mod] UI: add favicon's border- and background color to the definitons
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-05 08:18:28 +02:00
Markus Heiser 7ab577a1fb [mod] Revision of the favicon solution
All favicons implementations have been documented and moved to the Python
package:

    searx.favicons

There is a configuration (based on Pydantic) for the favicons and all its
components:

    searx.favicons.config

A solution for caching favicons has been implemented:

    searx.favicon.cache

If the favicon is already in the cache, the returned URL is a data URL [1]
(something like `data:image/png;base64,...`).  By generating a data url from
the FaviconCache, additional HTTP roundtripps via the favicon_proxy are saved:

    favicons.proxy.favicon_url

The favicon proxy service now sets a HTTP header "Cache-Control: max-age=...":

    favicons.proxy.favicon_proxy

The resolvers now also provide the mime type (data, mime):

    searx.favicon.resolvers

[1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-05 08:18:28 +02:00
Markus Heiser c49a2707c1 [mod] sqlitedb: access to SQLite databases a little more convenient.
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-10-05 08:18:28 +02:00
Brock Vojkovic e17d7632d0 [feat] add favicons to result urls 2024-10-05 08:18:28 +02:00
Markus Heiser 3581e1b85f [POC] increase efficiency of reg-expressions
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2024-06-21 18:49:34 +02:00
186 changed files with 8770 additions and 2147 deletions

View File

@ -1,5 +1,5 @@
name: "Checker"
on:
on: # yamllint disable-line rule:truthy
schedule:
- cron: "0 4 * * 5"
workflow_dispatch:

View File

@ -1,5 +1,5 @@
name: "Update searx.data"
on:
on: # yamllint disable-line rule:truthy
schedule:
- cron: "59 23 28 * *"
workflow_dispatch:

View File

@ -1,6 +1,6 @@
name: Integration
on:
on: # yamllint disable-line rule:truthy
push:
branches: ["master"]
pull_request:
@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-20.04]
python-version: ["3.9", "3.10", "3.11", "3.12",]
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- name: Checkout
uses: actions/checkout@v4
@ -45,14 +45,6 @@ jobs:
make V=1 gecko.driver
- name: Run tests
run: make V=1 ci.test
- name: Test coverage
run: make V=1 test.coverage
- name: Store coverage result
uses: actions/upload-artifact@v3
with:
name: coverage-${{ matrix.python-version }}
path: coverage/
retention-days: 60
themes:
name: Themes
@ -119,7 +111,7 @@ jobs:
BRANCH: gh-pages
FOLDER: dist/docs
CLEAN: true # Automatically remove deleted files from the deploy branch
SINGLE_COMMIT: True
SINGLE_COMMIT: true
COMMIT_MESSAGE: '[doc] build from commit ${{ github.sha }}'
babel:

View File

@ -1,5 +1,5 @@
name: "Security checks"
on:
on: # yamllint disable-line rule:truthy
schedule:
- cron: "42 05 * * *"
workflow_dispatch:

View File

@ -1,5 +1,5 @@
name: "Update translations"
on:
on: # yamllint disable-line rule:truthy
schedule:
- cron: "05 07 * * 5"
workflow_dispatch:

View File

@ -338,6 +338,7 @@ valid-metaclass-classmethod-first-arg=mcs
# Maximum number of arguments for function / method
max-args=8
max-positional-arguments=14
# Maximum number of attributes for a class (see R0902).
max-attributes=20

View File

@ -66,7 +66,7 @@ A user_, admin_ and developer_ handbook is available on the homepage_.
Contact
=======
Ask questions or just chat about SearXNG on
Ask questions or chat with the SearXNG community (this not a chatbot) on
IRC
`#searxng on libera.chat <https://web.libera.chat/?channel=#searxng>`_

View File

@ -15,6 +15,7 @@ Administrator documentation
installation-apache
update-searxng
answer-captcha
searx.favicons
searx.limiter
api
architecture

View File

@ -0,0 +1,251 @@
.. _favicons:
========
Favicons
========
.. sidebar:: warning
Don't activate the favicons before reading the documentation.
.. contents::
:depth: 2
:local:
:backlinks: entry
Activating the favicons in SearXNG is very easy, but this **generates a
significantly higher load** in the client/server communication and increases
resources needed on the server.
To mitigate these disadvantages, various methods have been implemented,
including a *cache*. The cache must be parameterized according to your own
requirements and maintained regularly.
To activate favicons in SearXNG's result list, set a default
``favicon_resolver`` in the :ref:`search <settings search>` settings:
.. code:: yaml
search:
favicon_resolver: "duckduckgo"
By default and without any extensions, SearXNG serves these resolvers:
- ``duckduckgo``
- ``allesedv``
- ``google``
- ``yandex``
With the above setting favicons are displayed, the user has the option to
deactivate this feature in his settings. If the user is to have the option of
selecting from several *resolvers*, a further setting is required / but this
setting will be discussed :ref:`later <register resolvers>` in this article,
first we have to setup the favicons cache.
Infrastructure
==============
The infrastructure for providing the favicons essentially consists of three
parts:
- :py:obj:`Favicons-Proxy <.favicons.proxy>` (aka *proxy*)
- :py:obj:`Favicons-Resolvers <.favicons.resolvers>` (aka *resolver*)
- :py:obj:`Favicons-Cache <.favicons.cache>` (aka *cache*)
To protect the privacy of users, the favicons are provided via a *proxy*. This
*proxy* is automatically activated with the above activation of a *resolver*.
Additional requests are required to provide the favicons: firstly, the *proxy*
must process the incoming requests and secondly, the *resolver* must make
outgoing requests to obtain the favicons from external sources.
A *cache* has been developed to massively reduce both, incoming and outgoing
requests. This *cache* is also activated automatically with the above
activation of a *resolver*. In its defaults, however, the *cache* is minimal
and not well suitable for a production environment!
.. _favicon cache setup:
Setting up the cache
====================
To parameterize the *cache* and more settings of the favicons infrastructure, a
TOML_ configuration is created in the file ``/etc/searxng/favicons.toml``.
.. code:: toml
[favicons]
cfg_schema = 1 # config's schema version no.
[favicons.cache]
db_url = "/var/cache/searxng/faviconcache.db" # default: "/tmp/faviconcache.db"
LIMIT_TOTAL_BYTES = 2147483648 # 2 GB / default: 50 MB
# HOLD_TIME = 5184000 # 60 days / default: 30 days
# BLOB_MAX_BYTES = 40960 # 40 KB / default 20 KB
# MAINTENANCE_MODE = "off" # default: "auto"
# MAINTENANCE_PERIOD = 600 # 10min / default: 1h
:py:obj:`cfg_schema <.FaviconConfig.cfg_schema>`:
Is required to trigger any processes required for future upgrades / don't
change it.
:py:obj:`cache.db_url <.FaviconCacheConfig.db_url>`:
The path to the (SQLite_) database file. The default path is in the `/tmp`_
folder, which is deleted on every reboot and is therefore unsuitable for a
production environment. The FHS_ provides the folder for the
application cache
The FHS_ provides the folder `/var/cache`_ for the cache of applications, so a
suitable storage location of SearXNG's caches is folder ``/var/cache/searxng``.
In container systems, a volume should be mounted for this folder and in a
standard installation (compare :ref:`create searxng user`), the folder must be
created and the user under which the SearXNG process is running must be given
write permission to this folder.
.. code:: bash
$ sudo mkdir /var/cache/searxng
$ sudo chown root:searxng /var/cache/searxng/
$ sudo chmod g+w /var/cache/searxng/
:py:obj:`cache.LIMIT_TOTAL_BYTES <.FaviconCacheConfig.LIMIT_TOTAL_BYTES>`:
Maximum of bytes stored in the cache of all blobs. The limit is only reached
at each maintenance interval after which the oldest BLOBs are deleted; the
limit is exceeded during the maintenance period.
.. attention::
If the maintenance period is too long or maintenance is switched
off completely, the cache grows uncontrollably.
SearXNG hosters can change other parameters of the cache as required:
- :py:obj:`cache.HOLD_TIME <.FaviconCacheConfig.HOLD_TIME>`
- :py:obj:`cache.BLOB_MAX_BYTES <.FaviconCacheConfig.BLOB_MAX_BYTES>`
Maintenance of the cache
------------------------
Regular maintenance of the cache is required! By default, regular maintenance
is triggered automatically as part of the client requests:
- :py:obj:`cache.MAINTENANCE_MODE <.FaviconCacheConfig.MAINTENANCE_MODE>` (default ``auto``)
- :py:obj:`cache.MAINTENANCE_PERIOD <.FaviconCacheConfig.MAINTENANCE_PERIOD>` (default ``6000`` / 1h)
As an alternative to maintenance as part of the client request process, it is
also possible to carry out maintenance using an external process. For example,
by creating a :man:`crontab` entry for maintenance:
.. code:: bash
$ python -m searx.favicons cache maintenance
The following command can be used to display the state of the cache:
.. code:: bash
$ python -m searx.favicons cache state
.. _favicon proxy setup:
Proxy configuration
===================
Most of the options of the :py:obj:`Favicons-Proxy <.favicons.proxy>` are
already set sensibly with settings from the :ref:`settings.yml <searxng
settings.yml>` and should not normally be adjusted.
.. code:: toml
[favicons.proxy]
max_age = 5184000 # 60 days / default: 7 days (604800 sec)
:py:obj:`max_age <.FaviconProxyConfig.max_age>`:
The `HTTP Cache-Control max-age`_ response directive indicates that the
response remains fresh until N seconds after the response is generated. This
setting therefore determines how long a favicon remains in the client's cache.
As a rule, in the favicons infrastructure of SearXNG's this setting only
affects favicons whose byte size exceeds :ref:`BLOB_MAX_BYTES <favicon cache
setup>` (the other favicons that are already in the cache are embedded as
`data URL`_ in the :py:obj:`generated HTML <.favicons.proxy.favicon_url>`,
which can greatly reduce the number of additional requests).
.. _register resolvers:
Register resolvers
------------------
A :py:obj:`resolver <.favicon.resolvers>` is a function that obtains the favicon
from an external source. The resolver functions available to the user are
registered with their fully qualified name (FQN_) in a ``resolver_map``.
If no ``resolver_map`` is defined in the ``favicon.toml``, the favicon
infrastructure of SearXNG generates this ``resolver_map`` automatically
depending on the ``settings.yml``. SearXNG would automatically generate the
following TOML configuration from the following YAML configuration:
.. code:: yaml
search:
favicon_resolver: "duckduckgo"
.. code:: toml
[favicons.proxy.resolver_map]
"duckduckgo" = "searx.favicons.resolvers.duckduckgo"
If this automatism is not desired, then (and only then) a separate
``resolver_map`` must be created. For example, to give the user two resolvers to
choose from, the following configuration could be used:
.. code:: toml
[favicons.proxy.resolver_map]
"duckduckgo" = "searx.favicons.resolvers.duckduckgo"
"allesedv" = "searx.favicons.resolvers.allesedv"
# "google" = "searx.favicons.resolvers.google"
# "yandex" = "searx.favicons.resolvers.yandex"
.. note::
With each resolver, the resource requirement increases significantly.
The number of resolvers increases:
- the number of incoming/outgoing requests and
- the number of favicons to be stored in the cache.
In the following we list the resolvers available in the core of SearXNG, but via
the FQN_ it is also possible to implement your own resolvers and integrate them
into the *proxy*:
- :py:obj:`searx.favicons.resolvers.duckduckgo`
- :py:obj:`searx.favicons.resolvers.allesedv`
- :py:obj:`searx.favicons.resolvers.google`
- :py:obj:`searx.favicons.resolvers.yandex`
.. _SQLite:
https://www.sqlite.org/
.. _FHS:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
.. _`/var/cache`:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s05.html
.. _`/tmp`:
https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
.. _TOML:
https://toml.io/en/
.. _HTTP Cache-Control max-age:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives
.. _data URL:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
.. _FQN: https://en.wikipedia.org/wiki/Fully_qualified_name

View File

@ -9,6 +9,7 @@
search:
safe_search: 0
autocomplete: ""
favicon_resolver: ""
default_lang: ""
ban_time_on_fail: 5
max_ban_time_on_fail: 120
@ -41,6 +42,11 @@
- ``qwant``
- ``wikipedia``
``favicon_resolver``:
To activate favicons in SearXNG's result list select a default
favicon-resolver, leave blank to turn off the feature. Don't activate the
favicons before reading the :ref:`Favicons documentation <favicons>`.
``default_lang``:
Default search language - leave blank to detect from browser information or
use codes from :origin:`searx/languages.py`.

View File

@ -127,6 +127,7 @@ extensions = [
"sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs
'myst_parser', # https://www.sphinx-doc.org/en/master/usage/markdown.html
'notfound.extension', # https://github.com/readthedocs/sphinx-notfound-page
'sphinxcontrib.autodoc_pydantic', # https://github.com/mansenfranzen/autodoc_pydantic
]
autodoc_default_options = {

View File

@ -0,0 +1,48 @@
.. _favicons source:
=================
Favicons (source)
=================
.. contents::
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.favicons
:members:
.. _favicons.config:
Favicons Config
===============
.. automodule:: searx.favicons.config
:members:
.. _favicons.proxy:
Favicons Proxy
==============
.. automodule:: searx.favicons.proxy
:members:
.. _favicons.resolver:
Favicons Resolver
=================
.. automodule:: searx.favicons.resolvers
:members:
.. _favicons.cache:
Favicons Cache
==============
.. automodule:: searx.favicons.cache
:members:

View File

@ -0,0 +1,8 @@
.. _sqlite db:
=========
SQLite DB
=========
.. automodule:: searx.sqlitedb
:members:

2
manage
View File

@ -57,7 +57,7 @@ while IFS= read -r line; do
if [ "$line" != "tests/unit/settings/syntaxerror_settings.yml" ]; then
YAMLLINT_FILES+=("$line")
fi
done <<< "$(git ls-files './tests/*.yml' './searx/*.yml' './utils/templates/etc/searxng/*.yml')"
done <<< "$(git ls-files './tests/*.yml' './searx/*.yml' './utils/templates/etc/searxng/*.yml' '.github/*.yml' '.github/*/*.yml')"
RST_FILES=(
'README.rst'

View File

@ -2,14 +2,14 @@ mock==5.1.0
nose2[coverage_plugin]==0.15.1
cov-core==1.15.0
black==24.3.0
pylint==3.2.7
pylint==3.3.1
splinter==0.21.0
selenium==4.25.0
Pallets-Sphinx-Themes==2.1.3
selenium==4.26.1
Pallets-Sphinx-Themes==2.3.0
Sphinx==7.4.7
sphinx-issues==4.1.0
sphinx-issues==5.0.0
sphinx-jinja==2.0.2
sphinx-tabs==3.4.5
sphinx-tabs==3.4.7
sphinxcontrib-programoutput==0.17
sphinx-autobuild==2024.10.3
sphinx-notfound-page==1.0.4
@ -21,3 +21,4 @@ wlc==1.15
coloredlogs==15.0.1
docutils>=0.21.2
parameterized==0.9.0
autodoc_pydantic==2.2.0

View File

@ -1,7 +1,7 @@
certifi==2024.8.30
babel==2.16.0
flask-babel==4.0.0
flask==3.0.3
flask==3.1.0
jinja2==3.1.4
lxml==5.3.0
pygments==2.18.0
@ -9,10 +9,13 @@ python-dateutil==2.9.0.post0
pyyaml==6.0.2
httpx[http2]==0.24.1
Brotli==1.1.0
uvloop==0.20.0
uvloop==0.21.0
httpx-socks[asyncio]==0.7.7
setproctitle==1.3.3
redis==5.0.8
markdown-it-py==3.0.0
fasttext-predict==0.9.2.2
pytomlpp==1.0.13; python_version < '3.11'
tomli==2.0.2; python_version < '3.11'
msgspec==0.18.6
eval_type_backport; python_version < '3.9'
typer-slim==0.13.0

View File

@ -14,17 +14,7 @@ import typing
import logging
import pathlib
try:
import tomllib
pytomlpp = None
USE_TOMLLIB = True
except ImportError:
import pytomlpp
tomllib = None
USE_TOMLLIB = False
from ..compat import tomllib
__all__ = ['Config', 'UNSET', 'SchemaIssue']
@ -183,8 +173,6 @@ class Config:
def toml_load(file_name):
if USE_TOMLLIB:
# Python >= 3.11
try:
with open(file_name, "rb") as f:
return tomllib.load(f)
@ -192,13 +180,6 @@ def toml_load(file_name):
msg = str(exc).replace('\t', '').replace('\n', ' ')
log.error("%s: %s", file_name, msg)
raise
# fallback to pytomlpp for Python < 3.11
try:
return pytomlpp.load(file_name)
except pytomlpp.DecodeError as exc:
msg = str(exc).replace('\t', '').replace('\n', ' ')
log.error("%s: %s", file_name, msg)
raise
# working with dictionaries

18
searx/compat.py Normal file
View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Compatibility with older versions"""
# pylint: disable=unused-import
__all__ = [
"tomllib",
]
import sys
# TOML (lib) compatibility
# ------------------------
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib

File diff suppressed because it is too large Load Diff

View File

@ -82,7 +82,7 @@
"af": "Albanese lek",
"ar": "ليك ألباني",
"bg": "Албански лек",
"ca": "lek",
"ca": "Lek (moneda)",
"cs": "Albánský lek",
"cy": "Lek",
"da": "Lek",
@ -383,6 +383,7 @@
"nl": "Azerbeidzjaanse manat",
"oc": "Manat",
"pa": "ਅਜ਼ਰਬਾਈਜਾਨੀ ਮਨਾਤ",
"pap": "Manat Azerbaijano",
"pl": "Manat azerski",
"pt": "Manat azeri",
"ro": "Manat azer",
@ -606,6 +607,7 @@
"pt": "Franco do Burúndi",
"ro": "franc burundez",
"ru": "бурундийский франк",
"sk": "Burundský frank",
"sl": "burundijski frank",
"sr": "бурундски франак",
"sv": "Burundisk franc",
@ -1327,6 +1329,7 @@
"pl": "escudo Zielonego Przylądka",
"pt": "escudo cabo-verdiano",
"ru": "Эскудо Кабо-Верде",
"sk": "Kapverdské escudo",
"sl": "zelenortski eskudo",
"sr": "зеленортски ескудо",
"sv": "Kapverdisk escudo",
@ -1405,6 +1408,7 @@
"pl": "frank Dżibuti",
"pt": "franco do Jibuti",
"ru": "Франк Джибути",
"sk": "Džibutský frank",
"sr": "џибутски франак",
"sv": "Djiboutisk franc",
"tr": "Cibuti frangı",
@ -1518,6 +1522,7 @@
"pt": "dinar argelino",
"ro": "Dinar algerian",
"ru": "алжирский динар",
"sk": "Alžírský dinár",
"sl": "alžirski dinar",
"sr": "алжирски динар",
"sv": "Algerisk dinar",
@ -1969,6 +1974,7 @@
"pl": "frank gwinejski",
"pt": "Franco da Guiné",
"ru": "Гвинейский франк",
"sk": "Guinejský frank",
"sl": "gvinejski frank",
"sr": "гвинејски франак",
"sv": "Guinesisk franc",
@ -2689,6 +2695,7 @@
"pt": "Franco comoriano",
"ro": "Franc comorian",
"ru": "Франк Комор",
"sk": "Komorský frank",
"sr": "коморски франак",
"sv": "Komoransk franc",
"tr": "Komor frangı",
@ -2986,6 +2993,7 @@
"pt": "rúpia do Sri Lanka",
"ru": "ланкийская рупия",
"si": "ශ්රී ලංකා රුපියල",
"sk": "Srílanská rupia",
"sl": "šrilanška rupija",
"sr": "шриланчанска рупија",
"sv": "Lankesisk rupie",
@ -3059,7 +3067,7 @@
"uk": "Лоті"
},
"LYD": {
"ar": "دينار ليبي",
"ar": "دينار ذهبي",
"bg": "Либийски динар",
"ca": "dinar libi",
"cs": "Libyjský dinár",
@ -3121,6 +3129,7 @@
"pt": "Dirham marroquino",
"ro": "Dirham marocan",
"ru": "марокканский дирхам",
"sk": "Marocký dirham",
"sl": "maroški dirham",
"sr": "марокански дирхам",
"sv": "Marockansk dirham",
@ -3140,6 +3149,7 @@
"et": "Moldova leu",
"fi": "Moldovan leu",
"fr": "leu moldave",
"gl": "leu moldovo",
"he": "לאו מולדובני",
"hr": "moldavski lej",
"hu": "moldován lej",
@ -3371,6 +3381,7 @@
"pl": "Ugija",
"pt": "Uguia",
"ru": "Мавританская угия",
"sk": "Mauritánska ukíjá",
"sr": "мауританска огија",
"sv": "Mauretansk ouguiya",
"tr": "Ugiya",
@ -3816,6 +3827,7 @@
"sl": "novozelandski dolar",
"sr": "новозеландски долар",
"sv": "Nyzeeländsk dollar",
"th": "ดอลลาร์นิวซีแลนด์",
"tr": "Yeni Zelanda doları",
"uk": "новозеландський долар",
"vi": "Đô la New Zealand"
@ -5386,12 +5398,14 @@
"ja": "スム",
"ko": "우즈베키스탄 숨",
"lt": "Uzbekijos sumas",
"lv": "Uzbekistānas soms",
"nl": "Oezbeekse sum",
"pa": "ਉਜ਼ਬੇਕਿਸਤਾਨੀ ਸੋਮ",
"pl": "Sum",
"pt": "som usbeque",
"ro": "Som uzbec",
"ru": "узбекский сум",
"sk": "Uzbecký som",
"sr": "узбекистански сом",
"sv": "Uzbekistansk som",
"tr": "Özbekistan somu",
@ -5645,7 +5659,7 @@
"eo": "specialaj rajtoj de enspezo",
"es": "Derechos Especiales de Giro",
"eu": "igorpen eskubide bereziak",
"fi": "Erityisnosto-oikeus",
"fi": "erityisnosto-oikeus",
"fr": "droits de tirage spéciaux",
"hr": "Posebna prava vučenja",
"hu": "különleges lehívási jog",
@ -5655,6 +5669,7 @@
"ko": "특별인출권",
"lt": "Specialiosios skolinimosi teisės",
"lv": "Speciālās aizņēmuma tiesības",
"ms": "hak pengeluaran khas",
"nl": "speciale trekkingsrechten",
"oc": "Drechs de tiratge Especials",
"pl": "specjalne prawa ciągnienia",
@ -5837,7 +5852,7 @@
"lt": "Randas",
"lv": "Dienvidāfrikas rands",
"ml": "സൗത്ത് ആഫ്രിക്കൻ റാൻഡ്",
"ms": "Rand",
"ms": "Rand Afrika Selatan",
"nl": "Zuid-Afrikaanse rand",
"oc": "Rand sudafrican",
"pl": "Rand",
@ -5900,6 +5915,7 @@
"ko": "짐바브웨 골드",
"nl": "Zimbabwe Gold",
"pl": "Złoto Zimbabwe",
"pt": "Ouro do Zimbábue",
"ru": "зимбабвийский золотой",
"sk": "zimbabwiansky zlatý",
"sl": "zimbabvejski gold",
@ -7817,6 +7833,7 @@
"eritrese nakfa": "ERN",
"erityinen nosto oikeus": "XDR",
"erityiset nosto oikeudet": "XDR",
"erityisnosto oikeudet": "XDR",
"erityisnosto oikeus": "XDR",
"ermeni dramı": "AMD",
"ermenistan dramı": "AMD",
@ -8372,6 +8389,8 @@
"haitský gourde": "HTG",
"haïtiaanse gourde": "HTG",
"hak penarikan khusus": "XDR",
"hak pengeluaran khas": "XDR",
"hak pengeluaran khusus": "XDR",
"halalas": "SAR",
"hegoafrikar rand": "ZAR",
"heller": "CZK",
@ -9115,6 +9134,7 @@
"leu da roménia": "RON",
"leu da romênia": "RON",
"leu de moldàvia": "MDL",
"leu de moldova": "MDL",
"leu moldau": "MDL",
"leu moldave": "MDL",
"leu moldavo": "MDL",
@ -9122,6 +9142,7 @@
"leu moldofa": "MDL",
"leu moldova": "MDL",
"leu moldovenesc": "MDL",
"leu moldovo": "MDL",
"leu romanès": "RON",
"leu romanés": "RON",
"leu romanian": "RON",
@ -9437,6 +9458,7 @@
"manat azerbaijandar": "AZN",
"manat azerbaijanês": "AZN",
"manat azerbaijano": "AZN",
"manat azerbaitjanés": "AZN",
"manat azerbaiyano": "AZN",
"manat azerbaïdjanais": "AZN",
"manat azerbejdżański": "AZN",
@ -9520,6 +9542,7 @@
"mauritanijska ouguja": "MRU",
"mauritanijska uguija": "MRU",
"mauritániai ouguiya": "MRU",
"mauritánska ukíjá": "MRU",
"mauritánská ukíjá": "MRU",
"mauritānijas oguja": "MRU",
"mauritiaanse roepee": "MUR",
@ -9985,6 +10008,7 @@
"ouguiya mauritana": "MRU",
"ouguiya mauritanien": "MRU",
"ouguiya mawritania": "MRU",
"ouro do zimbábue": "ZWG",
"örmény dram": "AMD",
"östkaribisk dollar": "XCD",
"özbekistan somu": "UZS",
@ -10796,6 +10820,7 @@
"salomona dolaro": "SBD",
"salomondollar": "SBD",
"salomonen dollar": "SBD",
"salomoninsaarten dollari": "SBD",
"salomonsaarten dollari": "SBD",
"salomonskootočni dolar": "SBD",
"salüng": "THB",
@ -11152,6 +11177,7 @@
"srilankansk rupee": "LKR",
"srilankanske rupee": "LKR",
"srí lanka i rúpia": "LKR",
"srílanská rupia": "LKR",
"srílanská rupie": "LKR",
"srpski dinar": "RSD",
"ssp": "SSP",
@ -11415,6 +11441,7 @@
"tengue": "KZT",
"tengue cazaque": "KZT",
"teňňe": "TMT",
"tetri": "GEL",
"thai baht": "THB",
"thai bát": "THB",
"thailandiar baht": "THB",
@ -11539,10 +11566,10 @@
"turkisk lira": "TRY",
"turkiska lira": "TRY",
"turkmeense manat": "TMT",
"turkmen manat": "TMT",
"turkmena manato": "TMT",
"turkmenistan manat": "TMT",
"turkmenistan new manat": "TMT",
"turkmenistani manat": "TMT",
"turkmenistani new manat": "TMT",
"turkmenistanin manat": "TMT",
"turkmenistansk manat": "TMT",
@ -11708,6 +11735,7 @@
"uzbekistano sumas": "UZS",
"uzbekistansk som": "UZS",
"uzbekistanski som": "UZS",
"uzbekistānas soms": "UZS",
"uzs": "UZS",
"új zélandi dollár": "NZD",
"ürdün dinarı": "JOD",
@ -11861,6 +11889,7 @@
"yuan cinese": "CNY",
"yuan renmimbi": "CNY",
"yuan renminbi": "CNY",
"yuan rmb": "CNY",
"yuans": "CNY",
"yuán chino": "CNY",
"z$": "ZWL",
@ -13734,6 +13763,7 @@
"دينار بحريني": "BHD",
"دينار تونسي": "TND",
"دينار جزائري": "DZD",
"دينار ذهبي": "LYD",
"دينار سوداني": "SDG",
"دينار صربي": "RSD",
"دينار عراقي": "IQD",
@ -14326,6 +14356,7 @@
"USD",
"TWD"
],
"ดอลลาร์นิวซีแลนด์": "NZD",
"ดอลลาร์บรูไน": "BND",
"ดอลลาร์สหรัฐ": "USD",
"ดอลลาร์สิงคโปร์": "SGD",
@ -14998,6 +15029,7 @@
"ボツワナ・プラ": "BWP",
"ボリバル・ソベラノ": "VES",
"ボリビアーノ": "BOB",
"ポンド・スターリング": "GBP",
"ポーランド・ズウォティ": [
"PLZ",
"PLN"
@ -15063,7 +15095,6 @@
"中華人民共和国の通貨": "CNY",
"中部アフリカcfaフラン": "XAF",
"人民元": "CNY",
"人民币": "CNY",
"人民幣": "CNY",
"元": [
"HKD",

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
"versions": [
"130.0",
"129.0"
"132.0",
"131.0"
]
}

View File

@ -832,7 +832,7 @@
"Q104907390": {
"si_name": "Q182429",
"symbol": "nmi/h",
"to_si_factor": 0.514444
"to_si_factor": 0.5144444444444445
},
"Q104907398": {
"si_name": "Q215571",
@ -1336,7 +1336,7 @@
},
"Q106636307": {
"si_name": "Q80842107",
"symbol": "μS/cm",
"symbol": "μS/cm-1",
"to_si_factor": 0.0001
},
"Q106639711": {
@ -4142,7 +4142,7 @@
"Q23931103": {
"si_name": "Q25343",
"symbol": "nmi²",
"to_si_factor": 3434290.0120544
"to_si_factor": 3429904.0
},
"Q239830": {
"si_name": "Q3395194",

View File

@ -34,10 +34,10 @@ Implementations
"""
from typing import List, Dict, Any, Optional
from urllib.parse import quote
from urllib.parse import urlencode
from lxml import html
from searx.utils import extract_text, eval_xpath, eval_xpath_list
from searx.utils import extract_text, eval_xpath, eval_xpath_getindex, eval_xpath_list
from searx.enginelib.traits import EngineTraits
from searx.data import ENGINE_TRAITS
@ -53,7 +53,7 @@ about: Dict[str, Any] = {
# engine dependent config
categories: List[str] = ["files"]
paging: bool = False
paging: bool = True
# search-url
base_url: str = "https://annas-archive.org"
@ -99,9 +99,18 @@ def init(engine_settings=None): # pylint: disable=unused-argument
def request(query, params: Dict[str, Any]) -> Dict[str, Any]:
q = quote(query)
lang = traits.get_language(params["language"], traits.all_locale) # type: ignore
params["url"] = base_url + f"/search?lang={lang or ''}&content={aa_content}&ext={aa_ext}&sort={aa_sort}&q={q}"
args = {
'lang': lang,
'content': aa_content,
'ext': aa_ext,
'sort': aa_sort,
'q': query,
'page': params['pageno'],
}
# filter out None and empty values
filtered_args = dict((k, v) for k, v in args.items() if v)
params["url"] = f"{base_url}/search?{urlencode(filtered_args)}"
return params
@ -128,12 +137,12 @@ def response(resp) -> List[Dict[str, Optional[str]]]:
def _get_result(item):
return {
'template': 'paper.html',
'url': base_url + item.xpath('./@href')[0],
'url': base_url + extract_text(eval_xpath_getindex(item, './@href', 0)),
'title': extract_text(eval_xpath(item, './/h3/text()[1]')),
'publisher': extract_text(eval_xpath(item, './/div[contains(@class, "text-sm")]')),
'authors': [extract_text(eval_xpath(item, './/div[contains(@class, "italic")]'))],
'content': extract_text(eval_xpath(item, './/div[contains(@class, "text-xs")]')),
'thumbnail': item.xpath('.//img/@src')[0],
'thumbnail': extract_text(eval_xpath_getindex(item, './/img/@src', 0, default=None), allow_none=True),
}

View File

@ -18,13 +18,13 @@ from searx import (
)
from searx.utils import (
eval_xpath,
eval_xpath_getindex,
extract_text,
)
from searx.network import get # see https://github.com/searxng/searxng/issues/762
from searx import redisdb
from searx.enginelib.traits import EngineTraits
from searx.utils import extr
from searx.exceptions import SearxEngineCaptchaException
if TYPE_CHECKING:
import logging
@ -53,31 +53,33 @@ paging = True
time_range_support = True
safesearch = True # user can't select but the results are filtered
url = 'https://lite.duckduckgo.com/lite/'
# url_ping = 'https://duckduckgo.com/t/sl_l'
url = "https://html.duckduckgo.com/html"
time_range_dict = {'day': 'd', 'week': 'w', 'month': 'm', 'year': 'y'}
form_data = {'v': 'l', 'api': 'd.js', 'o': 'json'}
__CACHE = []
def cache_vqd(query, value):
def _cache_key(data: dict):
return 'SearXNG_ddg_web_vqd' + redislib.secret_hash(f"{data['q']}//{data['kl']}")
def cache_vqd(data: dict, value):
"""Caches a ``vqd`` value from a query."""
c = redisdb.client()
if c:
logger.debug("cache vqd value: %s", value)
key = 'SearXNG_ddg_web_vqd' + redislib.secret_hash(query)
c.set(key, value, ex=600)
c.set(_cache_key(data), value, ex=600)
else:
logger.debug("MEM cache vqd value: %s", value)
if len(__CACHE) > 100: # cache vqd from last 100 queries
__CACHE.pop(0)
__CACHE.append((_cache_key(data), value))
def get_vqd(query):
"""Returns the ``vqd`` that fits to the *query*. If there is no ``vqd`` cached
(:py:obj:`cache_vqd`) the query is sent to DDG to get a vqd value from the
response.
.. hint::
If an empty string is returned there are no results for the ``query`` and
therefore no ``vqd`` value.
def get_vqd(data):
"""Returns the ``vqd`` that fits to the *query* (``data`` from HTTP POST).
DDG's bot detection is sensitive to the ``vqd`` value. For some search terms
(such as extremely long search terms that are often sent by bots), no ``vqd``
@ -105,28 +107,23 @@ def get_vqd(query):
- DuckDuckGo News: ``https://duckduckgo.com/news.js??q=...&vqd=...``
"""
key = _cache_key(data)
value = None
c = redisdb.client()
if c:
key = 'SearXNG_ddg_web_vqd' + redislib.secret_hash(query)
value = c.get(key)
if value or value == b'':
value = value.decode('utf-8')
logger.debug("re-use cached vqd value: %s", value)
logger.debug("re-use CACHED vqd value: %s", value)
return value
query_url = 'https://duckduckgo.com/?' + urlencode({'q': query})
res = get(query_url)
doc = lxml.html.fromstring(res.text)
for script in doc.xpath("//script[@type='text/javascript']"):
script = script.text
if 'vqd="' in script:
value = extr(script, 'vqd="', '"')
break
logger.debug("new vqd value: '%s'", value)
if value is not None:
cache_vqd(query, value)
else:
for k, value in __CACHE:
if k == key:
logger.debug("MEM re-use CACHED vqd value: %s", value)
return value
return None
def get_ddg_lang(eng_traits: EngineTraits, sxng_locale, default='en_US'):
@ -154,9 +151,10 @@ def get_ddg_lang(eng_traits: EngineTraits, sxng_locale, default='en_US'):
.. hint::
`DDG-lite <https://lite.duckduckgo.com/lite>`__ does not offer a language
selection to the user, only a region can be selected by the user
(``eng_region`` from the example above). DDG-lite stores the selected
`DDG-lite <https://lite.duckduckgo.com/lite>`__ and the *no Javascript*
page https://html.duckduckgo.com/html do not offer a language selection
to the user, only a region can be selected by the user (``eng_region``
from the example above). DDG-lite and *no Javascript* store the selected
region in a cookie::
params['cookies']['kl'] = eng_region # 'ar-es'
@ -240,10 +238,27 @@ def request(query, params):
query = quote_ddg_bangs(query)
# request needs a vqd argument
vqd = get_vqd(query)
if len(query) >= 500:
# DDG does not accept queries with more than 499 chars
params["url"] = None
return
# Advanced search syntax ends in CAPTCHA
# https://duckduckgo.com/duckduckgo-help-pages/results/syntax/
query = " ".join(
[
x.removeprefix("site:").removeprefix("intitle:").removeprefix("inurl:").removeprefix("filetype:")
for x in query.split()
]
)
eng_region = traits.get_region(params['searxng_locale'], traits.all_locale)
if eng_region == "wt-wt":
# https://html.duckduckgo.com/html sets an empty value for "all".
eng_region = ""
params['data']['kl'] = eng_region
params['cookies']['kl'] = eng_region
# eng_lang = get_ddg_lang(traits, params['searxng_locale'])
params['url'] = url
@ -251,45 +266,82 @@ def request(query, params):
params['data']['q'] = query
# The API is not documented, so we do some reverse engineering and emulate
# what https://lite.duckduckgo.com/lite/ does when you press "next Page"
# link again and again ..
# what https://html.duckduckgo.com/html does when you press "next Page" link
# again and again ..
params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
params['data']['vqd'] = vqd
# initial page does not have an offset
params['headers']['Sec-Fetch-Dest'] = "document"
params['headers']['Sec-Fetch-Mode'] = "navigate" # at least this one is used by ddg's bot detection
params['headers']['Sec-Fetch-Site'] = "same-origin"
params['headers']['Sec-Fetch-User'] = "?1"
# Form of the initial search page does have empty values in the form
if params['pageno'] == 1:
params['data']['b'] = ""
params['data']['df'] = ''
if params['time_range'] in time_range_dict:
params['data']['df'] = time_range_dict[params['time_range']]
params['cookies']['df'] = time_range_dict[params['time_range']]
if params['pageno'] == 2:
# second page does have an offset of 20
offset = (params['pageno'] - 1) * 20
params['data']['s'] = offset
params['data']['dc'] = offset + 1
elif params['pageno'] > 2:
# third and following pages do have an offset of 20 + n*50
offset = 20 + (params['pageno'] - 2) * 50
params['data']['s'] = offset
params['data']['dc'] = offset + 1
# initial page does not have additional data in the input form
if params['pageno'] > 1:
# initial page does not have these additional data in the input form
params['data']['o'] = form_data.get('o', 'json')
params['data']['api'] = form_data.get('api', 'd.js')
params['data']['nextParams'] = form_data.get('nextParams', '')
params['data']['v'] = form_data.get('v', 'l')
params['headers']['Referer'] = 'https://lite.duckduckgo.com/'
params['headers']['Referer'] = url
params['data']['kl'] = eng_region
params['cookies']['kl'] = eng_region
# from here on no more params['data'] shuld be set, since this dict is
# needed to get a vqd value from the cache ..
params['data']['df'] = ''
if params['time_range'] in time_range_dict:
params['data']['df'] = time_range_dict[params['time_range']]
params['cookies']['df'] = time_range_dict[params['time_range']]
vqd = get_vqd(params['data'])
# Certain conditions must be met in order to call up one of the
# following pages ...
if vqd:
params['data']['vqd'] = vqd # follow up pages / requests needs a vqd argument
else:
# Don't try to call follow up pages without a vqd value. DDG
# recognizes this as a request from a bot. This lowers the
# reputation of the SearXNG IP and DDG starts to activate CAPTCHAs.
params["url"] = None
return
if params['searxng_locale'].startswith("zh"):
# Some locales (at least China) do not have a "next page" button and ddg
# will return a HTTP/2 403 Forbidden for a request of such a page.
params["url"] = None
return
logger.debug("param data: %s", params['data'])
logger.debug("param cookies: %s", params['cookies'])
return params
def is_ddg_captcha(dom):
"""In case of CAPTCHA ddg response its own *not a Robot* dialog and is not
redirected to a CAPTCHA page."""
return bool(eval_xpath(dom, "//form[@id='challenge-form']"))
def response(resp):
@ -300,38 +352,40 @@ def response(resp):
results = []
doc = lxml.html.fromstring(resp.text)
result_table = eval_xpath(doc, '//html/body/form/div[@class="filters"]/table')
if is_ddg_captcha(doc):
# set suspend time to zero is OK --> ddg does not block the IP
raise SearxEngineCaptchaException(suspended_time=0, message=f"CAPTCHA ({resp.search_params['data'].get('kl')})")
if len(result_table) == 2:
# some locales (at least China) does not have a "next page" button and
# the layout of the HTML tables is different.
result_table = result_table[1]
elif not len(result_table) >= 3:
# no more results
return []
else:
result_table = result_table[2]
# update form data from response
form = eval_xpath(doc, '//html/body/form/div[@class="filters"]/table//input/..')
form = eval_xpath(doc, '//input[@name="vqd"]/..')
if len(form):
# some locales (at least China) does not have a "next page" button
form = form[0]
form_data['v'] = eval_xpath(form, '//input[@name="v"]/@value')[0]
form_data['api'] = eval_xpath(form, '//input[@name="api"]/@value')[0]
form_data['o'] = eval_xpath(form, '//input[@name="o"]/@value')[0]
logger.debug('form_data: %s', form_data)
form_vqd = eval_xpath(form, '//input[@name="vqd"]/@value')[0]
tr_rows = eval_xpath(result_table, './/tr')
# In the last <tr> is the form of the 'previous/next page' links
tr_rows = tr_rows[:-1]
cache_vqd(resp.search_params["data"], form_vqd)
len_tr_rows = len(tr_rows)
offset = 0
# just select "web-result" and ignore results of class "result--ad result--ad--small"
for div_result in eval_xpath(doc, '//div[@id="links"]/div[contains(@class, "web-result")]'):
zero_click_info_xpath = '//html/body/form/div/table[2]/tr[2]/td/text()'
item = {}
title = eval_xpath(div_result, './/h2/a')
if not title:
# this is the "No results." item in the result list
continue
item["title"] = extract_text(title)
item["url"] = eval_xpath(div_result, './/h2/a/@href')[0]
item["content"] = extract_text(eval_xpath(div_result, './/a[contains(@class, "result__snippet")]')[0])
results.append(item)
zero_click_info_xpath = '//div[@id="zero_click_abstract"]'
zero_click = extract_text(eval_xpath(doc, zero_click_info_xpath)).strip()
if zero_click and "Your IP address is" not in zero_click and "Your user agent:" not in zero_click:
if zero_click and (
"Your IP address is" not in zero_click
and "Your user agent:" not in zero_click
and "URL Decoded:" not in zero_click
):
current_query = resp.search_params["data"].get("q")
results.append(
@ -341,33 +395,6 @@ def response(resp):
}
)
while len_tr_rows >= offset + 4:
# assemble table rows we need to scrap
tr_title = tr_rows[offset]
tr_content = tr_rows[offset + 1]
offset += 4
# ignore sponsored Adds <tr class="result-sponsored">
if tr_content.get('class') == 'result-sponsored':
continue
a_tag = eval_xpath_getindex(tr_title, './/td//a[@class="result-link"]', 0, None)
if a_tag is None:
continue
td_content = eval_xpath_getindex(tr_content, './/td[@class="result-snippet"]', 0, None)
if td_content is None:
continue
results.append(
{
'title': a_tag.text_content(),
'content': extract_text(td_content),
'url': a_tag.get('href'),
}
)
return results

View File

@ -1,7 +1,8 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Engine to search in collaborative software platforms based on Gitea_.
"""Engine to search in collaborative software platforms based on Gitea_ or Forgejo_.
.. _Gitea: https://about.gitea.com/
.. _Forgejo: https://forgejo.org/
Configuration
=============
@ -23,6 +24,11 @@ Optional settings are:
base_url: https://gitea.com
shortcut: gitea
- name: forgejo.com
engine: gitea
base_url: https://code.forgejo.org
shortcut: forgejo
If you would like to use additional instances, just configure new engines in the
:ref:`settings <settings engine>` and set the ``base_url``.
@ -95,13 +101,14 @@ def response(resp):
'url': item.get('html_url'),
'title': item.get('full_name'),
'content': ' / '.join(content),
'img_src': item.get('owner', {}).get('avatar_url'),
# Use Repository Avatar and fall back to Owner Avatar if not set.
'thumbnail': item.get('avatar_url') or item.get('owner', {}).get('avatar_url'),
'package_name': item.get('name'),
'maintainer': item.get('owner', {}).get('login'),
'maintainer': item.get('owner', {}).get('username'),
'publishedDate': parser.parse(item.get("updated_at") or item.get("created_at")),
'tags': item.get('topics', []),
'popularity': item.get('stargazers_count'),
'homepage': item.get('homepage'),
'popularity': item.get('stars_count'),
'homepage': item.get('website'),
'source_code_url': item.get('clone_url'),
}
)

View File

@ -62,7 +62,7 @@ filter_mapping = {0: 'off', 1: 'medium', 2: 'high'}
results_xpath = './/div[contains(@jscontroller, "SC7lYd")]'
title_xpath = './/a/h3[1]'
href_xpath = './/a[h3]/@href'
content_xpath = './/div[@data-sncf="1"]'
content_xpath = './/div[contains(@data-sncf, "1")]'
# Suggestions are links placed in a *card-section*, we extract only the text
# from the links not the links itself.

View File

@ -57,7 +57,11 @@ def request(query, params):
if params['time_range']:
search_type = 'search_by_date'
timestamp = (datetime.now() - relativedelta(**{f"{params['time_range']}s": 1})).timestamp()
timestamp = (
# pylint: disable=unexpected-keyword-arg
datetime.now()
- relativedelta(**{f"{params['time_range']}s": 1}) # type: ignore
).timestamp()
query_params["numericFilters"] = f"created_at_i>{timestamp}"
params["url"] = f"{base_url}/{search_type}?{urlencode(query_params)}"

View File

@ -1,12 +1,15 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Mojeek (general, images, news)"""
from typing import TYPE_CHECKING
from datetime import datetime
from urllib.parse import urlencode
from lxml import html
from dateutil.relativedelta import relativedelta
from searx.utils import eval_xpath, eval_xpath_list, extract_text
from searx.enginelib.traits import EngineTraits
about = {
'website': 'https://mojeek.com',
@ -42,6 +45,18 @@ news_url_xpath = './/h2/a/@href'
news_title_xpath = './/h2/a'
news_content_xpath = './/p[@class="s"]'
language_param = 'lb'
region_param = 'arc'
_delta_kwargs = {'day': 'days', 'week': 'weeks', 'month': 'months', 'year': 'years'}
if TYPE_CHECKING:
import logging
logger = logging.getLogger()
traits: EngineTraits
def init(_):
if search_type not in ('', 'images', 'news'):
@ -53,13 +68,16 @@ def request(query, params):
'q': query,
'safe': min(params['safesearch'], 1),
'fmt': search_type,
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 == '':
args['s'] = 10 * (params['pageno'] - 1)
if params['time_range'] and search_type != 'images':
args["since"] = (datetime.now() - relativedelta(**{f"{params['time_range']}s": 1})).strftime("%Y%m%d")
kwargs = {_delta_kwargs[params['time_range']]: 1}
args["since"] = (datetime.now() - relativedelta(**kwargs)).strftime("%Y%m%d") # type: ignore
logger.debug(args["since"])
params['url'] = f"{base_url}/search?{urlencode(args)}"
@ -94,7 +112,7 @@ def _image_results(dom):
'template': 'images.html',
'url': extract_text(eval_xpath(result, image_url_xpath)),
'title': extract_text(eval_xpath(result, image_title_xpath)),
'img_src': base_url + extract_text(eval_xpath(result, image_img_src_xpath)),
'img_src': base_url + extract_text(eval_xpath(result, image_img_src_xpath)), # type: ignore
'content': '',
}
)
@ -130,3 +148,31 @@ def response(resp):
return _news_results(dom)
raise ValueError(f"Invalid search type {search_type}")
def fetch_traits(engine_traits: EngineTraits):
# pylint: disable=import-outside-toplevel
from searx import network
from searx.locales import get_official_locales, region_tag
from babel import Locale, UnknownLocaleError
import contextlib
resp = network.get(base_url + "/preferences", headers={'Accept-Language': 'en-US,en;q=0.5'})
dom = html.fromstring(resp.text) # type: ignore
languages = eval_xpath_list(dom, f'//select[@name="{language_param}"]/option/@value')
engine_traits.custom['language_all'] = languages[0]
for code in languages[1:]:
with contextlib.suppress(UnknownLocaleError):
locale = Locale(code)
engine_traits.languages[locale.language] = code
regions = eval_xpath_list(dom, f'//select[@name="{region_param}"]/option/@value')
engine_traits.custom['region_all'] = regions[1]
for code in regions[2:]:
for locale in get_official_locales(code, engine_traits.languages):
engine_traits.regions[region_tag(locale)] = code

View File

@ -0,0 +1,71 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Open library (books)
"""
from urllib.parse import urlencode
import re
from dateutil import parser
about = {
'website': 'https://openlibrary.org',
'wikidata_id': 'Q1201876',
'require_api_key': False,
'use_official_api': False,
'official_api_documentation': 'https://openlibrary.org/developers/api',
}
paging = True
categories = []
base_url = "https://openlibrary.org"
results_per_page = 10
def request(query, params):
args = {
'q': query,
'page': params['pageno'],
'limit': results_per_page,
}
params['url'] = f"{base_url}/search.json?{urlencode(args)}"
return params
def _parse_date(date):
try:
return parser.parse(date)
except parser.ParserError:
return None
def response(resp):
results = []
for item in resp.json().get("docs", []):
cover = None
if 'lending_identifier_s' in item:
cover = f"https://archive.org/services/img/{item['lending_identifier_s']}"
published = item.get('publish_date')
if published:
published_dates = [date for date in map(_parse_date, published) if date]
if published_dates:
published = min(published_dates)
if not published:
published = parser.parse(str(item.get('first_published_year')))
result = {
'template': 'paper.html',
'url': f"{base_url}{item['key']}",
'title': item['title'],
'content': re.sub(r"\{|\}", "", item['first_sentence'][0]) if item.get('first_sentence') else '',
'isbn': item.get('isbn', [])[:5],
'authors': item.get('author_name', []),
'thumbnail': cover,
'publishedDate': published,
'tags': item.get('subject', [])[:10] + item.get('place', [])[:10],
}
results.append(result)
return results

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Exception types raised by SearXNG modules.
"""
from __future__ import annotations
from typing import Optional, Union
@ -61,7 +62,7 @@ class SearxEngineAccessDeniedException(SearxEngineResponseException):
"""This settings contains the default suspended time (default 86400 sec / 1
day)."""
def __init__(self, suspended_time: int = None, message: str = 'Access denied'):
def __init__(self, suspended_time: int | None = None, message: str = 'Access denied'):
"""Generic exception to raise when an engine denies access to the results.
:param suspended_time: How long the engine is going to be suspended in
@ -70,12 +71,13 @@ class SearxEngineAccessDeniedException(SearxEngineResponseException):
:param message: Internal message. Defaults to ``Access denied``
:type message: str
"""
suspended_time = suspended_time or self._get_default_suspended_time()
if suspended_time is None:
suspended_time = self._get_default_suspended_time()
super().__init__(message + ', suspended_time=' + str(suspended_time))
self.suspended_time = suspended_time
self.message = message
def _get_default_suspended_time(self):
def _get_default_suspended_time(self) -> int:
from searx import get_setting # pylint: disable=C0415
return get_setting(self.SUSPEND_TIME_SETTING)
@ -88,7 +90,7 @@ class SearxEngineCaptchaException(SearxEngineAccessDeniedException):
"""This settings contains the default suspended time (default 86400 sec / 1
day)."""
def __init__(self, suspended_time=None, message='CAPTCHA'):
def __init__(self, suspended_time: int | None = None, message='CAPTCHA'):
super().__init__(message=message, suspended_time=suspended_time)
@ -102,7 +104,7 @@ class SearxEngineTooManyRequestsException(SearxEngineAccessDeniedException):
"""This settings contains the default suspended time (default 3660 sec / 1
hour)."""
def __init__(self, suspended_time=None, message='Too many request'):
def __init__(self, suspended_time: int | None = None, message='Too many request'):
super().__init__(message=message, suspended_time=suspended_time)

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations for providing the favicons in SearXNG"""
from __future__ import annotations
__all__ = ["init", "favicon_url", "favicon_proxy"]
import pathlib
from searx import logger
from searx import get_setting
from .proxy import favicon_url, favicon_proxy
logger = logger.getChild('favicons')
def is_active():
return bool(get_setting("search.favicon_resolver", False))
def init():
# pylint: disable=import-outside-toplevel
from . import config, cache, proxy
from .. import settings_loader
cfg_file = (settings_loader.get_user_cfg_folder() or pathlib.Path("/etc/searxng")) / "favicons.toml"
if not cfg_file.exists():
if is_active():
logger.error(f"missing favicon config: {cfg_file}")
cfg_file = config.DEFAULT_CFG_TOML_PATH
logger.debug(f"load favicon config: {cfg_file}")
cfg = config.FaviconConfig.from_toml_file(cfg_file, use_cache=True)
cache.init(cfg.cache)
proxy.init(cfg.proxy)
del cache, config, proxy, cfg, settings_loader

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Command line implementation"""
import typer
from . import cache
from . import init
init()
app = typer.Typer()
app.add_typer(cache.app, name="cache", help="commands related to the cache")
app()

476
searx/favicons/cache.py Normal file
View File

@ -0,0 +1,476 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations for caching favicons.
:py:obj:`FaviconCacheConfig`:
Configuration of the favicon cache
:py:obj:`FaviconCache`:
Abstract base class for the implementation of a favicon cache.
:py:obj:`FaviconCacheSQLite`:
Favicon cache that manages the favicon BLOBs in a SQLite DB.
:py:obj:`FaviconCacheNull`:
Fallback solution if the configured cache cannot be used for system reasons.
----
"""
from __future__ import annotations
from typing import Literal
import os
import abc
import dataclasses
import hashlib
import logging
import sqlite3
import tempfile
import time
import typer
import msgspec
from searx import sqlitedb
from searx import logger
from searx.utils import humanize_bytes, humanize_number
CACHE: "FaviconCache"
FALLBACK_ICON = b"FALLBACK_ICON"
logger = logger.getChild('favicons.cache')
app = typer.Typer()
@app.command()
def state():
"""show state of the cache"""
print(CACHE.state().report())
@app.command()
def maintenance(force: bool = True, debug: bool = False):
"""perform maintenance of the cache"""
root_log = logging.getLogger()
if debug:
root_log.setLevel(logging.DEBUG)
else:
root_log.handlers = []
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
state_t0 = CACHE.state()
CACHE.maintenance(force=force)
state_t1 = CACHE.state()
state_delta = state_t0 - state_t1
print("The cache has been reduced by:")
print(state_delta.report("\n- {descr}: {val}").lstrip("\n"))
def init(cfg: "FaviconCacheConfig"):
"""Initialization of a global ``CACHE``"""
global CACHE # pylint: disable=global-statement
if cfg.db_type == "sqlite":
if sqlite3.sqlite_version_info <= (3, 35):
logger.critical(
"Disable favicon caching completely: SQLite library (%s) is too old! (require >= 3.35)",
sqlite3.sqlite_version,
)
CACHE = FaviconCacheNull(cfg)
else:
CACHE = FaviconCacheSQLite(cfg)
elif cfg.db_type == "mem":
logger.error("Favicons are cached in memory, don't use this in production!")
CACHE = FaviconCacheMEM(cfg)
else:
raise NotImplementedError(f"favicons db_type '{cfg.db_type}' is unknown")
class FaviconCacheConfig(msgspec.Struct): # pylint: disable=too-few-public-methods
"""Configuration of the favicon cache."""
db_type: Literal["sqlite", "mem"] = "sqlite"
"""Type of the database:
``sqlite``:
:py:obj:`.cache.FaviconCacheSQLite`
``mem``:
:py:obj:`.cache.FaviconCacheMEM` (not recommended)
"""
db_url: str = tempfile.gettempdir() + os.sep + "faviconcache.db"
"""URL of the SQLite DB, the path to the database file."""
HOLD_TIME: int = 60 * 60 * 24 * 30 # 30 days
"""Hold time (default in sec.), after which a BLOB is removed from the cache."""
LIMIT_TOTAL_BYTES: int = 1024 * 1024 * 50 # 50 MB
"""Maximum of bytes (default) stored in the cache of all blobs. Note: The
limit is only reached at each maintenance interval after which the oldest
BLOBs are deleted; the limit is exceeded during the maintenance period. If
the maintenance period is *too long* or maintenance is switched off
completely, the cache grows uncontrollably."""
BLOB_MAX_BYTES: int = 1024 * 20 # 20 KB
"""The maximum BLOB size in bytes that a favicon may have so that it can be
saved in the cache. If the favicon is larger, it is not saved in the cache
and must be requested by the client via the proxy."""
MAINTENANCE_PERIOD: int = 60 * 60
"""Maintenance period in seconds / when :py:obj:`MAINTENANCE_MODE` is set to
``auto``."""
MAINTENANCE_MODE: Literal["auto", "off"] = "auto"
"""Type of maintenance mode
``auto``:
Maintenance is carried out automatically as part of the maintenance
intervals (:py:obj:`MAINTENANCE_PERIOD`); no external process is required.
``off``:
Maintenance is switched off and must be carried out by an external process
if required.
"""
@dataclasses.dataclass
class FaviconCacheStats:
"""Dataclass wich provides information on the status of the cache."""
favicons: int | None = None
bytes: int | None = None
domains: int | None = None
resolvers: int | None = None
field_descr = (
("favicons", "number of favicons in cache", humanize_number),
("bytes", "total size (approx. bytes) of cache", humanize_bytes),
("domains", "total number of domains in cache", humanize_number),
("resolvers", "number of resolvers", str),
)
def __sub__(self, other) -> FaviconCacheStats:
if not isinstance(other, self.__class__):
raise TypeError(f"unsupported operand type(s) for +: '{self.__class__}' and '{type(other)}'")
kwargs = {}
for field, _, _ in self.field_descr:
self_val, other_val = getattr(self, field), getattr(other, field)
if None in (self_val, other_val):
continue
if isinstance(self_val, int):
kwargs[field] = self_val - other_val
else:
kwargs[field] = self_val
return self.__class__(**kwargs)
def report(self, fmt: str = "{descr}: {val}\n"):
s = []
for field, descr, cast in self.field_descr:
val = getattr(self, field)
if val is None:
val = "--"
else:
val = cast(val)
s.append(fmt.format(descr=descr, val=val))
return "".join(s)
class FaviconCache(abc.ABC):
"""Abstract base class for the implementation of a favicon cache."""
@abc.abstractmethod
def __init__(self, cfg: FaviconCacheConfig):
"""An instance of the favicon cache is build up from the configuration."""
@abc.abstractmethod
def __call__(self, resolver: str, authority: str) -> None | tuple[None | bytes, None | str]:
"""Returns ``None`` or the tuple of ``(data, mime)`` that has been
registered in the cache. The ``None`` indicates that there was no entry
in the cache."""
@abc.abstractmethod
def set(self, resolver: str, authority: str, mime: str | None, data: bytes | None) -> bool:
"""Set data and mime-type in the cache. If data is None, the
:py:obj:`FALLBACK_ICON` is registered. in the cache."""
@abc.abstractmethod
def state(self) -> FaviconCacheStats:
"""Returns a :py:obj:`FaviconCacheStats` (key/values) with information
on the state of the cache."""
@abc.abstractmethod
def maintenance(self, force=False):
"""Performs maintenance on the cache"""
class FaviconCacheNull(FaviconCache):
"""A dummy favicon cache that caches nothing / a fallback solution. The
NullCache is used when more efficient caches such as the
:py:obj:`FaviconCacheSQLite` cannot be used because, for example, the SQLite
library is only available in an old version and does not meet the
requirements."""
def __init__(self, cfg: FaviconCacheConfig):
return None
def __call__(self, resolver: str, authority: str) -> None | tuple[None | bytes, None | str]:
return None
def set(self, resolver: str, authority: str, mime: str | None, data: bytes | None) -> bool:
return False
def state(self):
return FaviconCacheStats(favicons=0)
def maintenance(self, force=False):
pass
class FaviconCacheSQLite(sqlitedb.SQLiteAppl, FaviconCache):
"""Favicon cache that manages the favicon BLOBs in a SQLite DB. The DB
model in the SQLite DB is implemented using the abstract class
:py:obj:`sqlitedb.SQLiteAppl`.
The following configurations are required / supported:
- :py:obj:`FaviconCacheConfig.db_url`
- :py:obj:`FaviconCacheConfig.HOLD_TIME`
- :py:obj:`FaviconCacheConfig.LIMIT_TOTAL_BYTES`
- :py:obj:`FaviconCacheConfig.BLOB_MAX_BYTES`
- :py:obj:`MAINTENANCE_PERIOD`
- :py:obj:`MAINTENANCE_MODE`
"""
DB_SCHEMA = 1
DDL_BLOBS = """\
CREATE TABLE IF NOT EXISTS blobs (
sha256 TEXT,
bytes_c INTEGER,
mime TEXT NOT NULL,
data BLOB NOT NULL,
PRIMARY KEY (sha256))"""
"""Table to store BLOB objects by their sha256 hash values."""
DDL_BLOB_MAP = """\
CREATE TABLE IF NOT EXISTS blob_map (
m_time INTEGER DEFAULT (strftime('%s', 'now')), -- last modified (unix epoch) time in sec.
sha256 TEXT,
resolver TEXT,
authority TEXT,
PRIMARY KEY (resolver, authority))"""
"""Table to map from (resolver, authority) to sha256 hash values."""
DDL_CREATE_TABLES = {
"blobs": DDL_BLOBS,
"blob_map": DDL_BLOB_MAP,
}
SQL_DROP_LEFTOVER_BLOBS = (
"DELETE FROM blobs WHERE sha256 IN ("
" SELECT b.sha256"
" FROM blobs b"
" LEFT JOIN blob_map bm"
" ON b.sha256 = bm.sha256"
" WHERE bm.sha256 IS NULL)"
)
"""Delete blobs.sha256 (BLOBs) no longer in blob_map.sha256."""
SQL_ITER_BLOBS_SHA256_BYTES_C = (
"SELECT b.sha256, b.bytes_c FROM blobs b"
" JOIN blob_map bm "
" ON b.sha256 = bm.sha256"
" ORDER BY bm.m_time ASC"
)
SQL_INSERT_BLOBS = (
"INSERT INTO blobs (sha256, bytes_c, mime, data) VALUES (?, ?, ?, ?)"
" ON CONFLICT (sha256) DO NOTHING"
) # fmt: skip
SQL_INSERT_BLOB_MAP = (
"INSERT INTO blob_map (sha256, resolver, authority) VALUES (?, ?, ?)"
" ON CONFLICT DO UPDATE "
" SET sha256=excluded.sha256, m_time=strftime('%s', 'now')"
)
def __init__(self, cfg: FaviconCacheConfig):
"""An instance of the favicon cache is build up from the configuration.""" #
if cfg.db_url == ":memory:":
logger.critical("don't use SQLite DB in :memory: in production!!")
super().__init__(cfg.db_url)
self.cfg = cfg
def __call__(self, resolver: str, authority: str) -> None | tuple[None | bytes, None | str]:
sql = "SELECT sha256 FROM blob_map WHERE resolver = ? AND authority = ?"
res = self.DB.execute(sql, (resolver, authority)).fetchone()
if res is None:
return None
data, mime = (None, None)
sha256 = res[0]
if sha256 == FALLBACK_ICON:
return data, mime
sql = "SELECT data, mime FROM blobs WHERE sha256 = ?"
res = self.DB.execute(sql, (sha256,)).fetchone()
if res is not None:
data, mime = res
return data, mime
def set(self, resolver: str, authority: str, mime: str | None, data: bytes | None) -> bool:
if self.cfg.MAINTENANCE_MODE == "auto" and int(time.time()) > self.next_maintenance_time:
# Should automatic maintenance be moved to a new thread?
self.maintenance()
if data is not None and mime is None:
logger.error(
"favicon resolver %s tries to cache mime-type None for authority %s",
resolver,
authority,
)
return False
bytes_c = len(data or b"")
if bytes_c > self.cfg.BLOB_MAX_BYTES:
logger.info(
"favicon of resolver: %s / authority: %s to big to cache (bytes: %s) " % (resolver, authority, bytes_c)
)
return False
if data is None:
sha256 = FALLBACK_ICON
else:
sha256 = hashlib.sha256(data).hexdigest()
with self.connect() as conn:
if sha256 != FALLBACK_ICON:
conn.execute(self.SQL_INSERT_BLOBS, (sha256, bytes_c, mime, data))
conn.execute(self.SQL_INSERT_BLOB_MAP, (sha256, resolver, authority))
return True
@property
def next_maintenance_time(self) -> int:
"""Returns (unix epoch) time of the next maintenance."""
return self.cfg.MAINTENANCE_PERIOD + self.properties.m_time("LAST_MAINTENANCE")
def maintenance(self, force=False):
# Prevent parallel DB maintenance cycles from other DB connections
# (e.g. in multi thread or process environments).
if not force and int(time.time()) < self.next_maintenance_time:
logger.debug("no maintenance required yet, next maintenance interval is in the future")
return
self.properties.set("LAST_MAINTENANCE", "") # hint: this (also) sets the m_time of the property!
# do maintenance tasks
with self.connect() as conn:
# drop items not in HOLD time
res = conn.execute(
f"DELETE FROM blob_map"
f" WHERE cast(m_time as integer) < cast(strftime('%s', 'now') as integer) - {self.cfg.HOLD_TIME}"
)
logger.debug("dropped %s obsolete blob_map items from db", res.rowcount)
res = conn.execute(self.SQL_DROP_LEFTOVER_BLOBS)
logger.debug("dropped %s obsolete BLOBS from db", res.rowcount)
# drop old items to be in LIMIT_TOTAL_BYTES
total_bytes = conn.execute("SELECT SUM(bytes_c) FROM blobs").fetchone()[0] or 0
if total_bytes > self.cfg.LIMIT_TOTAL_BYTES:
x = total_bytes - self.cfg.LIMIT_TOTAL_BYTES
c = 0
sha_list = []
for row in conn.execute(self.SQL_ITER_BLOBS_SHA256_BYTES_C):
sha256, bytes_c = row
sha_list.append(sha256)
c += bytes_c
if c > x:
break
if sha_list:
conn.execute("DELETE FROM blobs WHERE sha256 IN ('%s')" % "','".join(sha_list))
conn.execute("DELETE FROM blob_map WHERE sha256 IN ('%s')" % "','".join(sha_list))
logger.debug("dropped %s blobs with total size of %s bytes", len(sha_list), c)
def _query_val(self, sql, default=None):
val = self.DB.execute(sql).fetchone()
if val is not None:
val = val[0]
if val is None:
val = default
return val
def state(self) -> FaviconCacheStats:
return FaviconCacheStats(
favicons=self._query_val("SELECT count(*) FROM blobs", 0),
bytes=self._query_val("SELECT SUM(bytes_c) FROM blobs", 0),
domains=self._query_val("SELECT count(*) FROM (SELECT authority FROM blob_map GROUP BY authority)", 0),
resolvers=self._query_val("SELECT count(*) FROM (SELECT resolver FROM blob_map GROUP BY resolver)", 0),
)
class FaviconCacheMEM(FaviconCache):
"""Favicon cache in process' memory. Its just a POC that stores the
favicons in the memory of the process.
.. attention::
Don't use it in production, it will blow up your memory!!
"""
def __init__(self, cfg):
self.cfg = cfg
self._data = {}
self._sha_mime = {}
def __call__(self, resolver: str, authority: str) -> None | tuple[bytes | None, str | None]:
sha, mime = self._sha_mime.get(f"{resolver}:{authority}", (None, None))
if sha is None:
return None
data = self._data.get(sha)
if data == FALLBACK_ICON:
data = None
return data, mime
def set(self, resolver: str, authority: str, mime: str | None, data: bytes | None) -> bool:
if data is None:
data = FALLBACK_ICON
mime = None
elif mime is None:
logger.error(
"favicon resolver %s tries to cache mime-type None for authority %s",
resolver,
authority,
)
return False
digest = hashlib.sha256(data).hexdigest()
self._data[digest] = data
self._sha_mime[f"{resolver}:{authority}"] = (digest, mime)
return True
def state(self):
return FaviconCacheStats(favicons=len(self._data.keys()))
def maintenance(self, force=False):
pass

65
searx/favicons/config.py Normal file
View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
from __future__ import annotations
import pathlib
import msgspec
from .cache import FaviconCacheConfig
from .proxy import FaviconProxyConfig
CONFIG_SCHEMA: int = 1
"""Version of the configuration schema."""
TOML_CACHE_CFG: dict[str, "FaviconConfig"] = {}
"""Cache config objects by TOML's filename."""
DEFAULT_CFG_TOML_PATH = pathlib.Path(__file__).parent / "favicons.toml"
class FaviconConfig(msgspec.Struct): # pylint: disable=too-few-public-methods
"""The class aggregates configurations of the favicon tools"""
cfg_schema: int
"""Config's schema version. The specification of the version of the schema
is mandatory, currently only version :py:obj:`CONFIG_SCHEMA` is supported.
By specifying a version, it is possible to ensure downward compatibility in
the event of future changes to the configuration schema"""
cache: FaviconCacheConfig = msgspec.field(default_factory=FaviconCacheConfig)
"""Setup of the :py:obj:`.cache.FaviconCacheConfig`."""
proxy: FaviconProxyConfig = msgspec.field(default_factory=FaviconProxyConfig)
"""Setup of the :py:obj:`.proxy.FaviconProxyConfig`."""
@classmethod
def from_toml_file(cls, cfg_file: pathlib.Path, use_cache: bool) -> "FaviconConfig":
"""Create a config object from a TOML file, the ``use_cache`` argument
specifies whether a cache should be used.
"""
cached = TOML_CACHE_CFG.get(str(cfg_file))
if use_cache and cached:
return cached
with cfg_file.open("rb") as f:
data = f.read()
cfg = msgspec.toml.decode(data, type=_FaviconConfig)
schema = cfg.favicons.cfg_schema
if schema != CONFIG_SCHEMA:
raise ValueError(
f"config schema version {CONFIG_SCHEMA} is needed, version {schema} is given in {cfg_file}"
)
cfg = cfg.favicons
if use_cache and cached:
TOML_CACHE_CFG[str(cfg_file.resolve())] = cfg
return cfg
class _FaviconConfig(msgspec.Struct): # pylint: disable=too-few-public-methods
# wrapper struct for root object "favicons."
favicons: FaviconConfig

View File

@ -0,0 +1,25 @@
[favicons]
cfg_schema = 1 # config's schema version no.
[favicons.proxy]
# max_age = 5184000 # 60 days / default: 7 days (604800 sec)
# [favicons.proxy.resolver_map]
#
# The available favicon resolvers are registered here.
#
# "duckduckgo" = "searx.favicons.resolvers.duckduckgo"
# "allesedv" = "searx.favicons.resolvers.allesedv"
# "google" = "searx.favicons.resolvers.google"
# "yandex" = "searx.favicons.resolvers.yandex"
[favicons.cache]
# db_url = "/var/cache/searxng/faviconcache.db" # default: "/tmp/faviconcache.db"
# HOLD_TIME = 5184000 # 60 days / default: 30 days
# LIMIT_TOTAL_BYTES = 2147483648 # 2 GB / default: 50 MB
# BLOB_MAX_BYTES = 40960 # 40 KB / default 20 KB
# MAINTENANCE_MODE = "off" # default: "auto"
# MAINTENANCE_PERIOD = 600 # 10min / default: 1h

237
searx/favicons/proxy.py Normal file
View File

@ -0,0 +1,237 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations for a favicon proxy"""
from __future__ import annotations
from typing import Callable
import importlib
import base64
import pathlib
import urllib.parse
import flask
from httpx import HTTPError
import msgspec
from searx import get_setting
from searx.webutils import new_hmac, is_hmac_of
from searx.exceptions import SearxEngineResponseException
from .resolvers import DEFAULT_RESOLVER_MAP
from . import cache
DEFAULT_FAVICON_URL = {}
CFG: FaviconProxyConfig = None # type: ignore
def init(cfg: FaviconProxyConfig):
global CFG # pylint: disable=global-statement
CFG = cfg
def _initial_resolver_map():
d = {}
name: str = get_setting("search.favicon_resolver", None) # type: ignore
if name:
func = DEFAULT_RESOLVER_MAP.get(name)
if func:
d = {name: f"searx.favicons.resolvers.{func.__name__}"}
return d
class FaviconProxyConfig(msgspec.Struct):
"""Configuration of the favicon proxy."""
max_age: int = 60 * 60 * 24 * 7 # seven days
"""HTTP header Cache-Control_ ``max-age``
.. _Cache-Control: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
"""
secret_key: str = get_setting("server.secret_key") # type: ignore
"""By default, the value from :ref:`server.secret_key <settings server>`
setting is used."""
resolver_timeout: int = get_setting("outgoing.request_timeout") # type: ignore
"""Timeout which the resolvers should not exceed, is usually passed to the
outgoing request of the resolver. By default, the value from
:ref:`outgoing.request_timeout <settings outgoing>` setting is used."""
resolver_map: dict[str, str] = msgspec.field(default_factory=_initial_resolver_map)
"""The resolver_map is a key / value dictionary where the key is the name of
the resolver and the value is the fully qualifying name (fqn) of resolver's
function (the callable). The resolvers from the python module
:py:obj:`searx.favicons.resolver` are available by default."""
def get_resolver(self, name: str) -> Callable | None:
"""Returns the callable object (function) of the resolver with the
``name``. If no resolver is registered for the ``name``, ``None`` is
returned.
"""
fqn = self.resolver_map.get(name)
if fqn is None:
return None
mod_name, _, func_name = fqn.rpartition('.')
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
if func is None:
raise ValueError(f"resolver {fqn} is not implemented")
return func
favicon_path: str = get_setting("ui.static_path") + "/themes/{theme}/img/empty_favicon.svg" # type: ignore
favicon_mime_type: str = "image/svg+xml"
def favicon(self, **replacements):
"""Returns pathname and mimetype of the default favicon."""
return (
pathlib.Path(self.favicon_path.format(**replacements)),
self.favicon_mime_type,
)
def favicon_data_url(self, **replacements):
"""Returns data image URL of the default favicon."""
cache_key = ", ".join(f"{x}:{replacements[x]}" for x in sorted(list(replacements.keys()), key=str))
data_url = DEFAULT_FAVICON_URL.get(cache_key)
if data_url is not None:
return data_url
fav, mimetype = CFG.favicon(**replacements)
# hint: encoding utf-8 limits favicons to be a SVG image
with fav.open("r", encoding="utf-8") as f:
data_url = f.read()
data_url = urllib.parse.quote(data_url)
data_url = f"data:{mimetype};utf8,{data_url}"
DEFAULT_FAVICON_URL[cache_key] = data_url
return data_url
def favicon_proxy():
"""REST API of SearXNG's favicon proxy service
::
/favicon_proxy?authority=<...>&h=<...>
``authority``:
Domain name :rfc:`3986` / see :py:obj:`favicon_url`
``h``:
HMAC :rfc:`2104`, build up from the :ref:`server.secret_key <settings
server>` setting.
"""
authority = flask.request.args.get('authority')
# malformed request or RFC 3986 authority
if not authority or "/" in authority:
return '', 400
# malformed request / does not have authorisation
if not is_hmac_of(
CFG.secret_key,
authority.encode(),
flask.request.args.get('h', ''),
):
return '', 400
resolver = flask.request.preferences.get_value('favicon_resolver') # type: ignore
# if resolver is empty or not valid, just return HTTP 400.
if not resolver or resolver not in CFG.resolver_map.keys():
return "", 400
data, mime = search_favicon(resolver, authority)
if data is not None and mime is not None:
resp = flask.Response(data, mimetype=mime) # type: ignore
resp.headers['Cache-Control'] = f"max-age={CFG.max_age}"
return resp
# return default favicon from static path
theme = flask.request.preferences.get_value("theme") # type: ignore
fav, mimetype = CFG.favicon(theme=theme)
return flask.send_from_directory(fav.parent, fav.name, mimetype=mimetype)
def search_favicon(resolver: str, authority: str) -> tuple[None | bytes, None | str]:
"""Sends the request to the favicon resolver and returns a tuple for the
favicon. The tuple consists of ``(data, mime)``, if the resolver has not
determined a favicon, both values are ``None``.
``data``:
Binary data of the favicon.
``mime``:
Mime type of the favicon.
"""
data, mime = (None, None)
func = CFG.get_resolver(resolver)
if func is None:
return data, mime
# to avoid superfluous requests to the resolver, first look in the cache
data_mime = cache.CACHE(resolver, authority)
if data_mime is not None:
return data_mime
try:
data, mime = func(authority, timeout=CFG.resolver_timeout)
if data is None or mime is None:
data, mime = (None, None)
except (HTTPError, SearxEngineResponseException):
pass
cache.CACHE.set(resolver, authority, mime, data)
return data, mime
def favicon_url(authority: str) -> str:
"""Function to generate the image URL used for favicons in SearXNG's result
lists. The ``authority`` argument (aka netloc / :rfc:`3986`) is usually a
(sub-) domain name. This function is used in the HTML (jinja) templates.
.. code:: html
<div class="favicon">
<img src="{{ favicon_url(result.parsed_url.netloc) }}">
</div>
The returned URL is a route to :py:obj:`favicon_proxy` REST API.
If the favicon is already in the cache, the returned URL is a `data URL`_
(something like ``data:image/png;base64,...``). By generating a data url from
the :py:obj:`.cache.FaviconCache`, additional HTTP roundtripps via the
:py:obj:`favicon_proxy` are saved. However, it must also be borne in mind
that data urls are not cached in the client (web browser).
.. _data URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
"""
resolver = flask.request.preferences.get_value('favicon_resolver') # type: ignore
# if resolver is empty or not valid, just return nothing.
if not resolver or resolver not in CFG.resolver_map.keys():
return ""
data_mime = cache.CACHE(resolver, authority)
if data_mime == (None, None):
# we have already checked, the resolver does not have a favicon
theme = flask.request.preferences.get_value("theme") # type: ignore
return CFG.favicon_data_url(theme=theme)
if data_mime is not None:
data, mime = data_mime
return f"data:{mime};base64,{str(base64.b64encode(data), 'utf-8')}" # type: ignore
h = new_hmac(CFG.secret_key, authority.encode())
proxy_url = flask.url_for('favicon_proxy')
query = urllib.parse.urlencode({"authority": authority, "h": h})
return f"{proxy_url}?{query}"

100
searx/favicons/resolvers.py Normal file
View File

@ -0,0 +1,100 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations of the favicon *resolvers* that are available in the favicon
proxy by default. A *resolver* is a function that obtains the favicon from an
external source. The *resolver* function receives two arguments (``domain,
timeout``) and returns a tuple ``(data, mime)``.
"""
from __future__ import annotations
__all__ = ["DEFAULT_RESOLVER_MAP", "allesedv", "duckduckgo", "google", "yandex"]
from typing import Callable
from searx import network
from searx import logger
DEFAULT_RESOLVER_MAP: dict[str, Callable]
logger = logger.getChild('favicons.resolvers')
def _req_args(**kwargs):
# add the request arguments from the searx.network
d = {"raise_for_httperror": False}
d.update(kwargs)
return d
def allesedv(domain: str, timeout: int) -> tuple[None | bytes, None | str]:
"""Favicon Resolver from allesedv.com / https://favicon.allesedv.com/"""
data, mime = (None, None)
url = f"https://f1.allesedv.com/32/{domain}"
logger.debug("fetch favicon from: %s", url)
# will just return a 200 regardless of the favicon existing or not
# sometimes will be correct size, sometimes not
response = network.get(url, **_req_args(timeout=timeout))
if response and response.status_code == 200:
mime = response.headers['Content-Type']
if mime != 'image/gif':
data = response.content
return data, mime
def duckduckgo(domain: str, timeout: int) -> tuple[None | bytes, None | str]:
"""Favicon Resolver from duckduckgo.com / https://blog.jim-nielsen.com/2021/displaying-favicons-for-any-domain/"""
data, mime = (None, None)
url = f"https://icons.duckduckgo.com/ip2/{domain}.ico"
logger.debug("fetch favicon from: %s", url)
# will return a 404 if the favicon does not exist and a 200 if it does,
response = network.get(url, **_req_args(timeout=timeout))
if response and response.status_code == 200:
# api will respond with a 32x32 png image
mime = response.headers['Content-Type']
data = response.content
return data, mime
def google(domain: str, timeout: int) -> tuple[None | bytes, None | str]:
"""Favicon Resolver from google.com"""
data, mime = (None, None)
# URL https://www.google.com/s2/favicons?sz=32&domain={domain}" will be
# redirected (HTTP 301 Moved Permanently) to t1.gstatic.com/faviconV2:
url = (
f"https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL"
f"&url=https://{domain}&size=32"
)
logger.debug("fetch favicon from: %s", url)
# will return a 404 if the favicon does not exist and a 200 if it does,
response = network.get(url, **_req_args(timeout=timeout))
if response and response.status_code == 200:
# api will respond with a 32x32 png image
mime = response.headers['Content-Type']
data = response.content
return data, mime
def yandex(domain: str, timeout: int) -> tuple[None | bytes, None | str]:
"""Favicon Resolver from yandex.com"""
data, mime = (None, None)
url = f"https://favicon.yandex.net/favicon/{domain}"
logger.debug("fetch favicon from: %s", url)
# api will respond with a 16x16 png image, if it doesn't exist, it will be a
# 1x1 png image (70 bytes)
response = network.get(url, **_req_args(timeout=timeout))
if response and response.status_code == 200 and len(response.content) > 70:
mime = response.headers['Content-Type']
data = response.content
return data, mime
DEFAULT_RESOLVER_MAP = {
"allesedv": allesedv,
"duckduckgo": duckduckgo,
"google": google,
"yandex": yandex,
}

View File

@ -128,9 +128,6 @@ _INSTALLED = False
LIMITER_CFG_SCHEMA = Path(__file__).parent / "limiter.toml"
"""Base configuration (schema) of the botdetection."""
LIMITER_CFG = Path('/etc/searxng/limiter.toml')
"""Local Limiter configuration."""
CFG_DEPRECATED = {
# "dummy.old.foo": "config 'dummy.old.foo' exists only for tests. Don't use it in your real project config."
}
@ -138,8 +135,12 @@ CFG_DEPRECATED = {
def get_cfg() -> config.Config:
global CFG # pylint: disable=global-statement
if CFG is None:
CFG = config.Config.from_toml(LIMITER_CFG_SCHEMA, LIMITER_CFG, CFG_DEPRECATED)
from . import settings_loader # pylint: disable=import-outside-toplevel
cfg_file = (settings_loader.get_user_cfg_folder() or Path("/etc/searxng")) / "limiter.toml"
CFG = config.Config.from_toml(LIMITER_CFG_SCHEMA, cfg_file, CFG_DEPRECATED)
return CFG

View File

@ -3,9 +3,13 @@
"""
import ast
import re
import operator
from multiprocessing import Process, Queue
from typing import Callable
import flask
import babel
from flask_babel import gettext
from searx.plugins import logger
@ -19,7 +23,7 @@ plugin_id = 'calculator'
logger = logger.getChild(plugin_id)
operators = {
operators: dict[type, Callable] = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
@ -39,11 +43,15 @@ def _eval_expr(expr):
>>> _eval_expr('1 + 2*3**(4^5) / (6 + -7)')
-5.0
"""
try:
return _eval(ast.parse(expr, mode='eval').body)
except ZeroDivisionError:
# This is undefined
return ""
def _eval(node):
if isinstance(node, ast.Constant) and isinstance(node.value, int):
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp):
@ -93,6 +101,19 @@ def post_search(_request, search):
# replace commonly used math operators with their proper Python operator
query = query.replace("x", "*").replace(":", "/")
# use UI language
ui_locale = babel.Locale.parse(flask.request.preferences.get_value('locale'), sep='-')
# parse the number system in a localized way
def _decimal(match: re.Match) -> str:
val = match.string[match.start() : match.end()]
val = babel.numbers.parse_decimal(val, ui_locale, numbering_system="latn")
return str(val)
decimal = ui_locale.number_symbols["latn"]["decimal"]
group = ui_locale.number_symbols["latn"]["group"]
query = re.sub(f"[0-9]+[{decimal}|{group}][0-9]+[{decimal}|{group}]?[0-9]?", _decimal, query)
# only numbers and math operators are accepted
if any(str.isalpha(c) for c in query):
return True
@ -102,10 +123,8 @@ def post_search(_request, search):
# Prevent the runtime from being longer than 50 ms
result = timeout_func(0.05, _eval_expr, query_py_formatted)
if result is None:
if result is None or result == "":
return True
result = str(result)
if result != query:
search.result_container.answers['calculate'] = {'answer': f"{query} = {result}"}
result = babel.numbers.format_decimal(result, locale=ui_locale)
search.result_container.answers['calculate'] = {'answer': f"{search.search_query.query} = {result}"}
return True

View File

@ -13,7 +13,7 @@ from collections import OrderedDict
import flask
import babel
from searx import settings, autocomplete
from searx import settings, autocomplete, favicons
from searx.enginelib import Engine
from searx.plugins import Plugin
from searx.locales import LOCALE_NAMES
@ -406,6 +406,11 @@ class Preferences:
locked=is_locked('autocomplete'),
choices=list(autocomplete.backends.keys()) + ['']
),
'favicon_resolver': EnumStringSetting(
settings['search']['favicon_resolver'],
locked=is_locked('favicon_resolver'),
choices=list(favicons.proxy.CFG.resolver_map.keys()) + ['']
),
'image_proxy': BooleanSetting(
settings['server']['image_proxy'],
locked=is_locked('image_proxy')

259
searx/regexp.py Normal file
View File

@ -0,0 +1,259 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations for efficient processing of regular expressions"""
from __future__ import annotations
from typing import Iterator
import abc
import re
import warnings
import json
class RegExprList(abc.ABC):
"""Abstract base class for efficient processing of lists of regular
expressions. The inheriting classes have to implement the
:py:obj:`RegExprList.load_regexp` method which is used to load the list of
regular expressions from a configuration, for example.
Intention: By concatenating the regular expressions from the list into one
regular expression, all patterns can be performed with just one search and
it is not necessary to iterate over the individual expressions and perform
n-searches.
"""
RE_GRP_PREFIX = "RegExprList"
@abc.abstractmethod
def load_regexps(self) -> list[tuple[str, tuple]]:
"""Abstract method to load the list of regular expressions from a
configuration. Returns a list of regular expressions (str) or a list of
two-digit tuples with a regular expression on its first position and
tuple of *n-objects* related to this regular expression on its second
position:
.. code:: python
[
( <regexpr_a>, (obj_a1, obj_a2, ..) ),
( <regexpr_b>, (obj_b1, obj_b2, ..) ),
..
]
If there is nothing related to the regular expression, the tuple is
empty (n=0). The **objects** must be of a simple data type (str, int,
..) so that they can be serialized (JSON).
"""
def __init__(self, chunk_size = 1000):
self.chunk_size = chunk_size
self._chunks = None
self._data_json = None
def _get_data(self):
if self._data_json is not None:
return json.loads(self._data_json)
return self.load_regexps()
@property
def JSON(self):
"""JSON representation of the regular expression list (see
:py:obj:`RegExprList.load_regexp`).
Serialize the :py:obj:`RegExprList` object into a JSON string.
"""
if self._data_json is not None:
return self._data_json
return json.dumps(self._get_data(), sort_keys=True)
@classmethod
def from_json(cls, json_str: str) -> "RegExprList":
"""Build a :py:obj:`RegExprList` object and load regular expressions from
a JSON string (compare :py:obj:`RegExprList.JSON`)."""
obj = cls()
obj._data_json = json_str
return obj
@property
def chunks(self) -> list[tuple[re.Pattern, list[tuple]]]:
"""A list of (concatenated) regular expressions"""
if self._chunks is None:
self._chunks = self.get_chunks()
return self._chunks
def get_chunks(self) -> list[tuple[re.Pattern, list[tuple]]]:
"""Returns a list chunks items. A chunk item is a two-digit tuple with
the concatened :py:obj:`re.Pattern` on its first position and a list of
tuples (aka grp_tuples) on its second position.
The regular expressions are placed in *named groups* and the group for
the match can be determined using :py:obj:`re.Match.groupdict:`.
.. code: re
(?P<{_0}>foo)|(?P<_1>bar)
.. code: python
>>> grp_tuples[0]
('foo', obj_foo_1, obj_foo_2, ...)
>>> grp_tuples[1]
('bar', obj_bar_1, obj_bar_1, ...)
"""
chunks = []
re_list = self._get_data()
chunk_re = ""
grp_tuples = []
c = -1
for pos in range(0, len(re_list)):
c += 1
objs_tpl = ()
if len(re_list[pos]) == 2:
re_str, objs_tpl = re_list[pos]
else:
re_str = re_list[pos]
grp_re = f"|(?P<{self.RE_GRP_PREFIX}_{c}>{re_str})"
if len(grp_re) + len(chunk_re) > self.chunk_size:
# remove the leading | from chunk_re
chunks.append((re.compile(chunk_re[1:]), grp_tuples))
chunk_re = ""
grp_tuples = []
chunk_re += grp_re
grp_tuples.append((re_str, ) + objs_tpl)
# Are there any leftovers from the for loop?
if chunk_re:
chunks.append((re.compile(chunk_re[1:]), grp_tuples))
return chunks
def search(self, string: str) -> tuple[re.Match, tuple] | None:
"""Search for regular expressions in ``string``. If none of the regular
expression matches, ``None`` is returned. If there is a match, the
first match (:py:obj:`re.Match`) is returned along with a tuple of
objects related to the matched pattern (compare :py:obj:`RegExprList`):
.. code:: python
( re.Match, ( <regexpr_str>, obj_1, obj_2, ..) )
"""
pos = -1
for regexp, objs_tpl in self.chunks:
m = regexp.search(string)
if m:
prefix = f"{self.RE_GRP_PREFIX}_"
for grp_name, val in m.groupdict().items():
if not grp_name.startswith(prefix):
continue
if val is None:
continue
try:
pos = int(grp_name[len(prefix):])
return (m, objs_tpl[pos])
except ValueError:
# This case should never occur unless there is something
# wrong with the regular expressions.
warnings.warn(f"ignoring group '{grp_name}' in regexpr match {m}: check your regular expressions!")
m = None
break
return None
def finditer(self, string: str) -> Iterator[tuple[re.Match, tuple]]:
"""Return an iterator yielding over all *"non-overlapping"* matches for
the RE pattern in string. Similar to :py:obj:`RegExpr.search` each
match (:py:obj:`re.Match`) comes along with a tuple of objects related
to the matched pattern:
.. code:: python
( re.Match, ( <regexpr_str>, obj_1, obj_2, ..) )
Since the list of regular expressions is concatenated and also broken up
at the boundaries of the chunks, it is not possible to ensure
*"non-overlapping"* over the entirety of all regular expressions in the
list! Nevertheless, there will be scenarios where this iterator makes
sense, e.g. if the regular expressions do not overlap.
.. caution:
Use this method with care if the :py:obj:`regular expressions in the
list <RegExprListload_regexps>` *overlap*, otherwise you get unexpected
results!
"""
pos = -1
for regexp, objs_tpl in self.chunks:
for m in regexp.finditer(string):
if m is None:
continue
prefix = f"{self.RE_GRP_PREFIX}_"
for grp_name, val in m.groupdict().items():
if not grp_name.startswith(prefix):
continue
if val is None:
continue
try:
pos = int(grp_name[len(prefix):])
yield (m, objs_tpl[pos])
except ValueError:
# This case should never occur unless there is something
# wrong with the regular expressions.
warnings.warn(f"ignoring group '{grp_name}' in regexpr match {m}: check your regular expressions!")
continue
##########################################################################
## some tests of the POC above
import pdb
def test_POC():
test_list = [
# hint: the order of the list counts!
(r'aa', ("double 'a' don't overlaps with any other regular expressions",)),
(r'a', ("single 'a' overlaps with all other regular expressions",)),
r'(.*\.)?academiapublishing\.org$',
r'(.*\.)?academiaresearch\.org$',
r'(.*\.)?academiascholarlyjournal\.org$',
r'(.*\.)?academicjournalsinc\.com$',
r'(.*\.)?academicjournalsonline\.co\.in$',
r'(.*\.)?academicjournals\.org$',
r'(.*\.)?academicoasis\.org$',
r'(.*\.)?academic-publishing-house\.com$',
r'(.*\.)?academicpub\.org$',
r'(.*\.)?academicresearchjournals\.org$',
r'(.*\.)?academicstar\.us$',
r'(.*\.)?academicsworld\.org$',
r'(.*\.)?academicwebpublishers\.org$',
r'(.*\.)?academievoorcontinuverbeteren\.nl$',
(r'(.*\.)?academyirmbr\.com$', ("XX", "YYYY", 7, 8.2)),
r'(.*\.)?academyjournals\.net$',
r'(.*\.)?academyofideas\.com$',
r'(.*\.)?academypublish\.org$'
]
class TestCls(RegExprList):
def load_regexps(self) -> list[tuple[str, tuple]] | list[str]:
return test_list
mylist = TestCls()
string = "aa.www.academyirmbr.com"
print(f"matches in '{string}' ...")
for m, tpl in mylist.finditer(string):
print(f" regexp: {tpl[0]} // match: {m.string[m.start():m.end()]} // objects related to regexp: {tpl}")
if __name__ == "__main__":
test_POC()

View File

@ -20,7 +20,7 @@ if (next_call_ts == false or next_call_ts == nil) then
-- 2/ the next call is a random time between start_after_from and start_after_to
local initial_delay = math.random(start_after_from, start_after_to)
redis.call('SET', redis_key, now + initial_delay)
return { false, delay }
return { false, initial_delay }
end
-- next_call_ts is defined

View File

@ -23,7 +23,7 @@ def name_to_iso4217(name):
currency = CURRENCIES['names'].get(name, [name])
if isinstance(currency, str):
return currency
return currency[0]
return currency[-1]
def iso4217_to_name(iso4217, language):

View File

@ -35,6 +35,9 @@ search:
autocomplete: ""
# minimun characters to type before autocompleter starts
autocomplete_min: 4
# backend for the favicon near URL in search results.
# Available resolvers: "allesedv", "duckduckgo", "google", "yandex" - leave blank to turn it off by default.
favicon_resolver: ""
# Default search language - leave blank to detect from browser information or
# use codes from 'languages.py'
default_lang: "auto"
@ -223,15 +226,12 @@ outgoing:
# - 'Hash plugin'
# - 'Self Information'
# - 'Tracker URL remover'
# - 'Unit converter plugin'
# - 'Ahmia blacklist' # activation depends on outgoing.using_tor_proxy
# # these plugins are disabled if nothing is configured ..
# - 'Hostnames plugin' # see 'hostnames' configuration below
# - 'Open Access DOI rewrite'
# - 'Tor check plugin'
# # Read the docs before activate: auto-detection of the language could be
# # detrimental to users expectations / users can activate the plugin in the
# # preferences if they want.
# - 'Autodetect search language'
# Configuration of the "Hostnames plugin":
#
@ -1280,6 +1280,12 @@ engines:
require_api_key: false
results: JSON
- name: openlibrary
engine: openlibrary
shortcut: ol
timeout: 5
disabled: true
- name: openmeteo
engine: open_meteo
shortcut: om

View File

@ -156,6 +156,7 @@ SCHEMA = {
'safe_search': SettingsValue((0, 1, 2), 0),
'autocomplete': SettingsValue(str, ''),
'autocomplete_min': SettingsValue(int, 4),
'favicon_resolver': SettingsValue(str, ''),
'default_lang': SettingsValue(tuple(SXNG_LOCALE_TAGS + ['']), ''),
'languages': SettingSublistValue(SXNG_LOCALE_TAGS, SXNG_LOCALE_TAGS),
'ban_time_on_fail': SettingsValue(numbers.Real, 5),

323
searx/sqlitedb.py Normal file
View File

@ -0,0 +1,323 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Implementations to make access to SQLite databases a little more convenient.
:py:obj:`SQLiteAppl`
Abstract class with which DB applications can be implemented.
:py:obj:`SQLiteProperties`:
Class to manage properties stored in a database.
----
"""
from __future__ import annotations
import sys
import re
import sqlite3
import threading
import abc
from searx import logger
logger = logger.getChild('sqlitedb')
class SQLiteAppl(abc.ABC):
"""Abstract base class for implementing convenient DB access in SQLite
applications. In the constructor, a :py:obj:`SQLiteProperties` instance is
already aggregated under ``self.properties``."""
DDL_CREATE_TABLES: dict[str, str] = {}
DB_SCHEMA: int = 1
"""As soon as changes are made to the DB schema, the version number must be
increased. Changes to the version number require the DB to be recreated (or
migrated / if an migration path exists and is implemented)."""
SQLITE_THREADING_MODE = {
0: "single-thread",
1: "multi-thread",
3: "serialized"}[sqlite3.threadsafety] # fmt:skip
"""Threading mode of the SQLite library. Depends on the options used at
compile time and is different for different distributions and architectures.
Possible values are 0:``single-thread``, 1:``multi-thread``,
3:``serialized`` (see :py:obj:`sqlite3.threadsafety`). Pre- Python 3.11
this value was hard coded to 1.
Depending on this value, optimizations are made, e.g. in serialized mode
it is not necessary to create a separate DB connector for each thread.
"""
SQLITE_JOURNAL_MODE = "WAL"
SQLITE_CONNECT_ARGS = {
# "timeout": 5.0,
# "detect_types": 0,
"check_same_thread": bool(SQLITE_THREADING_MODE != "serialized"),
"cached_statements": 0, # https://github.com/python/cpython/issues/118172
# "uri": False,
"autocommit": False,
} # fmt:skip
"""Connection arguments (:py:obj:`sqlite3.connect`)
``check_same_thread``:
Is disabled by default when :py:obj:`SQLITE_THREADING_MODE` is
``serialized``. The check is more of a hindrance in this case because it
would prevent a DB connector from being used in multiple threads.
``autocommit``:
Is disabled by default. Note: autocommit option has been added in Python
3.12.
``cached_statements``:
Is set to ``0`` by default. Note: Python 3.12+ fetch result are not
consistent in multi-threading application and causing an API misuse error.
The multithreading use in SQLiteAppl is intended and supported if
threadsafety is set to 3 (aka "serialized"). CPython supports serialized
from version 3.12 on, but unfortunately only with errors:
- https://github.com/python/cpython/issues/118172
- https://github.com/python/cpython/issues/123873
The workaround for SQLite3 multithreading cache inconsistency ist to set
option ``cached_statements`` to ``0`` by default.
"""
def __init__(self, db_url):
self.db_url = db_url
self.properties = SQLiteProperties(db_url)
self.thread_local = threading.local()
self._init_done = False
self._compatibility()
def _compatibility(self):
if self.SQLITE_THREADING_MODE == "serialized":
self._DB = None
else:
msg = (
f"SQLite library is compiled with {self.SQLITE_THREADING_MODE} mode,"
" read https://docs.python.org/3/library/sqlite3.html#sqlite3.threadsafety"
)
if threading.active_count() > 1:
logger.error(msg)
else:
logger.warning(msg)
if sqlite3.sqlite_version_info <= (3, 35):
# See "Generalize UPSERT:" in https://sqlite.org/releaselog/3_35_0.html
logger.critical(
"SQLite runtime library version %s is not supported (require >= 3.35)", sqlite3.sqlite_version
)
def connect(self) -> sqlite3.Connection:
"""Creates a new DB connection (:py:obj:`SQLITE_CONNECT_ARGS`). If not
already done, the DB schema is set up
"""
if sys.version_info < (3, 12):
# Prior Python 3.12 there is no "autocommit" option
self.SQLITE_CONNECT_ARGS.pop("autocommit", None)
self.init()
logger.debug("%s: connect to DB: %s // %s", self.__class__.__name__, self.db_url, self.SQLITE_CONNECT_ARGS)
conn = sqlite3.Connection(self.db_url, **self.SQLITE_CONNECT_ARGS) # type: ignore
conn.execute(f"PRAGMA journal_mode={self.SQLITE_JOURNAL_MODE}")
self.register_functions(conn)
return conn
def register_functions(self, conn):
"""Create user-defined_ SQL functions.
``REGEXP(<pattern>, <field>)`` : 0 | 1
`re.search`_ returns (int) 1 for a match and 0 for none match of
``<pattern>`` in ``<field>``.
.. code:: sql
SELECT '12' AS field WHERE REGEXP('^[0-9][0-9]$', field)
-- 12
SELECT REGEXP('[0-9][0-9]', 'X12Y')
-- 1
SELECT REGEXP('[0-9][0-9]', 'X1Y')
-- 0
.. _user-defined: https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.create_function
.. _deterministic: https://sqlite.org/deterministic.html
.. _re.search: https://docs.python.org/3/library/re.html#re.search
"""
conn.create_function('regexp', 2, lambda x, y: 1 if re.search(x, y) else 0, deterministic=True)
@property
def DB(self) -> sqlite3.Connection:
"""Provides a DB connection. The connection is a *singleton* and
therefore well suited for read access. If
:py:obj:`SQLITE_THREADING_MODE` is ``serialized`` only one DB connection
is created for all threads.
.. note::
For dedicated `transaction control`_, it is recommended to create a
new connection (:py:obj:`SQLiteAppl.connect`).
.. _transaction control:
https://docs.python.org/3/library/sqlite3.html#sqlite3-controlling-transactions
"""
if getattr(self.thread_local, 'DB', None) is None:
self.thread_local.DB = self.connect()
# Theoretically it is possible to reuse the DB cursor across threads as
# of Python 3.12, in practice the threading of the cursor seems to me to
# be so faulty that I prefer to establish one connection per thread
self.thread_local.DB.commit()
return self.thread_local.DB
# In "serialized" mode, SQLite can be safely used by multiple threads
# with no restriction.
#
# if self.SQLITE_THREADING_MODE != "serialized":
# if getattr(self.thread_local, 'DB', None) is None:
# self.thread_local.DB = self.connect()
# return self.thread_local.DB
#
# if self._DB is None:
# self._DB = self.connect() # pylint: disable=attribute-defined-outside-init
# return self._DB
def init(self):
"""Initializes the DB schema and properties, is only executed once even
if called several times."""
if self._init_done:
return
self._init_done = True
logger.debug("init DB: %s", self.db_url)
self.properties.init()
ver = self.properties("DB_SCHEMA")
if ver is None:
with self.properties.DB:
self.create_schema(self.properties.DB)
else:
ver = int(ver)
if ver != self.DB_SCHEMA:
raise sqlite3.DatabaseError("Expected DB schema v%s, DB schema is v%s" % (self.DB_SCHEMA, ver))
logger.debug("DB_SCHEMA = %s", ver)
def create_schema(self, conn):
logger.debug("create schema ..")
with conn:
for table_name, sql in self.DDL_CREATE_TABLES.items():
conn.execute(sql)
self.properties.set(f"Table {table_name} created", table_name)
self.properties.set("DB_SCHEMA", self.DB_SCHEMA)
self.properties.set("LAST_MAINTENANCE", "")
class SQLiteProperties(SQLiteAppl):
"""Simple class to manage properties of a DB application in the DB. The
object has its own DB connection and transaction area.
.. code:: sql
CREATE TABLE IF NOT EXISTS properties (
name TEXT,
value TEXT,
m_time INTEGER DEFAULT (strftime('%s', 'now')),
PRIMARY KEY (name))
"""
SQLITE_JOURNAL_MODE = "WAL"
DDL_PROPERTIES = """\
CREATE TABLE IF NOT EXISTS properties (
name TEXT,
value TEXT,
m_time INTEGER DEFAULT (strftime('%s', 'now')), -- last modified (unix epoch) time in sec.
PRIMARY KEY (name))"""
"""Table to store properties of the DB application"""
SQL_GET = "SELECT value FROM properties WHERE name = ?"
SQL_M_TIME = "SELECT m_time FROM properties WHERE name = ?"
SQL_SET = (
"INSERT INTO properties (name, value) VALUES (?, ?)"
" ON CONFLICT(name) DO UPDATE"
" SET value=excluded.value, m_time=strftime('%s', 'now')"
)
SQL_TABLE_EXISTS = (
"SELECT name FROM sqlite_master"
" WHERE type='table' AND name='properties'"
) # fmt:skip
SQLITE_CONNECT_ARGS = dict(SQLiteAppl.SQLITE_CONNECT_ARGS)
SQLITE_CONNECT_ARGS["autocommit"] = True # This option has no effect before Python 3.12
def __init__(self, db_url: str): # pylint: disable=super-init-not-called
self.db_url = db_url
self.thread_local = threading.local()
self._init_done = False
self._compatibility()
def init(self):
"""Initializes DB schema of the properties in the DB."""
if self._init_done:
return
self._init_done = True
logger.debug("init properties of DB: %s", self.db_url)
with self.DB as conn:
res = conn.execute(self.SQL_TABLE_EXISTS)
if res.fetchone() is None: # DB schema needs to be be created
self.create_schema(conn)
def __call__(self, name, default=None):
"""Returns the value of the property ``name`` or ``default`` if property
not exists in DB."""
res = self.DB.execute(self.SQL_GET, (name,)).fetchone()
if res is None:
return default
return res[0]
def set(self, name, value):
"""Set ``value`` of property ``name`` in DB. If property already
exists, update the ``m_time`` (and the value)."""
self.DB.execute(self.SQL_SET, (name, value))
if sys.version_info <= (3, 12):
# Prior Python 3.12 there is no "autocommit" option / lets commit
# explicitely.
self.DB.commit()
def row(self, name, default=None):
"""Returns the DB row of property ``name`` or ``default`` if property
not exists in DB."""
cur = self.DB.cursor()
cur.execute("SELECT * FROM properties WHERE name = ?", (name,))
res = cur.fetchone()
if res is None:
return default
col_names = [column[0] for column in cur.description]
return dict(zip(col_names, res))
def m_time(self, name, default: int = 0) -> int:
"""Last modification time of this property."""
res = self.DB.execute(self.SQL_M_TIME, (name,)).fetchone()
if res is None:
return default
return int(res[0])
def create_schema(self, conn):
with conn:
conn.execute(self.DDL_PROPERTIES)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path fill="#58f" d="M11 20.85a.92.92 0 0 1-1.1.93A10 10 0 0 1 2.06 13c-.06-.55.4-1 .95-1h3a1 1 0 0 1 1 1 3 3 0 0 0 3 3 1 1 0 0 1 1 1v3.85Zm6-1.92c0 .77.83 1.23 1.42.74a10 10 0 0 0 2.03-2.32c.39-.61-.09-1.35-.81-1.35H18a1 1 0 0 0-1 1v1.93ZM12 2a10 10 0 0 1 6.65 2.53c.61.55.17 1.47-.65 1.47h-.15A2.85 2.85 0 0 0 15 8.85c0 .33-.18.62-.47.77l-.08.04a1 1 0 0 1-.9 0l-.08-.04a.85.85 0 0 1-.47-.77A2.85 2.85 0 0 0 10.15 6H10a1 1 0 0 1-1-1V3.2c0-.44.28-.84.7-.94C10.45 2.1 11.22 2 12 2Z"/>
<path fill="#58f" d="M3.42 10c-.63 0-1.1-.58-.9-1.18.6-1.8 1.7-3.36 3.12-4.53C6.2 3.82 7 4.26 7 5a3 3 0 0 0 3 3h.15c.47 0 .85.38.85.85 0 1.09.61 2.07 1.58 2.56l.08.04a3 3 0 0 0 2.68 0l.08-.04A2.85 2.85 0 0 0 17 8.85c0-.47.38-.85.85-.85h2.66c.4 0 .77.23.9.6a9.98 9.98 0 0 1 .52 4.6.94.94 0 0 1-.95.8H18a3 3 0 0 0-3 3v3.8c0 .44-.28.84-.7.94l-.2.04a.92.92 0 0 1-1.1-.93V17a3 3 0 0 0-3-3 1 1 0 0 1-1-1 3 3 0 0 0-3-3H3.42Z"/>
</svg>

After

Width:  |  Height:  |  Size: 989 B

View File

@ -123,6 +123,9 @@
--color-loading-indicator: rgba(255, 255, 255, 0.2);
--color-loading-indicator-gap: #fff;
--color-line-number: #64708d;
// Favicons Colors
--color-favicon-background-color: #ddd;
--color-favicon-border-color: #ccc;
}
.dark-themes() {
@ -235,6 +238,9 @@
--color-toolkit-loader-borderleft: rgba(0, 0, 0, 0);
--color-doc-code: #ddd;
--color-doc-code-background: #4d5a6f;
// Favicons Colors
--color-favicon-background-color: #ddd;
--color-favicon-border-color: #ccc;
}
.black-themes() {
@ -271,7 +277,7 @@
@results-margin: 0.125rem;
@result-padding: 1rem;
@results-image-row-height: 12rem;
@results-image-row-height-phone: 6rem;
@results-image-row-height-phone: 10rem;
@search-width: 44rem;
// heigh of #search, see detail.less
@search-height: 7.6rem;

View File

@ -378,3 +378,12 @@ html.no-js #clear_search.hide_if_nojs {
#categories_container {
position: relative;
}
.favicon img {
height: 1.5rem;
width: 1.5rem;
border-radius: 10%;
background-color: var(--color-favicon-background-color);
border: 1px solid var(--color-favicon-border-color);
display: flex;
}

View File

@ -94,6 +94,10 @@
direction: initial;
text-align: right;
.result .url_header {
direction: rtl;
}
.result .url_wrapper {
justify-content: end;
}

View File

@ -170,6 +170,8 @@ article[data-vim-selected].category-social {
.result {
margin: @results-margin 0;
padding: @result-padding;
box-sizing: border-box;
width: 100%;
.ltr-border-left(0.2rem solid transparent);
h3 {
@ -232,8 +234,14 @@ article[data-vim-selected].category-social {
}
}
.url_header {
display: flex;
gap: 0.5rem;
}
.url_wrapper {
display: flex;
align-items: center;
font-size: 1rem;
color: var(--color-result-url-font);
flex-wrap: nowrap;
@ -448,6 +456,7 @@ article[data-vim-selected].category-social {
margin: 0.25rem;
border: none !important;
height: @results-image-row-height;
width: unset;
& > a {
position: relative;
@ -1090,7 +1099,8 @@ summary.title {
.result {
background: var(--color-result-background);
border: 1px solid var(--color-result-background);
margin: 1rem 10px;
margin: 1rem 2%;
width: 96%;
.rounded-corners;
}
@ -1098,6 +1108,7 @@ summary.title {
margin: 0;
height: @results-image-row-height-phone;
background: var(--color-base-background-mobile);
width: unset;
}
.infobox {

View File

@ -16,9 +16,9 @@ sxng_locales = (
('bg', 'Български', '', 'Bulgarian', '\U0001f310'),
('bg-BG', 'Български', 'България', 'Bulgarian', '\U0001f1e7\U0001f1ec'),
('ca', 'Català', '', 'Catalan', '\U0001f310'),
('ca-ES', 'Català', 'Espanya', 'Catalan', '\U0001f1ea\U0001f1f8'),
('cs', 'Čeština', '', 'Czech', '\U0001f310'),
('cs-CZ', 'Čeština', 'Česko', 'Czech', '\U0001f1e8\U0001f1ff'),
('cy', 'Cymraeg', '', 'Welsh', '\U0001f310'),
('da', 'Dansk', '', 'Danish', '\U0001f310'),
('da-DK', 'Dansk', 'Danmark', 'Danish', '\U0001f1e9\U0001f1f0'),
('de', 'Deutsch', '', 'German', '\U0001f310'),
@ -56,6 +56,8 @@ sxng_locales = (
('fr-CA', 'Français', 'Canada', 'French', '\U0001f1e8\U0001f1e6'),
('fr-CH', 'Français', 'Suisse', 'French', '\U0001f1e8\U0001f1ed'),
('fr-FR', 'Français', 'France', 'French', '\U0001f1eb\U0001f1f7'),
('ga', 'Gaeilge', '', 'Irish', '\U0001f310'),
('gd', 'Gàidhlig', '', 'Scottish Gaelic', '\U0001f310'),
('gl', 'Galego', '', 'Galician', '\U0001f310'),
('he', 'עברית', '', 'Hebrew', '\U0001f1ee\U0001f1f1'),
('hi', 'हिन्दी', '', 'Hindi', '\U0001f310'),
@ -92,6 +94,7 @@ sxng_locales = (
('ru-RU', 'Русский', 'Россия', 'Russian', '\U0001f1f7\U0001f1fa'),
('sk', 'Slovenčina', '', 'Slovak', '\U0001f310'),
('sl', 'Slovenščina', '', 'Slovenian', '\U0001f310'),
('sq', 'Shqip', '', 'Albanian', '\U0001f310'),
('sv', 'Svenska', '', 'Swedish', '\U0001f310'),
('sv-SE', 'Svenska', 'Sverige', 'Swedish', '\U0001f1f8\U0001f1ea'),
('ta', 'தமிழ்', '', 'Tamil', '\U0001f310'),
@ -100,10 +103,8 @@ sxng_locales = (
('tr', 'Türkçe', '', 'Turkish', '\U0001f310'),
('tr-TR', 'Türkçe', 'Türkiye', 'Turkish', '\U0001f1f9\U0001f1f7'),
('uk', 'Українська', '', 'Ukrainian', '\U0001f310'),
('uk-UA', 'Українська', 'Україна', 'Ukrainian', '\U0001f1fa\U0001f1e6'),
('ur', 'اردو', '', 'Urdu', '\U0001f310'),
('vi', 'Tiếng Việt', '', 'Vietnamese', '\U0001f310'),
('vi-VN', 'Tiếng Việt', 'Việt Nam', 'Vietnamese', '\U0001f1fb\U0001f1f3'),
('zh', '中文', '', 'Chinese', '\U0001f310'),
('zh-CN', '中文', '中国', 'Chinese', '\U0001f1e8\U0001f1f3'),
('zh-HK', '中文', '中國香港特別行政區', 'Chinese', '\U0001f1ed\U0001f1f0'),

View File

@ -20,10 +20,15 @@
<!-- Draw result header -->
{% macro result_header(result, favicons, image_proxify) -%}
<article class="result {% if result['template'] %}result-{{ result.template|replace('.html', '') }}{% else %}result-default{% endif %} {% if result['category'] %}category-{{ result['category'] }}{% endif %}{% for e in result.engines %} {{ e }}{% endfor %}">
{{- result_open_link(result.url, "url_wrapper") -}}
{{- result_open_link(result.url, "url_header") -}}
{%- if favicon_resolver != "" %}
<div class="favicon"><img loading="lazy" src="{{ favicon_url(result.parsed_url.netloc) }}"></div>
{%- endif -%}
<div class="url_wrapper">
{%- for part in get_pretty_url(result.parsed_url) -%}
<span class="url_o{{loop.index}}"><span class="url_i{{loop.index}}">{{- part -}}</span></span>
{%- endfor %}
</div>
{{- result_close_link() -}}
{%- if result.thumbnail %}{{ result_open_link(result.url) }}<img class="thumbnail" src="{{ image_proxify(result.thumbnail) }}" title="{{ result.title|striptags }}" loading="lazy">{{ result_close_link() }}{% endif -%}
<h3>{{ result_link(result.url, result.title|safe) }}</h3>

View File

@ -173,6 +173,9 @@
{%- if 'autocomplete' not in locked_preferences -%}
{%- include 'simple/preferences/autocomplete.html' -%}
{%- endif -%}
{%- if 'favicon' not in locked_preferences -%}
{%- include 'simple/preferences/favicon.html' -%}
{%- endif -%}
{% if 'safesearch' not in locked_preferences %}
{%- include 'simple/preferences/safesearch.html' -%}
{%- endif -%}

View File

@ -0,0 +1,17 @@
<fieldset>{{- '' -}}
<legend id="pref_favicon_resolver">{{- _('Favicon Resolver') -}}</legend>{{- '' -}}
<div class="value">{{- '' -}}
<select name="favicon_resolver" aria-labelledby="pref_favicon_resolver">{{- '' -}}
<option value=""> - </option>
{%- for backend in favicon_resolver_names -%}
<option value="{{ backend }}"
{%- if backend == favicon_resolver %} selected="selected" {%- endif -%}>
{{- backend -}}
</option>
{%- endfor -%}
</select>{{- '' -}}
</div>{{- '' -}}
<div class="description">
{{- _('Display favicons near search results') -}}
</div>{{- '' -}}
</fieldset>{{- '' -}}

View File

@ -15,7 +15,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-09-28 15:23+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -346,28 +346,28 @@ msgstr "toe"
msgid "answered"
msgstr "geantwoord"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Geen item gevind"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Bron"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Fout met die laai van die volgende bladsy"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Ongeldige opstellings, redigeer asb jou voorkeure"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Ongeldige opstellings"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "soekfout"
@ -687,26 +687,26 @@ msgstr "Kontak instansie onderhouer"
msgid "Click on the magnifier to perform search"
msgstr "Kliek op die vergrootglas om 'n soektog te doen"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Lengte"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "sienings"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Outeur"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "gekas"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "gevolmagtig"
@ -781,27 +781,27 @@ msgstr "Algemeen"
msgid "Default categories"
msgstr "Verstek kategoriee"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Gebruikerskoppelvlak"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privaatheid"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Enjins"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Huidige gebruikte soekenjins"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Spesiale Navrae"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Koekies"
@ -1221,6 +1221,14 @@ msgstr "Gewig"
msgid "Max time"
msgstr "Maks tyd"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -22,18 +22,18 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-04 07:09+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>\n"
"Language-Team: Arabic <https://translate.codeberg.org/projects/searxng/"
"searxng/ar/>\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\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"
"Content-Type: text/plain; charset=utf-8\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 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -354,28 +354,28 @@ msgstr "مغلق"
msgid "answered"
msgstr "أُجيبت"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "تعذر العثور على عناصر"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "المصدر"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "حدث خلل أثناء تحميل الصفحة التالية"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "إنّ الإعدادات خاطئة، يرجى تعديل خياراتك"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "إعدادات غير صالحة"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "خطأ في البحث"
@ -690,26 +690,26 @@ msgstr "اتصال بالمشرف المخدم النموذجي"
msgid "Click on the magnifier to perform search"
msgstr "انقر على رمز المكبر للقيام بالبحث"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "الطول"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "المشاهدات"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "الكاتب"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "النسخة المخبأة"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "المخدم البروكسي"
@ -782,27 +782,27 @@ msgstr "الرئيسية"
msgid "Default categories"
msgstr "القوائم الإفتراضية"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "واجهة المستخدم"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "الخصوصية"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "المحركات"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "محركات البحث المُستخدَمة حاليًا"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "استفسارات خاصة"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "كعكات الكوكيز"
@ -1222,6 +1222,14 @@ msgstr "وَزن"
msgid "Max time"
msgstr "أقصى مدّة"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1965,3 +1973,4 @@ msgstr "إخفاء الفيديو"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "لم تتمكن محركات البحث من العثور على أية نتيجة"

View File

@ -12,20 +12,23 @@
# Salif Mehmed <mail@salif.eu>, 2023, 2024.
# return42 <return42@users.noreply.translate.codeberg.org>, 2024.
# krlsk <krlsk@users.noreply.translate.codeberg.org>, 2024.
# stoychevww <stoychevww@users.noreply.translate.codeberg.org>, 2024.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-05-25 08:18+0000\n"
"Last-Translator: krlsk <krlsk@users.noreply.translate.codeberg.org>\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-13 03:30+0000\n"
"Last-Translator: stoychevww <stoychevww@users.noreply.translate.codeberg.org>"
"\n"
"Language-Team: Bulgarian <https://translate.codeberg.org/projects/searxng/"
"searxng/bg/>\n"
"Language: bg\n"
"Language-Team: Bulgarian "
"<https://translate.codeberg.org/projects/searxng/searxng/bg/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -166,7 +169,7 @@ msgstr "тъмен"
#. STYLE_NAMES['BLACK']
#: searx/searxng.msg
msgid "black"
msgstr ""
msgstr "черно"
#. BRAND_CUSTOM_LINKS['UPTIME']
#: searx/searxng.msg
@ -334,40 +337,40 @@ msgstr "Автор"
#. SOCIAL_MEDIA_TERMS['THREAD OPEN']
#: searx/engines/discourse.py:149 searx/searxng.msg
msgid "open"
msgstr ""
msgstr "отворено"
#. SOCIAL_MEDIA_TERMS['THREAD CLOSED']
#: searx/engines/discourse.py:149 searx/searxng.msg
msgid "closed"
msgstr ""
msgstr "Затворено"
#. SOCIAL_MEDIA_TERMS['THREAD ANSWERED']
#: searx/engines/discourse.py:160 searx/searxng.msg
msgid "answered"
msgstr ""
msgstr "Отговорено"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Не е намерен артикул"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Източник"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Грешка при зареждането на следващата страница"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Неправилни настройки, моля проверете предпочитанията си"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Невалидни настройки"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "Грешка при търсенето"
@ -453,7 +456,7 @@ msgstr "Изчислете {functions} на аргументите"
#: searx/engines/mozhi.py:57
msgid "Synonyms"
msgstr ""
msgstr "Синоними"
#: searx/engines/openstreetmap.py:159
msgid "Get directions"
@ -678,26 +681,26 @@ msgstr "Контакт за връзка с поддържащия публич
msgid "Click on the magnifier to perform search"
msgstr "Кликнете лупичката, за да изпълните търсене"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Дължина"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Автор"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "кеширана"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "прекарана"
@ -774,27 +777,27 @@ msgstr "Общи"
msgid "Default categories"
msgstr "Първоначални категории"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Потребителски интерфейс"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Поверителност"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Търсачки"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Използвани търсачки в момента"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Специялни Запитвания"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Бисквитки"
@ -1219,6 +1222,14 @@ msgstr "Тегло"
msgid "Max time"
msgstr "Максимално време"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1967,4 +1978,3 @@ msgstr "скрий видеото"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "Търсачките не можаха да намерят резултати"

View File

@ -18,17 +18,17 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-04 07:09+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>\n"
"Language-Team: Bengali <https://translate.codeberg.org/projects/searxng/"
"searxng/bn/>\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
"Language: bn\n"
"Language-Team: Bengali "
"<https://translate.codeberg.org/projects/searxng/searxng/bn/>\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -349,28 +349,28 @@ msgstr "বন্ধ"
msgid "answered"
msgstr "উত্তরকৃত"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "কোন আইটেম পাওয়া যায়নি"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "উৎস"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "পরবর্তী পৃষ্ঠাটি লোড করায় ত্রুটি দেখা যাচ্ছে"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "অকেজো সেটিংস, অনুগ্রহ করে আপনার পছন্দগুলি সম্পাদনা করুন"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "অকেজো সেটিংস"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "সার্চ ত্রুটি"
@ -685,26 +685,26 @@ msgstr "ইন্সট্যান্স রক্ষণাবেক্ষণ
msgid "Click on the magnifier to perform search"
msgstr "অনুসন্ধান করতে ম্যাগনিফায়ার আইকনে ক্লিক করুন"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "দৈর্ঘ্য"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "ভিউ"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "লেখক"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "ক্যাশকৃত"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "প্রক্সিকৃত"
@ -781,27 +781,27 @@ msgstr "সাধারণ"
msgid "Default categories"
msgstr "ডিফল্ট বিভাগ"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "ব‍্যবহারকারীর সম্মুখে প্রদর্শিত"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "গোপনীয়তা"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "ইঞ্জিন"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "বর্তমানে ব্যবহৃত সার্চ ইঞ্জিন"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "বিশেষ প্রশ্ন"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "কুকি"
@ -1219,6 +1219,14 @@ msgstr "ওজন"
msgid "Max time"
msgstr "সর্বোচ্চ সময়"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1723,3 +1731,4 @@ msgstr "ভিডিও লুকিয়ে ফেলুন"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "ইঞ্জিন ফলাফল পুনরুদ্ধার করতে পারেছেনা"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2023-06-02 07:07+0000\n"
"Last-Translator: return42 <markus.heiser@darmarit.de>\n"
"Language: bo\n"
@ -340,28 +340,28 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "རྣམ་གྲངས་གང་ཡང་རྙེད་རྒྱུ་མ་བྱུང་།"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr ""
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།ཁྱེད་ཀྱིས་གདམ་ཀ་ལ་བཅོས་སྒྲིག་གཏོང་རོགས།"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "འཚོལ་བཤེར་ལ་ནོར་འཁྲུལ་བྱུང་།"
@ -658,26 +658,26 @@ msgstr ""
msgid "Click on the magnifier to perform search"
msgstr "ས་བོན་སྟེང་གི་སྦྲེལ་ཐག་ལ་རྡེབ་ནས་འཚོལ་བཤེར་གཏོང་།"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr ""
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr ""
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "འདྲ་བཤུས་རྒྱབ་ཚར།"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "མངག་བཅོལ་བྱེད་ཟིན།"
@ -750,27 +750,27 @@ msgstr "སྤྱི་བཏང་།"
msgid "Default categories"
msgstr "གཞི་བཞག་གི་རིགས།"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "མདུན་ངོས།"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "མི་སྒེར་གསང་དོན།"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "སྒུལ་བྱེད།"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "ཉེ་ལམ་སྤྱད་ཟིན་པའི་འཚོལ་བྱེད་སྒུལ་བྱེད།"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr ""
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "རྐང་རྗེས།"
@ -1182,6 +1182,14 @@ msgstr ""
msgid "Max time"
msgstr "མང་མཐའི་དུས་ཚོད།"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -21,7 +21,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-08-16 06:18+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -352,28 +352,28 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "No s'ha trobat cap element"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Origen"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "S'ha produït un error en carregar la següent pàgina"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "La configuració no és vàlida, editeu-la"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "La configuració no és vàlida"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "error de cerca"
@ -686,26 +686,26 @@ msgstr "Contacteu amb el mantenidor de la instància"
msgid "Click on the magnifier to perform search"
msgstr "Feu clic en la lupa per a executar la cerca"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Longitud"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Autor"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "en memòria cau"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "en servidor intermediari"
@ -782,27 +782,27 @@ msgstr "General"
msgid "Default categories"
msgstr "Categories predeterminades"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Interfície de l'usuari"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privadesa"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Motors de cerca"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Cercadors usats actualment"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Consultes especials"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Galetes"
@ -1223,6 +1223,14 @@ msgstr "Pes"
msgid "Max time"
msgstr "Temps màxim"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -18,8 +18,8 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-10-03 19:18+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-06 14:31+0000\n"
"Last-Translator: Fjuro <fjuro@alius.cz>\n"
"Language-Team: Czech <https://translate.codeberg.org/projects/searxng/"
"searxng/cs/>\n"
@ -350,28 +350,28 @@ msgstr "zavřené"
msgid "answered"
msgstr "zodpovězené"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Nic nenalezeno"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "zdroj"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Chyba při načítání další stránky"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Neplatné nastavení, upravte své předvolby"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Neplatné nastavení"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "chyba vyhledávání"
@ -686,26 +686,26 @@ msgstr "Kontaktujte správce instance"
msgid "Click on the magnifier to perform search"
msgstr "Vyhledávání provedete kliknutím na lupu"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Délka"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "Zhlédnutí"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Autor"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "archivovaná verze"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "přes proxy"
@ -780,27 +780,27 @@ msgstr "Obecné"
msgid "Default categories"
msgstr "Základní kategorie"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Uživatelské rozhraní"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Soukromí"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Vyhledávače"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Aktuálně používané vyhledávače"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Zvláštní dotazy"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookies"
@ -1220,6 +1220,14 @@ msgstr "Váha"
msgid "Max time"
msgstr "Max. čas"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Zobrazit ikony"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Zobrazit ikony webů vedle výsledků vyhledávání"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-07-28 14:18+0000\n"
"Last-Translator: EifionLlwyd "
"<EifionLlwyd@users.noreply.translate.codeberg.org>\n"
@ -346,28 +346,28 @@ msgstr "ar gau"
msgid "answered"
msgstr "wedi'i ateb"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Ni chanfuwyd eitem"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Ffynhonnell"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Gwall wrth lwytho'r dudalen nesaf"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Gosodiadau annilys, golygwch eich dewisiadau"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Gosodiadau annilys"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "gwall chwilio"
@ -687,26 +687,26 @@ msgstr "Cysylltu â chynhaliwr y gweinydd"
msgid "Click on the magnifier to perform search"
msgstr "Cliciwch ar y chwyddwydr i chwilio"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Hyd"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Awdur"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "wedi'i storio"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "wedi'i ddirprwyo"
@ -781,27 +781,27 @@ msgstr "Cyffredinol"
msgid "Default categories"
msgstr "Categorïau rhagosodedig"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Rhyngwyneb defnyddiwr"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Preifatrwydd"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Peiriannau"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Peiriannau a ddefnyddir ar hyn o bryd"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Ymholiadau arbennig"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Briwsion"
@ -1220,6 +1220,14 @@ msgstr "Pwysau"
msgid "Max time"
msgstr "Amser hiraf"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -9,21 +9,23 @@
# return42 <markus.heiser@darmarit.de>, 2023.
# return42 <return42@users.noreply.translate.codeberg.org>, 2024.
# lolmeOzzi <lolmeOzzi@users.noreply.translate.codeberg.org>, 2024.
# AndersNordh <AndersNordh@users.noreply.translate.codeberg.org>, 2024.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-09-05 06:18+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-08 13:41+0000\n"
"Last-Translator: AndersNordh <AndersNordh@users.noreply.translate.codeberg."
"org>\n"
"Language-Team: Danish <https://translate.codeberg.org/projects/searxng/"
"searxng/da/>\n"
"Language: da\n"
"Language-Team: Danish "
"<https://translate.codeberg.org/projects/searxng/searxng/da/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -164,7 +166,7 @@ msgstr "mørk"
#. STYLE_NAMES['BLACK']
#: searx/searxng.msg
msgid "black"
msgstr ""
msgstr "sort"
#. BRAND_CUSTOM_LINKS['UPTIME']
#: searx/searxng.msg
@ -337,35 +339,35 @@ msgstr "Åbn"
#. SOCIAL_MEDIA_TERMS['THREAD CLOSED']
#: searx/engines/discourse.py:149 searx/searxng.msg
msgid "closed"
msgstr ""
msgstr "lukket"
#. SOCIAL_MEDIA_TERMS['THREAD ANSWERED']
#: searx/engines/discourse.py:160 searx/searxng.msg
msgid "answered"
msgstr ""
msgstr "svaret"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Intet fundet"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Kilde"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Fejl ved indlæsning af den næste side"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Ugyldige indstillinger, redigér venligst dine valg"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Ugyldig indstilling"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "søgefejl"
@ -451,7 +453,7 @@ msgstr "Beregn {functions} af parametrene"
#: searx/engines/mozhi.py:57
msgid "Synonyms"
msgstr ""
msgstr "Synonymer"
#: searx/engines/openstreetmap.py:159
msgid "Get directions"
@ -539,11 +541,13 @@ msgstr "hash-digest"
#: searx/plugins/hostnames.py:103
msgid "Hostnames plugin"
msgstr ""
msgstr "Værtsnavne plugin"
#: searx/plugins/hostnames.py:104
msgid "Rewrite hostnames, remove results or prioritize them based on the hostname"
msgstr ""
"Omskriv værtsnavne, fjern resultater eller prioriter dem baseret på "
"værtsnavnet"
#: searx/plugins/oa_doi_rewrite.py:12
msgid "Open Access DOI rewrite"
@ -571,11 +575,11 @@ msgstr ""
#: searx/plugins/self_info.py:28
msgid "Your IP is: "
msgstr ""
msgstr "Din IP er: "
#: searx/plugins/self_info.py:31
msgid "Your user-agent is: "
msgstr ""
msgstr "Din brugeragent er: "
#: searx/plugins/tor_check.py:24
msgid "Tor check plugin"
@ -679,26 +683,26 @@ msgstr "Kontakt tilbyderen af instansen"
msgid "Click on the magnifier to perform search"
msgstr "Klik på forstørrelsesglasset for at udføre søgning"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Længde"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
msgstr "Visninger"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Forfatter"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "cachet"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "viderestillet"
@ -775,27 +779,27 @@ msgstr "Generelt"
msgid "Default categories"
msgstr "Standardkategorier"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Brugerinterface"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privatliv"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Søgemaskiner"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Pt. anvendte søgemaskiner"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Specielle Søgetermer"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookies"
@ -1216,6 +1220,14 @@ msgstr "Vægt"
msgid "Max time"
msgstr "Maks-tid"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Favicon resolver"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Vis favicons i nærheden af søgeresultater"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1971,4 +1983,3 @@ msgstr "skjul video"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "Søgemotorer kan ikke hente resultater"

View File

@ -26,8 +26,8 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-10-04 07:09+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-06 14:31+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>\n"
"Language-Team: German <https://translate.codeberg.org/projects/searxng/"
"searxng/de/>\n"
@ -357,28 +357,28 @@ msgstr "geschlossen"
msgid "answered"
msgstr "beantwortet"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Keine Einträge gefunden"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Quelle"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Fehler beim Laden der nächsten Seite"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Ungültige Einstellungen, bitte Einstellungen ändern"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Ungültige Einstellungen"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "Suchfehler"
@ -698,26 +698,26 @@ msgstr "Kontakt zum Betreuer der Instanz"
msgid "Click on the magnifier to perform search"
msgstr "klicke auf die Lupe, um die Suche zu starten"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Länge"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "Aufrufe"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Autor"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "Im Cache"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proxy"
@ -796,27 +796,27 @@ msgstr "Allgemein"
msgid "Default categories"
msgstr "Standardkategorien"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Benutzeroberfläche"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privatsphäre"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Suchmaschinen"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Aktuell benutzte Suchmaschinen"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Besondere Abfragen"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookies"
@ -1237,6 +1237,14 @@ msgstr "Gewichtung"
msgid "Max time"
msgstr "max. Zeit"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Favicon Anbieter"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Anzeigen der Favicons neben dem Suchergebnis"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2022-11-04 07:18+0000\n"
"Last-Translator: Landhoo School Students "
"<landhooschoolstudents@gmail.com>\n"
@ -338,28 +338,28 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr ""
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr ""
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr ""
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr ""
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr ""
@ -656,26 +656,26 @@ msgstr ""
msgid "Click on the magnifier to perform search"
msgstr ""
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr ""
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr ""
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr ""
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr ""
@ -748,27 +748,27 @@ msgstr ""
msgid "Default categories"
msgstr ""
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr ""
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr ""
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr ""
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr ""
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr ""
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr ""
@ -1180,6 +1180,14 @@ msgstr ""
msgid "Max time"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -17,7 +17,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-09-05 06:18+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -348,28 +348,28 @@ msgstr "κλειστό"
msgid "answered"
msgstr "απάντησε"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Δεν βρέθηκαν αντικείμενα"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Πηγή"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Σφάλμα φόρτωσης της επόμενης σελίδας"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Μη έγκυρες ρυθμίσεις, παρακαλούμε ελέγξτε τις προτιμήσεις σας"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Μη έγκυρες ρυθμίσεις"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "σφάλμα αναζήτησης"
@ -689,26 +689,26 @@ msgstr "Επικοινωνήστε με τον συντηρητή αυτής τ
msgid "Click on the magnifier to perform search"
msgstr "Κάντε κλικ στο μεγεθυντικό φακό για να πραγματοποιήσετε αναζήτηση"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Μήκος"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Συγγραφέας"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "προσωρινά αποθηκευμένο"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "Διαμεσολαβημένα"
@ -787,27 +787,27 @@ msgstr "Γενικά"
msgid "Default categories"
msgstr "Προεπιλεγμένες κατηγορίες"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Διεπαφή χρήστη"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Ιδιωτικότητα"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Μηχανές"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Μηχανές αναζήτησης που χρησιμοποιούνται"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Ειδικά Ερωτήματα"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookies"
@ -1233,6 +1233,14 @@ msgstr "Βάρος"
msgid "Max time"
msgstr "Μέγιστος χρόνος"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2014-01-30 15:22+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
@ -336,28 +336,28 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr ""
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr ""
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr ""
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr ""
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr ""
@ -654,26 +654,26 @@ msgstr ""
msgid "Click on the magnifier to perform search"
msgstr ""
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr ""
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr ""
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr ""
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr ""
@ -746,27 +746,27 @@ msgstr ""
msgid "Default categories"
msgstr ""
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr ""
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr ""
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr ""
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr ""
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr ""
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr ""
@ -1178,6 +1178,14 @@ msgstr ""
msgid "Max time"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -17,7 +17,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-09-05 06:18+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -348,28 +348,28 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Nenio trovita"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Fonto"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Eraro dum la ŝarĝado de la sekvan paĝon"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Nevalidaj agordoj, bonvolu redaktu viajn agordojn"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Nevalidaj agordoj"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "serĉa eraro"
@ -684,26 +684,26 @@ msgstr "Kontaktu instancon prizorganto"
msgid "Click on the magnifier to perform search"
msgstr "Alklaku la lupeon por serĉi"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Longo"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Verkisto"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "kaŝmemorigita"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "prokurata"
@ -776,27 +776,27 @@ msgstr "Ĝenerala"
msgid "Default categories"
msgstr "Defaŭltaj kategorioj"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Fasado"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privateco"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Serĉiloj"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Aktuale uzataj serĉiloj"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Specialaj Demandoj"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Kuketoj"
@ -1216,6 +1216,14 @@ msgstr "Pezo"
msgid "Max time"
msgstr "Maksimuma tempo"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -33,20 +33,22 @@
# tiziodcaio <tiziodcaio@users.noreply.translate.codeberg.org>, 2024.
# return42 <return42@users.noreply.translate.codeberg.org>, 2024.
# kny5 <kny5@users.noreply.translate.codeberg.org>, 2024.
# Atul_Eterno <Atul_Eterno@users.noreply.translate.codeberg.org>, 2024.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-09-15 14:18+0000\n"
"Last-Translator: kny5 <kny5@users.noreply.translate.codeberg.org>\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-11-14 14:07+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>\n"
"Language-Team: Spanish <https://translate.codeberg.org/projects/searxng/"
"searxng/es/>\n"
"Language: es\n"
"Language-Team: Spanish "
"<https://translate.codeberg.org/projects/searxng/searxng/es/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.8.1\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -152,7 +154,7 @@ msgstr "preguntas y respuestas"
#. CATEGORY_GROUPS['REPOS']
#: searx/searxng.msg
msgid "repos"
msgstr "repos"
msgstr "repositorios"
#. CATEGORY_GROUPS['SOFTWARE_WIKIS']
#: searx/searxng.msg
@ -187,7 +189,7 @@ msgstr "oscuro"
#. STYLE_NAMES['BLACK']
#: searx/searxng.msg
msgid "black"
msgstr ""
msgstr "negro"
#. BRAND_CUSTOM_LINKS['UPTIME']
#: searx/searxng.msg
@ -367,28 +369,28 @@ msgstr "cerrar"
msgid "answered"
msgstr "contestado"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Ningún artículo encontrado"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Fuente"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Error al cargar la siguiente página"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Ajustes inválidos, por favor, cambia tus preferencias"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Ajustes inválidos"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "error en la búsqueda"
@ -474,7 +476,7 @@ msgstr "Calcular las funciones {functions} de parámetros dados"
#: searx/engines/mozhi.py:57
msgid "Synonyms"
msgstr ""
msgstr "Sinónimos"
#: searx/engines/openstreetmap.py:159
msgid "Get directions"
@ -706,26 +708,26 @@ msgstr "Contactar al mantenedor de la instancia"
msgid "Click on the magnifier to perform search"
msgstr "Haz clic en la lupa para realizar la búsqueda"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Longitud"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "Visualizaciones"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Autor"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "en caché"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "por un proxy"
@ -800,27 +802,27 @@ msgstr "General"
msgid "Default categories"
msgstr "Categorías predeterminadas"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Interfaz de usuario"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privacidad"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Motores"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Motores de búsqueda actualmente en uso"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Consultas Especiales"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookies"
@ -1241,6 +1243,14 @@ msgstr "Peso"
msgid "Max time"
msgstr "Tiempo máximo"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Buscador de favicon"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Mostrar los favicons al lado de los resultados de búsqueda"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -2009,4 +2019,3 @@ msgstr "ocultar video"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "Los motores no pueden obtener resultados"

View File

@ -16,17 +16,18 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-10-03 11:11+0000\n"
"Last-Translator: Priit Jõerüüt "
"<jrtcdbrg@users.noreply.translate.codeberg.org>\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-21 20:07+0000\n"
"Last-Translator: Priit Jõerüüt <jrtcdbrg@users.noreply.translate.codeberg."
"org>\n"
"Language-Team: Estonian <https://translate.codeberg.org/projects/searxng/"
"searxng/et/>\n"
"Language: et\n"
"Language-Team: Estonian "
"<https://translate.codeberg.org/projects/searxng/searxng/et/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -347,28 +348,28 @@ msgstr "suletud"
msgid "answered"
msgstr "vastatud"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Üksust ei leitud"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Allikas"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Viga järgmise lehekülje laadimisel"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Sobimatud seaded, palun muuda oma eelistusi"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Sobimatud seaded"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "otsingu viga"
@ -454,7 +455,7 @@ msgstr "Arvuta argumentide {functions}"
#: searx/engines/mozhi.py:57
msgid "Synonyms"
msgstr ""
msgstr "Sünonüümid"
#: searx/engines/openstreetmap.py:159
msgid "Get directions"
@ -685,26 +686,26 @@ msgstr "Võta ühendust serveri haldajaga"
msgid "Click on the magnifier to perform search"
msgstr "Otsingu teostamiseks klõpsa luubile"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Pikkus"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "Vaateid"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Autor"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "vahemälus"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proksiserveris"
@ -781,27 +782,27 @@ msgstr "Üldine"
msgid "Default categories"
msgstr "Vaikimisi kategooriad"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Kasutajaliides"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Privaatsus"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Otsingumootorid"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Hetkel kasutatud otsingumootorid"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Spetsiaalsed päringud"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Küpsised"
@ -1154,8 +1155,8 @@ msgid ""
"Note: specifying custom settings in the search URL can reduce privacy by "
"leaking data to the clicked result sites."
msgstr ""
"Märkus: täpsemate seadete määramine otsingu URLis võib vähendada "
"privaatsust, lekitades andmed klõpsatud tulemuste saitidele."
"Märkus: lekitades andmed klõpsatud tulemuste saitidele võib täpsemate "
"seadete määramine otsingu URLis vähendada privaatsust."
#: searx/templates/simple/preferences/cookies.html:35
msgid "URL to restore your preferences in another browser"
@ -1221,6 +1222,14 @@ msgstr "Kaal"
msgid "Max time"
msgstr "Maksimaalne aeg"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Saidiikoonide kuvamine"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Kuva otsingutulemuste kõrval saidiikoone"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1954,4 +1963,3 @@ msgstr "peida video"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "Otsingumootorid ei anna päringutele vastuseid"

View File

@ -16,7 +16,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-09-09 11:18+0000\n"
"Last-Translator: alexgabi <alexgabi@users.noreply.translate.codeberg.org>"
"\n"
@ -347,28 +347,28 @@ msgstr "itxita"
msgid "answered"
msgstr "erantzunda"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Ez da elementurik aurkitu"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Iturria"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Errorea hurrengo orrialdea kargatzean"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Ezarpen baliogabeak, editatu zure hobespenak"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Ezarpen baliogabeak"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "bilaketa akatsa"
@ -685,26 +685,26 @@ msgstr "Instantziaren mantentzailearekin harremanetan jarri"
msgid "Click on the magnifier to perform search"
msgstr "Lupan sakatu bilaketa egiteko"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Luzera"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "Ikuspegiak"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Egilea"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "cachean gordeta"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proxyan gordeta"
@ -777,27 +777,27 @@ msgstr "Orokorra"
msgid "Default categories"
msgstr "Lehenetsitako kategoriak"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Erabiltzailearen interfazea"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Pribatutasuna"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Bilatzaileak"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Erabiltzen ari diren bilatzaileak"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Kontsulta bereziak"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Cookieak"
@ -1217,6 +1217,14 @@ msgstr "Pisua"
msgid "Max time"
msgstr "Gehienezko denbora"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -21,7 +21,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-09-16 18:18+0000\n"
"Last-Translator: MPBDev <MPBDev@users.noreply.translate.codeberg.org>\n"
"Language: fa_IR\n"
@ -358,28 +358,28 @@ msgstr "بسته شده"
msgid "answered"
msgstr "جواب داده شده"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "چیزی پیدا نشد"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "منبع"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "خطا در بارگزاری صفحه جدید"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "تنظیمات نادرست است، لطفا تنظیمات جستجو را تغییر دهید"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "تنظیمات نادرست"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "خطای جست‌وجو"
@ -700,26 +700,26 @@ msgstr "تماس با مسئول‌نگهداری نمونه"
msgid "Click on the magnifier to perform search"
msgstr "برای انجام جست‌وجو روی ذره‌بین کلیک کنید"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "طول"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr "بازدید‌ها"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "نویسنده"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "جاسازی‌شده"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "پروکسی‌شده"
@ -794,27 +794,27 @@ msgstr "کلی"
msgid "Default categories"
msgstr "دسته‌بندی‌های پیش‌گزیده"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "رابط کاربری"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "حریم شخصی"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "موتورها"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "موتور جستجو های در حال استفاده"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "مقدارهای ویژه"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "کلوچک‌ها"
@ -1232,6 +1232,14 @@ msgstr "وزن"
msgid "Max time"
msgstr "زمان بیشینه"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr ""
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr ""
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "

View File

@ -12,21 +12,23 @@
# Implosion <Implosion@users.noreply.translate.codeberg.org>, 2024.
# artnay <artnay@users.noreply.translate.codeberg.org>, 2024.
# jonkke9 <jonkke9@users.noreply.translate.codeberg.org>, 2024.
# Ricky-Tigg <Ricky-Tigg@users.noreply.translate.codeberg.org>, 2024.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-10-03 11:11+0000\n"
"PO-Revision-Date: 2024-09-05 06:18+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"POT-Creation-Date: 2024-10-05 06:24+0000\n"
"PO-Revision-Date: 2024-10-08 13:41+0000\n"
"Last-Translator: Ricky-Tigg <Ricky-Tigg@users.noreply.translate.codeberg.org>"
"\n"
"Language-Team: Finnish <https://translate.codeberg.org/projects/searxng/"
"searxng/fi/>\n"
"Language: fi\n"
"Language-Team: Finnish "
"<https://translate.codeberg.org/projects/searxng/searxng/fi/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.7.2\n"
"Generated-By: Babel 2.16.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -167,7 +169,7 @@ msgstr "tumma"
#. STYLE_NAMES['BLACK']
#: searx/searxng.msg
msgid "black"
msgstr ""
msgstr "musta"
#. BRAND_CUSTOM_LINKS['UPTIME']
#: searx/searxng.msg
@ -335,7 +337,7 @@ msgstr "tekijä"
#. SOCIAL_MEDIA_TERMS['THREAD OPEN']
#: searx/engines/discourse.py:149 searx/searxng.msg
msgid "open"
msgstr ""
msgstr "Avaa"
#. SOCIAL_MEDIA_TERMS['THREAD CLOSED']
#: searx/engines/discourse.py:149 searx/searxng.msg
@ -347,28 +349,28 @@ msgstr "suljettu"
msgid "answered"
msgstr "vastattu"
#: searx/webapp.py:330
#: searx/webapp.py:332
msgid "No item found"
msgstr "Tietuetta ei löytynyt"
#: searx/engines/qwant.py:288
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:332
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:334
msgid "Source"
msgstr "Lähde"
#: searx/webapp.py:334
#: searx/webapp.py:336
msgid "Error loading the next page"
msgstr "Virhe ladattaessa seuraavaa sivua"
#: searx/webapp.py:491 searx/webapp.py:894
#: searx/webapp.py:495 searx/webapp.py:898
msgid "Invalid settings, please edit your preferences"
msgstr "Virheelliset asetukset, muokkaa siis asetuksia"
#: searx/webapp.py:507
#: searx/webapp.py:511
msgid "Invalid settings"
msgstr "Virheelliset asetukset"
#: searx/webapp.py:584 searx/webapp.py:666
#: searx/webapp.py:588 searx/webapp.py:670
msgid "search error"
msgstr "hakuvirhe"
@ -454,7 +456,7 @@ msgstr "Laske argumenttien {functions}"
#: searx/engines/mozhi.py:57
msgid "Synonyms"
msgstr ""
msgstr "Synonyymit"
#: searx/engines/openstreetmap.py:159
msgid "Get directions"
@ -541,11 +543,13 @@ msgstr "hash-digest"
#: searx/plugins/hostnames.py:103
msgid "Hostnames plugin"
msgstr ""
msgstr "Isäntänimien laajennus"
#: searx/plugins/hostnames.py:104
msgid "Rewrite hostnames, remove results or prioritize them based on the hostname"
msgstr ""
"Kirjoita isäntänimiä uudelleen, poista tuloksia tai priorisoi ne isäntänimen "
"perusteella"
#: searx/plugins/oa_doi_rewrite.py:12
msgid "Open Access DOI rewrite"
@ -683,26 +687,26 @@ msgstr "Ota yhteyttä palvelun ylläpitäjään"
msgid "Click on the magnifier to perform search"
msgstr "Napsauta suurennuslasia suorittaaksesi haun"
#: searx/templates/simple/macros.html:35
#: searx/templates/simple/macros.html:40
msgid "Length"
msgstr "Pituus"
#: searx/templates/simple/macros.html:36
#: searx/templates/simple/macros.html:41
msgid "Views"
msgstr ""
msgstr "Näkymät"
#: searx/templates/simple/macros.html:37
#: searx/templates/simple/macros.html:42
#: searx/templates/simple/result_templates/files.html:34
#: searx/templates/simple/result_templates/images.html:19
#: searx/templates/simple/result_templates/paper.html:6
msgid "Author"
msgstr "Tekijä"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "cached"
msgstr "välimuistissa"
#: searx/templates/simple/macros.html:45
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "välityspalvelimella"
@ -777,27 +781,27 @@ msgstr "Yleiset"
msgid "Default categories"
msgstr "Oletusluokat"
#: searx/templates/simple/preferences.html:187
#: searx/templates/simple/preferences.html:190
msgid "User interface"
msgstr "Käyttöliittymä"
#: searx/templates/simple/preferences.html:208
#: searx/templates/simple/preferences.html:211
msgid "Privacy"
msgstr "Yksityisyys"
#: searx/templates/simple/preferences.html:221
#: searx/templates/simple/preferences.html:224
msgid "Engines"
msgstr "Hakukoneet"
#: searx/templates/simple/preferences.html:223
#: searx/templates/simple/preferences.html:226
msgid "Currently used search engines"
msgstr "Nyt käytetyt hakukoneet"
#: searx/templates/simple/preferences.html:231
#: searx/templates/simple/preferences.html:234
msgid "Special Queries"
msgstr "Erityiset kyselyt"
#: searx/templates/simple/preferences.html:237
#: searx/templates/simple/preferences.html:240
msgid "Cookies"
msgstr "Evästeet"
@ -1069,7 +1073,7 @@ msgstr "Vaihtaa toiseen instanssiin:"
#: searx/templates/simple/messages/no_results.html:24
msgid "Search for another query or select another category."
msgstr ""
msgstr "Hae toista kyselyä tai valitse toinen luokka."
#: searx/templates/simple/messages/no_results.html:25
msgid "Go back to the previous page using the previous page button."
@ -1218,6 +1222,14 @@ msgstr "Paino"
msgid "Max time"
msgstr "Enimmäisaika"
#: searx/templates/simple/preferences/favicon.html:2
msgid "Favicon Resolver"
msgstr "Favicon-ratkaisija"
#: searx/templates/simple/preferences/favicon.html:15
msgid "Display favicons near search results"
msgstr "Näytä Faviconit hakutulosten lähellä"
#: searx/templates/simple/preferences/footer.html:2
msgid ""
"These settings are stored in your cookies, this allows us not to store "
@ -1969,4 +1981,3 @@ msgstr "piilota video"
#~ msgid "Engines cannot retrieve results"
#~ msgstr "Moottorit eivät voi palauttaa tuloksia"

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