Author SHA1 Message Date
searxng-bot 448ced4b18 [mod] data: update searx.data - update_wikidata.py 2026-10-29 00:17:16 +00:00
Bnyro c360868e5b [feat] xprivo: properly detect ratelimits instead of showing HTTP errors 2026-10-28 10:54:38 +01:00
dependabot[bot] b5ed73fcb6 [upd] pypi: Bump python-dateutil from 2.9.0.post0 to 3.0.0 (#6888)
Bumps [python-dateutil](https://github.com/dateutil/dateutil) from 2.9.0.post0 to 3.0.0.
- [Release notes](https://github.com/dateutil/dateutil/releases)
- [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS)
- [Commits](https://github.com/dateutil/dateutil/compare/2.9.0.post0...3.0.0)

---
updated-dependencies:
- dependency-name: python-dateutil
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-27 21:33:35 +01:00
dependabot[bot] 28423d332d [upd] pypi: Bump the minor group with 2 updates (#6887)
Bumps the minor group with 2 updates: [certifi](https://github.com/certifi/python-certifi) and [granian](https://github.com/emmett-framework/granian).


Updates `certifi` from 2026.10.19 to 2026.10.23
- [Commits](https://github.com/certifi/python-certifi/compare/2026.10.19...2026.10.23)

Updates `granian` from 2.8.4 to 2.8.5
- [Release notes](https://github.com/emmett-framework/granian/releases)
- [Commits](https://github.com/emmett-framework/granian/compare/v2.8.4...v2.8.5)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2026.10.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: granian
  dependency-version: 2.8.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-27 20:00:45 +01:00
Brock Vojkovic ef7bae9ddb [upd] pypi: Bump curl_cffi from 0.16.3 to 0.17.0 (#6890)
Fixes HTTP2Shell (CVE-2026-111497).

Signed-off-by: vojkovic <git@vojk.au>
2026-10-27 17:00:58 +08:00
Brock Vojkovic cb74e7f810 [fix] autocomplete: apply outgoing request timeout (#6886)
Fixes update_kwargs so it does not throw away the dict and instead uses the correct settings.

Signed-off-by: vojkovic <git@vojk.au>
2026-10-27 10:54:52 +08:00
Sandro 54ca1d9cfb [fix] tagesschau: remove from general category (#6882) 2026-10-25 19:34:17 +01:00
github-actions[bot]andsearxng-bot ef0ec03da0 [l10n] update translations from Weblate (#6850)
3dfe3d3f7 - 2026-10-19 - cyh21185 <cyh21185@noreply.codeberg.org>
896dc9faf - 2026-10-20 - marc-lopez <marc-lopez@noreply.codeberg.org>
f53baeeae - 2026-10-16 - marc-lopez <marc-lopez@noreply.codeberg.org>

Co-authored-by: searxng-bot <searxng-bot@users.noreply.github.com>
2026-10-24 10:46:18 +02:00
dependabot[bot] 629368e40c [upd] web-client (simple): Bump the minor group (#6880)
Bumps the minor group in /client/simple with 2 updates: [browserslist](https://github.com/browserslist/browserslist) and [sort-package-json](https://github.com/keithamus/sort-package-json).


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

Updates `sort-package-json` from 4.0.0 to 4.1.0
- [Release notes](https://github.com/keithamus/sort-package-json/releases)
- [Commits](https://github.com/keithamus/sort-package-json/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: browserslist
  dependency-version: 4.29.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: sort-package-json
  dependency-version: 4.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-23 20:33:46 +02:00
dependabot[bot] 68647d0806 [upd] pypi: Bump certifi from 2026.7.22 to 2026.10.19 in the minor group (#6881)
Bumps the minor group with 1 update: [certifi](https://github.com/certifi/python-certifi).


Updates `certifi` from 2026.7.22 to 2026.10.19
- [Commits](https://github.com/certifi/python-certifi/compare/2026.07.22...2026.10.19)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2026.10.19
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-23 20:33:12 +02:00
Bnyro 81371861f6 [del] engines: remove andisearch for now - #687
This reverts commit 2a6fc07be3.
2026-10-19 23:27:52 +02:00
WookieeOnCrack d47cb96bfb [feat] brave: parse JavaScript IIFE to fix engine
- Should resolve #6819 by reverting Accept-Encoding headers back to the curl_cffi default value
- Adds IIFE Parsing / Data Extraction that appears after headers update
2026-10-19 13:30:53 +02:00
jerry155756294 62366b05c0 [fix] engine - DDG: ignore zero-click answers without URLs (#6865)
If the provider uses DDG, and a HEX color code is searched for (for example, `A87771`), it directly reports `SearxEngineXPathException: index 0 not found` (the Web UI displays a parsing error), because DDG displays an icon for a color code.  This causes the parser to be unable to parse that icon. This fix directly ignores this special function.
2026-10-19 08:44:54 +02:00
Ivan Gabaldon 1c10abe2d3 [enh] container: nitpicks (#6862)
Reduce Valkey writes to database
Should error with 1, not 127
2026-10-17 20:30:22 +02:00
Ivan Gabaldon 3afd949268 [enh] container: merge dockerfiles (#6861)
* [enh] container: merge dockerfiles

Leftover from https://github.com/searxng/searxng/pull/6720

* [enh] apply suggestion

https://github.com/searxng/searxng/pull/6861#discussion_r4302473383
2026-10-17 20:30:09 +02:00
Bnyro 2a6fc07be3 [feat] engines: add andisearch (general) 2026-10-17 11:16:04 +02:00
dependabot[bot] decdfa6f7d [upd] pypi: Bump the minor group with 4 updates (#6852)
Bumps the minor group with 4 updates: [black](https://github.com/psf/black), [pylint](https://github.com/pylint-dev/pylint), [selenium](https://github.com/SeleniumHQ/Selenium) and [typer](https://github.com/fastapi/typer).


Updates `black` from 26.10.0 to 26.10.1
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/26.10.0...26.10.1)

Updates `pylint` from 4.1.2 to 4.1.3
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v4.1.2...v4.1.3)

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

Updates `typer` from 0.27.2 to 0.27.3
- [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.27.2...0.27.3)

---
updated-dependencies:
- dependency-name: black
  dependency-version: 26.10.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: pylint
  dependency-version: 4.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: selenium
  dependency-version: 4.51.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: typer
  dependency-version: 0.27.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-17 10:15:00 +02:00
Bnyro 8252c0bf6d [chore] resulthunter: fix broken linter 2026-10-16 18:24:21 +02:00
Bnyro 47aa11475b [fix] resulthunter: blocked with access denied 2026-10-16 18:17:43 +02:00
dependabot[bot] 0ce72dd2cc [upd] github-actions: Bump actions/upload-artifact from 7.0.1 to 7.0.2 (#6851)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a...cf430e030ddbb5b0abf93d22962f4752f3646cd9)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-16 14:56:49 +02:00
dependabot[bot] c6d7fe91d3 [upd] web-client (simple): Bump @types/node (#6849)
Bumps the minor group in /client/simple with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


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

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-16 14:56:19 +02:00
dependabot[bot] 7847767832 [upd] github-actions: Bump actions/download-artifact from 8.0.1 to 8.0.2 (#6848)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c...9000827ccba6bdab643e8b6fd33ac0654aef8333)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-16 14:55:46 +02:00
dependabot[bot] d2209bfbb0 [upd] github-actions: Bump actions/setup-node from 7.0.0 to 7.1.0 (#6847)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/820762786026740c76f36085b0efc47a31fe5020...949feb2413d6458794dcd2491c4babbbce0c15c1)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-16 14:55:17 +02:00
Aadniz ca8e1582be [build] /static 2026-10-16 13:27:46 +02:00
Aadniz 243d3a9df2 [fix] image descriptions disappearing on firefox 2026-10-16 13:27:46 +02:00
Bnyro ff8f5ddcc9 [fix] yep: send x-csrf-protection header to bypass botdetection 2026-10-16 00:18:44 +02:00
Brock Vojkovic b00212a836 [mod] image proxy: /image with the default network (#6816)
- Changes the path of the /image_proxy endpoint to /image. This ensures that if the user copies the image through the browser, it is named image.png rather than image_proxy.png as the latter can be attributed to searxng hence preserving the privacy of the user.
- Removed the dedicated network for the image proxy. Previously this was used to force http/2 off but since moving to curl from httpx this is not as much of a problem. Now instead uses the default network.

Signed-off-by: vojkovic <git@vojk.au>
2026-10-15 06:59:50 +08:00
Bnyro 22c64b8d39 [fix] xprivo: crashes when parsing response 2026-10-13 12:15:09 +02:00
ky0nshi ad46ebd7d8 [fix] typo in ayo engine - https://search.ayo.de (#6817)
typo in the address fixed -> https://search.ayo.de
2026-10-11 17:52:29 +02:00
dependabot[bot] f4822b3fc4 [upd] web-client (simple): Bump the minor group (#6826)
Bumps the minor group in /client/simple with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [ol](https://github.com/openlayers/openlayers) | `10.10.0` | `10.11.0` |
| [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.14` | `2.5.15` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.6.3` | `26.6.4` |
| [browserslist](https://github.com/browserslist/browserslist) | `4.29.2` | `4.29.3` |
| [stylelint](https://github.com/stylelint/stylelint) | `17.15.0` | `17.16.0` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.3.1` | `8.3.3` |

Updates `ol` from 10.10.0 to 10.11.0
- [Release notes](https://github.com/openlayers/openlayers/releases)
- [Commits](https://github.com/openlayers/openlayers/compare/v10.10.0...v10.11.0)

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

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

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

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

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

---
updated-dependencies:
- dependency-name: ol
  dependency-version: 10.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 26.6.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: browserslist
  dependency-version: 4.29.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: stylelint
  dependency-version: 17.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor
- dependency-name: vite
  dependency-version: 8.3.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-09 20:59:16 +02:00
Markus Heiser 9f042d2f67 [build] /static 2026-10-09 16:16:48 +02:00
DangTAnh 815f58994a [feat] simple theme: translate hotkeys help dialog (#6806)
Move hardcoded English strings of the hotkeys help (keyboard.ts) to the
server side get_translations() so translators can pick them up. The client
looks them up via settings.translations with identical English fallbacks.
2026-10-09 16:16:48 +02:00
Markus Heiser 6a0ac6a391 [upd] update to gecko driver v0.37.1 (#6827)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-10-09 15:19:50 +02:00
Markus Heiser bba4f64e8a [fix] issues reported by pylint v4.1.2 (#6827)
Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
2026-10-09 15:19:50 +02:00
dependabot[bot] 619696aa6c [upd] pypi: Bump the minor group with 5 updates (#6827)
Bumps the minor group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [granian](https://github.com/emmett-framework/granian) | `2.8.3` | `2.8.4` |
| [black](https://github.com/psf/black) | `26.5.1` | `26.10.0` |
| [pylint](https://github.com/pylint-dev/pylint) | `4.0.9` | `4.1.2` |
| [selenium](https://github.com/SeleniumHQ/Selenium) | `4.49.0` | `4.50.0` |
| [basedpyright](https://github.com/detachhead/basedpyright) | `1.40.1` | `1.40.2` |

Updates `granian` from 2.8.3 to 2.8.4
- [Release notes](https://github.com/emmett-framework/granian/releases)
- [Commits](https://github.com/emmett-framework/granian/compare/v2.8.3...v2.8.4)

Updates `black` from 26.5.1 to 26.10.0
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/26.5.1...26.10.0)

Updates `pylint` from 4.0.9 to 4.1.2
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v4.0.9...v4.1.2)

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

Updates `basedpyright` from 1.40.1 to 1.40.2
- [Release notes](https://github.com/detachhead/basedpyright/releases)
- [Commits](https://github.com/detachhead/basedpyright/compare/v1.40.1...v1.40.2)
2026-10-09 15:19:50 +02:00
github-actions[bot] 2dc2b14628 [l10n] update translations from Weblate (#6828)
cf8a68cc8 - 2026-10-05 - lugged9922 <lugged9922@noreply.codeberg.org>
046b92a92 - 2026-10-06 - kratos <makesocialfoss32@keemail.me>
475f33d38 - 2026-10-04 - SomeTr <sometr@noreply.codeberg.org>
2f508cdb2 - 2026-10-05 - Stephan-P <stephan-p@noreply.codeberg.org>
0de45dca2 - 2026-10-05 - Kito <q1t0@noreply.codeberg.org>
76a5f5248 - 2026-10-05 - ghose <ghose@noreply.codeberg.org>
d7bfd43bd - 2026-10-04 - Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
bcc67b083 - 2026-10-04 - gallegonovato <gallegonovato@noreply.codeberg.org>
2026-10-09 14:08:51 +02:00
Guillaume P.andGuillaume PUTIER 6671d89bed [fix] engines: wikidata, no list as content when an entity has no description (#6824)
Wikidata kept showing up in `unresponsive_engines` as "unexpected crash", but only for some queries (`!wikidata loi anti gaspillage` for instance).

In `get_results`, `itemDescription` defaults to `[]`. When an entity has no description and `display_type` contains `"list"`, that empty list goes out as the `content` of a list result, and `ResultContainer.extend` fails with `TypeError: expected string or bytes-like object, got 'list'`. Every wikidata result of the query is dropped with it.

The default is now an empty string. `len(infobox_content) == 0` still works, and the infobox branch already expected a string.

`tests/unit/test_engine_wikidata.py` covers an entity with no description, once with `display_type` `["list"]` and once with `["infobox"]`. Without the fix both tests fail (`'content': []` instead of `''`), with it both pass.




itemDescription defaulted to [] in get_results. With "list" in display_type, that empty list was sent as the content of a list result and ResultContainer.extend raised a TypeError, so the whole wikidata answer of the query was lost. Default to an empty string instead.

Signed-off-by: Guillaume PUTIER <putier.guillaume@gmail.com>
Co-authored-by: Guillaume PUTIER <putier.guillaume@gmail.com>
2026-10-07 11:18:19 +02:00
Brock Vojkovic d48c4b5554 [feat] sort infobox so images are first (#6815)
Often there will be two or more results in the infobox.

We now sort this so infoboxes with an image show above those that do not.

Signed-off-by: vojkovic <git@vojk.au>
2026-10-04 08:16:51 +02:00
Brock Vojkovic 44b98e6102 [fix] engines: privacywall referer to bypass botdetection (#6813)
Privacywall needs a referer header now or else returns access denied.
2026-10-04 07:49:12 +02:00
Bnyro 7a3e41d420 [feat] s1search: parse suggestions (#6810)
- parse s1search suggestions
- thereby also fixes that some queries would crash the engine if using `https://search.nation.online` as base url because it tried to parse the suggestions as real results

### How to test this PR locally?

- set `base_url = "https://search.nation.online"` and `inactive: false`
- !s1 rust
2026-10-04 07:47:00 +02:00
Brock Vojkovic ce913b8934 [fix] engines: dogpile cors headers (#6814)
Added Sec-Fetch-Mode header to fix the engine. Dogpile now has better bot detection, similar to tusksearch, we now require CORS headers in the request.
2026-10-04 07:44:15 +02:00
dependabot[bot] 19ffbcd306 [upd] web-client (simple): Bump the minor group (#6807)
Bumps the minor group in /client/simple with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [browserslist](https://github.com/browserslist/browserslist), [sharp](https://github.com/lovell/sharp) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).

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

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

Updates `sharp` from 0.35.4 to 0.35.5
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.4...v0.35.5)

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

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: browserslist
  dependency-version: 4.29.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: sharp
  dependency-version: 0.35.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: vite
  dependency-version: 8.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-02 16:37:24 +02:00
dependabot[bot] e652ca7d29 [upd] pypi: Bump msgspec from 0.21.1 to 0.22.0 in the minor group (#6811)
Bumps the minor group with 1 update: [msgspec](https://github.com/msgspec/msgspec).


Updates `msgspec` from 0.21.1 to 0.22.0
- [Release notes](https://github.com/msgspec/msgspec/releases)
- [Changelog](https://github.com/msgspec/msgspec/blob/main/docs/changelog.md)
- [Commits](https://github.com/msgspec/msgspec/compare/0.21.1...0.22.0)
2026-10-02 16:30:24 +02:00
Brock Vojkovic 26b90797e0 [enh] simplify and clean up preferences (#6126)
- The 'engine tokens' field is only shown now if there are any engines with tokens configured.

- DOI Resolver now has combined the previous two settings (one dropdown and one toggle) into just a single dropdown similar to the favicon resolver.
2026-10-02 18:36:07 +08:00
dependabot[bot] a5659a5364 [upd] pypi: Bump the minor group with 2 updates (#6808)
Bumps the minor group with 2 updates: [pylint](https://github.com/pylint-dev/pylint) and [wlc](https://github.com/WeblateOrg/wlc).


Updates `pylint` from 4.0.8 to 4.0.9
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v4.0.8...v4.0.9)

Updates `wlc` from 2.2.0 to 2.2.1
- [Release notes](https://github.com/WeblateOrg/wlc/releases)
- [Changelog](https://github.com/WeblateOrg/wlc/blob/main/CHANGES.md)
- [Commits](https://github.com/WeblateOrg/wlc/compare/2.2.0...2.2.1)
2026-10-02 12:12:46 +02:00
kcs81 a9d9900334 [fix] map Brave API SafeSearch levels correctly (#6799)
API description:

- https://api-dashboard.search.brave.com/api-reference/web/search/get#parameter-safesearch
2026-09-30 07:39:34 +02:00
quacker 4e2c1ea7f4 [fix] duckduckgo_web: make preload link timeout use the global configuration - #6768 2026-09-29 14:33:54 +02:00
github-actions[bot] f5035873ad [mod] data: update searx.data - update_firefox_version.py (#6792) 2026-09-29 08:44:07 +02:00
github-actions[bot] c11cbda117 [mod] data: update searx.data - update_ahmia_blacklist.py (#6793) 2026-09-29 08:35:03 +02:00
github-actions[bot] bad451b3da [mod] data: update searx.data - update_currencies.py (#6794) 2026-09-29 08:08:20 +02:00
github-actions[bot] 896ec8d3af [mod] data: update searx.data - update_engine_descriptions.py (#6796) 2026-09-29 07:43:48 +02:00
203 changed files with 13144 additions and 9257 deletions
+3 -3
View File
@@ -83,7 +83,7 @@ jobs:
run: make container.build
- name: Upload container image
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: container-image-${{ matrix.arch }}
path: "/var/tmp/searxng-podman/*.tar"
@@ -116,7 +116,7 @@ jobs:
persist-credentials: "false"
- name: Download container image
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: container-image-${{ matrix.arch }}
path: "/var/tmp/searxng-podman"
@@ -160,7 +160,7 @@ jobs:
persist-credentials: "false"
- name: Download container images
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: container-image-*
path: "/var/tmp/searxng-podman"
+1 -1
View File
@@ -67,7 +67,7 @@ jobs:
python-version: "${{ env.PYTHON_VERSION }}"
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "26"
check-latest: "true"
+341 -341
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -25,25 +25,25 @@
"dependencies": {
"ionicons": "^8.1.0",
"normalize.css": "8.0.1",
"ol": "^10.10.0",
"ol": "^10.11.0",
"swiped-events": "1.2.0"
},
"devDependencies": {
"@biomejs/biome": "2.5.14",
"@types/node": "^26.6.2",
"browserslist": "^4.28.8",
"@biomejs/biome": "2.5.15",
"@types/node": "^26.6.5",
"browserslist": "^4.29.4",
"browserslist-to-esbuild": "^2.1.1",
"edge.js": "^6.5.1",
"less": "^4.9.0",
"mathjs": "^15.2.0",
"sharp": "~0.35.4",
"sort-package-json": "^4.0.0",
"stylelint": "^17.14.1",
"sharp": "~0.35.5",
"sort-package-json": "^4.1.0",
"stylelint": "^17.16.0",
"stylelint-config-standard-less": "^4.1.0",
"stylelint-prettier": "^5.0.3",
"svgo": "^4.1.0",
"typescript": "~7.0.2",
"vite": "^8.3.0",
"vite": "^8.3.3",
"vite-bundle-analyzer": "^1.3.9"
}
}
+47 -44
View File
@@ -8,67 +8,68 @@ export type KeyBindingLayout = "default" | "vim";
type KeyBinding = {
key: string;
fun: (event: KeyboardEvent) => void;
des: string;
cat: string;
des?: string;
cat?: string;
};
type HighlightResultElement = "down" | "up" | "visible" | "bottom" | "top";
/* common base for layouts */
const baseKeyBinding: Record<string, KeyBinding> = {
Escape: {
key: "ESC",
fun: (event: KeyboardEvent) => removeFocus(event),
des: "remove focus from the focused input",
cat: "Control"
des: settings.translations?.hotkeys_remove_focus,
cat: settings.translations?.hotkeys_category_control
},
c: {
key: "c",
fun: () => copyURLToClipboard(),
des: "copy url of the selected result to the clipboard",
cat: "Results"
des: settings.translations?.hotkeys_copy_url,
cat: settings.translations?.hotkeys_category_results
},
h: {
key: "h",
fun: () => toggleHelp(keyBindings),
des: "toggle help window",
cat: "Other"
des: settings.translations?.hotkeys_toggle_help,
cat: settings.translations?.hotkeys_category_other
},
i: {
key: "i",
fun: () => searchInputFocus(),
des: "focus on the search input",
cat: "Control"
des: settings.translations?.hotkeys_focus_search,
cat: settings.translations?.hotkeys_category_control
},
n: {
key: "n",
fun: () => GoToNextPage(),
des: "go to next page",
cat: "Results"
des: settings.translations?.hotkeys_next_page,
cat: settings.translations?.hotkeys_category_results
},
o: {
key: "o",
fun: () => openResult(false),
des: "open search result",
cat: "Results"
des: settings.translations?.hotkeys_open_result,
cat: settings.translations?.hotkeys_category_results
},
p: {
key: "p",
fun: () => GoToPreviousPage(),
des: "go to previous page",
cat: "Results"
des: settings.translations?.hotkeys_previous_page,
cat: settings.translations?.hotkeys_category_results
},
r: {
key: "r",
fun: () => reloadPage(),
des: "reload page from the server",
cat: "Control"
des: settings.translations?.hotkeys_reload_page,
cat: settings.translations?.hotkeys_category_control
},
t: {
key: "t",
fun: () => openResult(true),
des: "open the result in a new tab",
cat: "Results"
des: settings.translations?.hotkeys_open_new_tab,
cat: settings.translations?.hotkeys_category_results
}
};
@@ -78,14 +79,14 @@ const keyBindingLayouts: Record<KeyBindingLayout, Record<string, KeyBinding>> =
ArrowLeft: {
key: "←",
fun: () => highlightResult("up")(),
des: "select previous search result",
cat: "Results"
des: settings.translations?.hotkeys_select_previous,
cat: settings.translations?.hotkeys_category_results
},
ArrowRight: {
key: "→",
fun: () => highlightResult("down")(),
des: "select next search result",
cat: "Results"
des: settings.translations?.hotkeys_select_next,
cat: settings.translations?.hotkeys_category_results
},
...baseKeyBinding
},
@@ -95,56 +96,56 @@ const keyBindingLayouts: Record<KeyBindingLayout, Record<string, KeyBinding>> =
b: {
key: "b",
fun: () => scrollPage(-window.innerHeight),
des: "scroll one page up",
cat: "Navigation"
des: settings.translations?.hotkeys_page_up,
cat: settings.translations?.hotkeys_category_navigation
},
d: {
key: "d",
fun: () => scrollPage(window.innerHeight / 2),
des: "scroll half a page down",
cat: "Navigation"
des: settings.translations?.hotkeys_half_page_down,
cat: settings.translations?.hotkeys_category_navigation
},
f: {
key: "f",
fun: () => scrollPage(window.innerHeight),
des: "scroll one page down",
cat: "Navigation"
des: settings.translations?.hotkeys_page_down,
cat: settings.translations?.hotkeys_category_navigation
},
g: {
key: "g",
fun: () => scrollPageTo(-document.body.scrollHeight, "top"),
des: "scroll to the top of the page",
cat: "Navigation"
des: settings.translations?.hotkeys_scroll_top,
cat: settings.translations?.hotkeys_category_navigation
},
j: {
key: "j",
fun: () => highlightResult("down")(),
des: "select next search result",
cat: "Results"
des: settings.translations?.hotkeys_select_next,
cat: settings.translations?.hotkeys_category_results
},
k: {
key: "k",
fun: () => highlightResult("up")(),
des: "select previous search result",
cat: "Results"
des: settings.translations?.hotkeys_select_previous,
cat: settings.translations?.hotkeys_category_results
},
u: {
key: "u",
fun: () => scrollPage(-window.innerHeight / 2),
des: "scroll half a page up",
cat: "Navigation"
des: settings.translations?.hotkeys_half_page_up,
cat: settings.translations?.hotkeys_category_navigation
},
v: {
key: "v",
fun: () => scrollPageTo(document.body.scrollHeight, "bottom"),
des: "scroll to the bottom of the page",
cat: "Navigation"
des: settings.translations?.hotkeys_scroll_bottom,
cat: settings.translations?.hotkeys_category_navigation
},
y: {
key: "y",
fun: () => copyURLToClipboard(),
des: "copy url of the selected result to the clipboard",
cat: "Results"
des: settings.translations?.hotkeys_copy_url,
cat: settings.translations?.hotkeys_category_results
},
...baseKeyBinding
}
@@ -344,6 +345,7 @@ const initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBin
for (const binding of Object.values(keyBindings)) {
const { cat } = binding;
if (!cat) continue;
categories[cat] ??= [];
categories[cat].push(binding);
}
@@ -352,8 +354,8 @@ const initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBin
(a, b) => (categories[b]?.length ?? 0) - (categories[a]?.length ?? 0)
);
let html = '<a href="#" class="close" aria-label="close" title="close">×</a>';
html += "<h3>How to navigate SearXNG with hotkeys</h3>";
let html = `<a href="#" class="close" aria-label="${settings.translations?.close}" title="${settings.translations?.close}">×</a>`;
html += `<h3>${settings.translations?.hotkeys_navigation_title}</h3>`;
html += "<table>";
for (const [i, categoryKey] of sortedCategoryKeys.entries()) {
@@ -372,6 +374,7 @@ const initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBin
html += '<ul class="list-unstyled">';
for (const binding of bindings) {
if (!binding.des) continue;
html += `<li><kbd>${binding.key}</kbd> ${binding.des}</li>`;
}
+2
View File
@@ -473,6 +473,8 @@ article[data-vim-selected].category-social {
& > a {
position: relative;
outline: none;
display: inline-block;
height: 100%;
}
img {
+1 -1
View File
@@ -1,7 +1,7 @@
# Read the documentation before using the `docker-compose.yml` file:
# https://docs.searxng.org/admin/installation-docker.html
#
# Additional ENVs:
# Additional variables:
# https://docs.searxng.org/admin/settings/settings_general.html#settings-general
# https://docs.searxng.org/admin/settings/settings_server.html#settings-server
@@ -1,4 +1,29 @@
FROM localhost/searxng/searxng:builder AS builder
FROM docker.io/searxng/base:searxng-builder AS builder
COPY ./requirements.txt ./requirements-server.txt ./
ENV UV_NO_MANAGED_PYTHON="true"
RUN --mount=type=cache,id=uv,target=/root/.cache/uv set -eux -o pipefail; \
uv venv; \
uv pip install --requirements ./requirements.txt --requirements ./requirements-server.txt; \
uv cache prune --ci; \
find ./.venv/lib/ -type f -exec strip --strip-unneeded {} + || true; \
find ./.venv/lib/ -type d -name "__pycache__" -exec rm -rf {} +; \
find ./.venv/lib/ -type f -name "*.pyc" -delete; \
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./.venv/lib/
COPY --exclude=./searx/version_frozen.py ./searx/ ./searx/
RUN set -eux -o pipefail; \
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./searx/; \
find ./searx/static/ -type f \
\( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.svg" \) \
-exec gzip -9 -k {} + \
-exec brotli -9 -k {} + \
-exec gzip --test {}.gz + \
-exec brotli --test {}.br +
FROM docker.io/searxng/base:searxng AS dist
COPY --chown=977:977 --from=builder /usr/local/searxng/.venv/ ./.venv/
@@ -21,6 +46,8 @@ LABEL org.opencontainers.image.created="$CREATED" \
org.opencontainers.image.url="https://searxng.org" \
org.opencontainers.image.version="$VERSION"
# Extended from:
# https://codeberg.org/searxng/base/src/branch/main/Dockerfile
ENV __SEARXNG_VERSION="$VERSION" \
__SEARXNG_SETTINGS_PATH="$__SEARXNG_CONFIG_PATH/settings.yml" \
GRANIAN_PROCESS_NAME="searxng" \
@@ -32,7 +59,6 @@ ENV __SEARXNG_VERSION="$VERSION" \
GRANIAN_WORKERS_KILL_TIMEOUT="30s" \
GRANIAN_BLOCKING_THREADS_IDLE_TIMEOUT="5m"
# "*_PATH" ENVs are defined in base images
VOLUME $__SEARXNG_CONFIG_PATH
VOLUME $__SEARXNG_DATA_PATH
-25
View File
@@ -1,25 +0,0 @@
FROM docker.io/searxng/base:searxng-builder AS builder
COPY ./requirements.txt ./requirements-server.txt ./
ENV UV_NO_MANAGED_PYTHON="true"
RUN --mount=type=cache,id=uv,target=/root/.cache/uv set -eux -o pipefail; \
uv venv; \
uv pip install --requirements ./requirements.txt --requirements ./requirements-server.txt; \
uv cache prune --ci; \
find ./.venv/lib/ -type f -exec strip --strip-unneeded {} + || true; \
find ./.venv/lib/ -type d -name "__pycache__" -exec rm -rf {} +; \
find ./.venv/lib/ -type f -name "*.pyc" -delete; \
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./.venv/lib/
COPY --exclude=./searx/version_frozen.py ./searx/ ./searx/
RUN set -eux -o pipefail; \
python -m compileall -q -f -j 0 --invalidation-mode=unchecked-hash ./searx/; \
find ./searx/static/ -type f \
\( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.svg" \) \
-exec gzip -9 -k {} + \
-exec brotli -9 -k {} + \
-exec gzip --test {}.gz + \
-exec brotli --test {}.br +
+1 -1
View File
@@ -18,7 +18,7 @@ services:
valkey:
container_name: searxng-valkey
image: docker.io/valkey/valkey:9-alpine
command: valkey-server --save 30 1 --loglevel warning
command: valkey-server --save 300 1 --save 60 1000 --loglevel warning
restart: always
volumes:
- valkey-data:/data/
+2 -2
View File
@@ -13,7 +13,7 @@ check_file() {
!!! "$target" is not a valid file, exiting...
!!!
EOF
exit 127
exit 1
fi
}
@@ -28,7 +28,7 @@ check_directory() {
!!! "$target" is not a valid directory, exiting...
!!!
EOF
exit 127
exit 1
fi
}
+6 -6
View File
@@ -283,12 +283,12 @@ container images are not officially supported):
$ make container
$ docker images
REPOSITORY TAG IMAGE ID SIZE
localhost/searxng/searxng 2026.6.19-93f66bfb4 ... 265 MB
localhost/searxng/searxng latest ... 265 MB
localhost/searxng/searxng builder ... 687 MB
docker.io/searxng/base searxng-builder ... 565 MB
docker.io/searxng/base searxng ... 143 MB
REPOSITORY TAG IMAGE ID SIZE
localhost/searxng/searxng latest ... 271 MB
localhost/searxng/searxng 2026.10.17-* ... 271 MB
<none> <none> ... 677 MB
docker.io/searxng/base searxng ... 144 MB
docker.io/searxng/base searxng-builder ... 550 MB
Migrate from ``searxng-docker``
===============================
+1 -1
View File
@@ -48,7 +48,7 @@ PATH="${PY_ENV}/bin:${REPO_ROOT}/node_modules/.bin:${GOROOT}/bin:${GOPATH}/bin:$
PYOBJECTS="searx"
PY_SETUP_EXTRAS='[test]'
GECKODRIVER_VERSION="v0.37.0"
GECKODRIVER_VERSION="v0.37.1"
# SPHINXOPTS=
BLACK_OPTIONS=("--target-version" "py311" "--line-length" "120" "--skip-string-normalization")
BLACK_TARGETS=("--exclude" "(searx/static|searx/languages.py)" "--include" 'searxng.msg|\.pyi?$' "searx" "searxng_extra" "tests")
+6 -6
View File
@@ -1,10 +1,10 @@
mock==5.2.0
nose2[coverage_plugin]==0.16.0
cov-core==1.15.0
black==26.5.1
pylint==4.0.8
black==26.10.1
pylint==4.1.3
splinter==0.21.0
selenium==4.49.0
selenium==4.51.0
Sphinx==8.2.3;python_version <= "3.11"
Sphinx==9.1.0; python_version > "3.11"
sphinx-issues==6.0.0
@@ -18,11 +18,11 @@ myst-parser==5.0.0
linuxdoc==20260504
aiounittest==1.5.0
yamllint==1.38.0
wlc==2.2.0
wlc==2.2.1
coloredlogs==15.0.1
docutils>=0.21.2;python_version <= "3.11"
docutils>=0.22.4; python_version > "3.11"
parameterized==0.9.0
granian[reload]==2.8.3
basedpyright==1.40.1
granian[reload]==2.8.5
basedpyright==1.40.2
types-lxml==2026.2.16
+2 -2
View File
@@ -1,2 +1,2 @@
granian==2.8.3
granian[pname]==2.8.3
granian==2.8.5
granian[pname]==2.8.5
+5 -5
View File
@@ -1,17 +1,17 @@
certifi==2026.7.22
certifi==2026.10.23
babel==2.18.0
flask-babel==4.0.0
flask==3.1.3
jinja2==3.1.6
lxml==6.1.3
pygments==2.21.0
python-dateutil==2.9.0.post0
python-dateutil==3.0.0
pyyaml==6.0.3
curl_cffi==0.16.3
curl_cffi==0.17.0
valkey==6.1.1
markdown-it-py==4.2.0
msgspec==0.21.1
typer==0.27.2
msgspec==0.22.0
typer==0.27.3
isodate==0.7.2
whitenoise==6.12.0
typing-extensions==4.16.0
+3 -3
View File
@@ -28,19 +28,19 @@ if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
def update_kwargs(**kwargs) -> None: # type: ignore
def update_kwargs(kwargs) -> None: # type: ignore
if 'timeout' not in kwargs:
kwargs['timeout'] = settings['outgoing']['request_timeout']
kwargs['raise_for_httperror'] = True
def get(*args, **kwargs) -> "SXNG_Response": # type: ignore
update_kwargs(**kwargs) # pyright: ignore[reportUnknownArgumentType]
update_kwargs(kwargs)
return http_get(*args, **kwargs) # pyright: ignore[reportUnknownArgumentType]
def post(*args, **kwargs) -> "SXNG_Response": # type: ignore
update_kwargs(**kwargs) # pyright: ignore[reportUnknownArgumentType]
update_kwargs(kwargs)
return http_post(*args, **kwargs) # pyright: ignore[reportUnknownArgumentType]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -93,7 +93,7 @@ class TrackerPatternsDB:
continue
if resp.status_code != 200:
log.warning(f"TRACKER_PATTERNS: ClearURL ignore HTTP {resp.status_code} {url}")
log.warning("TRACKER_PATTERNS: ClearURL ignore HTTP %s %s", resp.status_code, url)
continue
break
+2 -2
View File
@@ -5,7 +5,7 @@
],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
"versions": [
"154.0",
"153.0"
"157.0",
"156.0"
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+14 -12
View File
@@ -115,26 +115,26 @@ def load_engine(engine_data: dict[str, t.Any]) -> "Engine | types.ModuleType | N
logger.error('An engine does not have a "name" field')
return None
if '_' in engine_name:
logger.error('Engine name contains underscore: "{}"'.format(engine_name))
logger.error('Engine name contains underscore: "%s"', engine_name)
return None
if engine_name.lower() != engine_name:
logger.warning('Engine name is not lowercase: "{}", converting to lowercase'.format(engine_name))
logger.warning('Engine name is not lowercase: "%s", converting to lowercase', engine_name)
engine_name = engine_name.lower()
engine_data['name'] = engine_name
# load_module
module_name = engine_data.get('engine')
if module_name is None:
logger.error('The "engine" field is missing for the engine named "{}"'.format(engine_name))
logger.error('The "engine" field is missing for the engine named "%s"', engine_name)
return None
try:
engine = load_module(module_name + '.py', ENGINE_DIR)
except (SyntaxError, KeyboardInterrupt, SystemExit, SystemError, ImportError, RuntimeError):
logger.exception('Fatal exception in engine "{}"'.format(module_name))
logger.exception('Fatal exception in engine "%s"', module_name)
sys.exit(1)
except BaseException:
logger.exception('Cannot load engine "{}"'.format(module_name))
logger.exception('Cannot load engine "%s"', module_name)
return None
check_engine_module(engine)
@@ -247,7 +247,7 @@ def is_missing_required_attributes(engine: "Engine | types.ModuleType"):
missing = False
for engine_attr in dir(engine):
if not engine_attr.startswith('_') and getattr(engine, engine_attr) is None:
logger.error('Missing engine config attribute: "{0}.{1}"'.format(engine.name, engine_attr))
logger.error('Missing engine config attribute: "%s.%s"', engine.name, engine_attr)
missing = True
return missing
@@ -277,12 +277,12 @@ def call_engine_setup(engine: "Engine | types.ModuleType", engine_data: dict[str
if setup_func is None:
setup_ok = True
elif not callable(setup_func):
logger.error(f"engine's setup method isn't a callable (is of type: {type(setup_func)})")
logger.error("engine's setup method isn't a callable (is of type: %s)", type(setup_func))
else:
try:
setup_ok = engine.setup(engine_data)
except Exception as e: # pylint: disable=broad-except
logger.exception(f"(PID {os.getpid()}) {engine.name}: engine SETUP failed, exception: {e}")
logger.exception("(PID %s) %s: engine SETUP failed, exception: %s", os.getpid(), engine.name, e)
setup_ok = False
# The evaluation of the return value is analogous to Engine.init
@@ -290,18 +290,18 @@ def call_engine_setup(engine: "Engine | types.ModuleType", engine_data: dict[str
setup_ok = True
if not setup_ok:
logger.error(f"(PID {os.getpid()}) {engine.name}: engine setup was not successful")
logger.error("(PID %s) %s: engine setup was not successful", os.getpid(), engine.name)
return setup_ok
def register_engine(engine: "Engine | types.ModuleType"):
if engine.name in engines:
logger.error('Engine config error: ambiguous name: {0}'.format(engine.name))
logger.error('Engine config error: ambiguous name: %s', engine.name)
sys.exit(1)
engines[engine.name] = engine
if engine.shortcut in engine_shortcuts:
logger.error('Engine config error: ambiguous shortcut: {0}'.format(engine.shortcut))
logger.error('Engine config error: ambiguous shortcut: %s', engine.shortcut)
sys.exit(1)
engine_shortcuts[engine.shortcut] = engine.name
@@ -327,7 +327,9 @@ def load_engines(engine_list: list[dict[str, t.Any]]):
# if an engine can't be loaded (if for example the engine is missing
# tor or some other requirements) its set to inactive!
logger.error(
f"(PID {os.getpid()}) {engine_data.get('name', '???')}: can't register engine (loading engine failed)"
"(PID %s) %s: can't register engine (loading engine failed)",
os.getpid(),
engine_data.get("name", "???"),
)
engine_data["inactive"] = True
return engines
+1 -2
View File
@@ -37,8 +37,7 @@ def obtain_token():
)
token['value'] = loads(actual_token.text)['authInfo']['access_token']
token['last_updated'] = update_time
# pylint: disable=bare-except
except:
except: # pylint: disable=bare-except
pass
return token
+102 -21
View File
@@ -118,6 +118,7 @@ Implementations
"""
import json
import re
import typing as t
from collections.abc import Callable
from urllib.parse import urlencode
@@ -129,7 +130,14 @@ from searx.enginelib.traits import EngineTraits
from searx.exceptions import SearxEngineResponseException
from searx.result_types import EngineResults, MainResult, Video
from searx.result_types.image import Image
from searx.utils import html_to_text, js_obj_str_to_json_str, js_obj_str_to_python
from searx.utils import (
html_to_text,
js_find_matching_bracket,
js_obj_str_to_json_str,
js_obj_str_to_python,
js_split_args,
js_find_variable_positions,
)
if t.TYPE_CHECKING:
from searx.extended_types import SXNG_Response
@@ -188,6 +196,16 @@ time_range_map: dict[str, str] = {
"year": "py",
}
IIFE_RE = re.compile(
r"""
\(function\s*\( # (function(
(?P<params>[^)]*) # formal parameters
\)\s*\{\s*return\s* # ){ return
(?P<body>\{) # the object literal starts
""",
re.VERBOSE | re.DOTALL,
)
def request(query: str, params: dict[str, t.Any]) -> None:
@@ -207,12 +225,10 @@ def request(query: str, params: dict[str, t.Any]) -> None:
if brave_category == "goggles":
args["goggles_id"] = Goggles
params["headers"]["Accept-Encoding"] = "gzip, deflate"
params["url"] = f"{base_url}{brave_category}?{urlencode(args)}"
logger.debug("url %s", params["url"])
# set properties in the cookies
params["cookies"]["safesearch"] = safesearch_map.get(params["safesearch"], "off")
# the useLocation is IP based, we use cookie "country" for the region
params["cookies"]["useLocation"] = "0"
@@ -235,25 +251,90 @@ def _extract_published_date(published_date_raw: str | None):
return None
def extract_iife_object(data_src: str, match: re.Match[str]) -> tuple[str, int]:
"""
Extract and substitute the object literal from a JavaScript IIFE.
Returns the transformed object literal and the position immediately after the IIFE call.
"""
body_start = match.start("body")
body_end = js_find_matching_bracket(data_src, body_start, "{", "}")
obj_str = data_src[body_start : body_end + 1]
# 1. Locate the argument call execution signature block: ...)(arg1, arg2)
call_start = data_src.find("(", body_end)
if call_start == -1:
raise ValueError("IIFE missing call arguments")
call_end = js_find_matching_bracket(data_src, call_start, "(", ")")
# 2. map arguments
args = js_split_args(data_src[call_start + 1 : call_end])
params = [p.strip() for p in match["params"].split(",") if p.strip()]
mapping = dict(zip(params, args))
# 3. substitute idents
variables = js_find_variable_positions(obj_str)
for variable_name, pos in reversed(variables):
if variable_name in mapping:
value = mapping[variable_name]
obj_str = obj_str[:pos] + value + obj_str[pos + len(variable_name) :]
return obj_str, call_end + 1
def extract_json_data(text: str) -> dict[str, t.Any]:
# Example script source containing the data:
#
# kit.start(app, element, {
# node_ids: [0, 19],
# data: [{type:"data",data: .... ["q","goggles_id"],route:1,url:1}}]
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# form: null,
# error: null
# });
start = text.index("data: [{")
newline = text.index("\n", start)
end = text.rindex("}}]", start, newline)
js_obj_str = "{" + text[start:end] + "}}]}"
# js_obj_str = js_obj_str.replace("\xa0", "") # remove ASCII for &nbsp;
# js_obj_str = js_obj_str.replace(r"\u003C", "<").replace(r"\u003c", "<") # fix broken HTML tags in strings
json_str = js_obj_str_to_json_str(js_obj_str)
data: dict[str, t.Any] = json.loads(json_str)
return data
"""
Extract the SvelteKit data payload that Brave embeds in the page.
Handles:
- plain-object form
- pure IIFE form
- mixed form (IIFE + plain object) that appears on some news pages
"""
marker = "data: ["
start = text.find(marker)
if start == -1:
raise ValueError("Brave data payload not found (no 'data: [')")
array_start = start + len(marker) - 1 # Points directly at '['
array_end = js_find_matching_bracket(text, array_start, "[", "]")
data_src = text[array_start + 1 : array_end]
objects: list[str] = []
pos = 0
n = len(data_src)
while pos < n:
# Move past whitespace and commas safely
while pos < n and data_src[pos] in " \t\n\r,":
pos += 1
if pos >= n:
break
# Case 1: IIFE Object Signature Matches
if m := IIFE_RE.match(data_src, pos):
obj_str, next_pos = extract_iife_object(data_src, m)
objects.append(obj_str)
pos = next_pos
# Brave sometimes emits an extra ')' after the IIFE call
while pos < n and data_src[pos] in " \t\n\r)":
pos += 1
continue
# Case 2: Standard raw plain JSON mapping block {type:"data", ...}
if data_src[pos] == "{":
obj_end = js_find_matching_bracket(data_src, pos, "{", "}")
objects.append(data_src[pos : obj_end + 1])
pos = obj_end + 1
continue
raise ValueError(f"unexpected token in data array at offset {pos}: {data_src[pos : pos + 40]!r}")
if not objects:
raise ValueError("no data objects found inside Brave payload")
combined = '{"data":[' + ",".join(objects) + "]}"
json_str = js_obj_str_to_json_str(combined)
return json.loads(json_str)
def response(resp: "SXNG_Response") -> EngineResults:
+3 -3
View File
@@ -51,6 +51,7 @@ api_key: str = ""
categories = ["general", "web"]
paging = True
safesearch = True
safesearch_map = {2: "strict", 1: "moderate", 0: "off"}
time_range_support = True
results_per_page: int = 20
@@ -84,9 +85,8 @@ def request(query: str, params: "OnlineParams") -> None:
if params["time_range"]:
search_args["time_range"] = time_range_map.get(params["time_range"])
# Apply SafeSearch if enabled
if params["safesearch"]:
search_args["safesearch"] = "strict"
# Apply SafeSearch
search_args["safesearch"] = safesearch_map.get(params["safesearch"], "off")
params["url"] = f"{base_url}?{urlencode(search_args)}"
params["headers"]["X-Subscription-Token"] = api_key
+1 -1
View File
@@ -169,7 +169,7 @@ def _get_results_from_process(res: EngineResults, cmd, pageno):
for raw_result in raw_results:
result = __parse_single_result(raw_result)
if result is None:
_command_logger.debug('skipped result:', raw_result)
_command_logger.debug('skipped result: %s', raw_result)
continue
if start <= count and count <= end: # pylint: disable=chained-comparison
+6 -1
View File
@@ -54,7 +54,11 @@ def _obtain_token() -> str:
token = CACHE.get("token")
if token:
return token
resp = post(f"{base_url}/api/token/refresh", headers={"Origin": base_url}, cookies={"dp_api_token": "1"})
resp = post(
f"{base_url}/api/token/refresh",
headers={"Origin": base_url, "Sec-Fetch-Mode": "cors"},
cookies={"dp_api_token": "1"},
)
if not resp.ok:
raise SearxEngineAPIException("failed to obtain dogpile token")
token = resp.json()["token"]
@@ -65,6 +69,7 @@ def _obtain_token() -> str:
def request(query: str, params: "OnlineParams"):
params["url"] = f"{base_url}/api/{dogpile_categ}"
params["headers"]["Origin"] = base_url
params["headers"]["Sec-Fetch-Mode"] = "cors"
params["cookies"]["dp_api_token"] = "1"
params["headers"]["x-dogpile-token"] = _obtain_token()
+6 -5
View File
@@ -510,12 +510,13 @@ def response(resp: "SXNG_Response") -> EngineResults:
and "Your user agent:" not in zero_click
and "URL Decoded:" not in zero_click
):
res.add(
res.types.Answer(
answer=zero_click,
url=eval_xpath_getindex(doc, '//div[@id="zero_click_abstract"]/a/@href', 0),
if zero_click_url := eval_xpath_getindex(doc, '//div[@id="zero_click_abstract"]/a/@href', 0, None):
res.add(
res.types.Answer(
answer=zero_click,
url=zero_click_url,
)
)
)
return res
-1
View File
@@ -74,7 +74,6 @@ def _fetch_first_page_link(
headers=headers,
impersonate="firefox",
default_headers=False,
timeout=2,
)
if resp.status_code != 200:
+1
View File
@@ -88,6 +88,7 @@ def request(query: str, params: "OnlineParams") -> None:
params["url"] = f"{base_url}/search/secure/?{urlencode(args)}"
else:
params["url"] = f"{base_url}/{privacywall_category}/?{urlencode(args)}"
params["headers"]["Referer"] = f"{base_url}/"
def _general_results(doc: "ElementBase") -> EngineResults:
+34 -3
View File
@@ -4,11 +4,14 @@
.. _Resulthunter : https://resulthunter.com
"""
import re
import typing as t
from urllib.parse import urlencode
from searx.enginelib import EngineCache
from searx import locales
from searx.exceptions import SearxEngineResponseException
from searx.exceptions import SearxEngineAPIException, SearxEngineResponseException
from searx.network import get
from searx.result_types import EngineResults
from searx.utils import eval_xpath_list, eval_xpath, extract_text
@@ -40,18 +43,46 @@ base_url = "https://resulthunter.com"
resulthunter_categ = "web"
"""Supported categories are ``web`` and ``images``."""
CACHE: EngineCache
"""Cache for storing search token and cookies."""
_SEARCH_TOKEN_RE = re.compile(r"window.__rhSearchToken\s*=\s*'(.*)'")
def setup(_: dict[str, str]) -> bool | None:
global CACHE # pylint: disable=global-statement
CACHE = EngineCache("resulthunter")
def setup(_: dict[str, t.Any]) -> bool | None:
if resulthunter_categ not in ("web", "images"):
raise ValueError("invalid category: %s" % resulthunter_categ)
def _get_search_token() -> tuple[str, dict[str, str]]:
if cached := CACHE.get("token"):
return cached
resp = get(base_url)
token = _SEARCH_TOKEN_RE.search(resp.text)
if not token:
raise SearxEngineAPIException("failed to find search token")
token = token.group(1)
cookies = dict(resp.cookies)
CACHE.set("token", (token, cookies))
return token, cookies
def request(query: str, params: "OnlineParams") -> None:
search_token, cookies = _get_search_token()
params["cookies"] = cookies
args = {
"q": query,
"search_type": resulthunter_categ,
"offset": params["pageno"] - 1,
"search_source": "other",
"search_source": "header",
"st": search_token,
}
# uses Brave's engine traits
+15 -10
View File
@@ -48,16 +48,21 @@ def response(resp: "SXNG_Response") -> EngineResults:
mainline = [s for s in data["search"]["regions"] if s["name"] == "mainline"][0]
for group in mainline["groups"]:
for result in group["results"]:
if not ("url" in result or "clickUrl" in result):
continue
match group["result_type"]:
case "web":
for result in group["results"]:
res.add(
res.types.MainResult(
url=result.get("url") or result.get("clickUrl"),
title=html_to_text(result["title"]),
content=html_to_text(result["description"]),
)
)
case "aylf":
for result in group["results"]:
res.add(res.types.LegacyResult(suggestion=result["term"]))
res.add(
res.types.MainResult(
url=result.get("url") or result.get("clickUrl"),
title=html_to_text(result["title"]),
content=html_to_text(result["description"]),
)
)
case _:
pass
return res
+1 -1
View File
@@ -32,7 +32,7 @@ about = {
}
language = "de"
categories = ['general', 'news']
categories = ['news']
paging = True
results_per_page = 10
+1 -1
View File
@@ -167,7 +167,7 @@ def get_results(
infobox_id_lang: str | None = None
infobox_urls: list[dict[str, str]] = []
infobox_attributes: list[dict[str, str]] = []
infobox_content = attribute_result.get("itemDescription", [])
infobox_content: str = attribute_result.get("itemDescription", "")
img_src: str | None = None
img_src_priority = 0
+18 -2
View File
@@ -4,6 +4,8 @@
import json
import typing as t
from searx.exceptions import SearxEngineAPIException, SearxEngineTooManyRequestsException
from searx.network import raise_for_httperror
from searx.result_types import EngineResults
if t.TYPE_CHECKING:
@@ -53,13 +55,27 @@ def request(query: str, params: "OnlineParams"):
"source": "",
}
params["raise_for_httperror"] = False
def response(resp: "SXNG_Response") -> EngineResults:
if resp.status_code in (400, 429):
raise SearxEngineTooManyRequestsException()
raise_for_httperror(resp)
res = EngineResults()
# response is a Server-Side Events (SSE) stream
data_raw = resp.text.split("\n", 1)[0].removeprefix("data: ")
data = json.loads(data_raw)
# we search for a JSON object with the "results" key
data_parts = [line for line in resp.text.split("\n") if line]
for data_raw in data_parts:
data_raw = data_raw.removeprefix("data: ")
data: dict[str, t.Any] = json.loads(data_raw)
if "results" in data:
break
else:
raise SearxEngineAPIException("no results in response")
result: dict[str, str]
for result in data["results"]:
+1
View File
@@ -48,6 +48,7 @@ def request(query: str, params: "OnlineParams") -> None:
{
"Referer": f"{web_base_url}/",
"Origin": web_base_url,
"x-csrf-protection": "1",
}
)
+2 -2
View File
@@ -32,10 +32,10 @@ def init():
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}")
logger.error("missing favicon config: %s", cfg_file)
cfg_file = config.DEFAULT_CFG_TOML_PATH
logger.debug(f"load favicon config: {cfg_file}")
logger.debug("load favicon config: %s", cfg_file)
cfg = config.FaviconConfig.from_toml_file(cfg_file, use_cache=True)
cache.init(cfg.cache)
proxy.init(cfg.proxy)
+1 -1
View File
@@ -352,7 +352,7 @@ CREATE TABLE IF NOT EXISTS blob_map (
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)
"favicon of resolver: %s / authority: %s to big to cache (bytes: %s) ", resolver, authority, bytes_c
)
return False
+1 -1
View File
@@ -137,7 +137,7 @@ class Request(t.NamedTuple):
method: str
url: str
kwargs: dict[str, str] = {}
kwargs: dict[str, str] = {} # pylint: disable=dangerous-default-value
@staticmethod
def get(url: str, **kwargs: t.Any):
-8
View File
@@ -409,14 +409,6 @@ def initialize(
if isinstance(network, str):
NETWORKS[engine_name] = NETWORKS[network]
# the /image_proxy endpoint has a dedicated network.
# same parameters than the default network, but HTTP/2 is disabled.
# It decreases the CPU load average, and the total time is more or less the same
if 'image_proxy' not in NETWORKS:
image_proxy_params = default_params.copy()
image_proxy_params['enable_http2'] = False
NETWORKS['image_proxy'] = new_network(image_proxy_params, logger_name='image_proxy')
@atexit.register
def done():
+9 -8
View File
@@ -19,9 +19,6 @@ if typing.TYPE_CHECKING:
from searx.plugins import PluginCfg
ahmia_blacklist: list = []
def filter_url_field(result: "Result|LegacyResult", field_name: str, url_src: str) -> bool | str:
"""Returns bool ``True`` to use URL unchanged (``False`` to ignore URL).
If URL should be modified, the returned string is the new URL to use."""
@@ -29,11 +26,15 @@ def filter_url_field(result: "Result|LegacyResult", field_name: str, url_src: st
if field_name != "url":
return True # use it unchanged
resolver_url = get_doi_resolver()
if not resolver_url:
return True # use it unchanged
doi = extract_doi(result.parsed_url)
if doi and len(doi) < 50:
for suffix in ("/", ".pdf", ".xml", "/full", "/meta", "/abstract"):
doi = doi.removesuffix(suffix)
new_url = get_doi_resolver() + doi
new_url = resolver_url + doi
if "doi" not in result:
result["doi"] = doi
log.debug("oa_doi_rewrite: [URL field: %s] %s -> %s", field_name, url_src, new_url)
@@ -53,7 +54,7 @@ class SXNGPlugin(Plugin):
id=self.id,
name=gettext("Open Access DOI rewrite"),
description=gettext("Avoid paywalls by redirecting to open-access versions of publications when available"),
preference_section="general",
preference_section=None,
)
def on_result(
@@ -83,7 +84,7 @@ def extract_doi(url):
def get_doi_resolver() -> str:
doi_resolvers = get_setting("doi_resolvers")
selected_resolver = sxng_request.preferences.get_value('doi_resolver')[0]
if selected_resolver not in doi_resolvers:
selected_resolver = get_setting("default_doi_resolver")
selected_resolver = sxng_request.preferences.get_value("doi_resolver")
if not selected_resolver or selected_resolver not in doi_resolvers:
return ""
return doi_resolvers[selected_resolver]
+7 -3
View File
@@ -457,10 +457,10 @@ class Preferences:
get_setting("ui.results_on_new_tab"),
locked="results_on_new_tab" in self.cfg.lock,
),
'doi_resolver': MultipleChoiceSetting(
[get_setting("default_doi_resolver")],
"doi_resolver": EnumStringSetting(
get_setting("default_doi_resolver"),
locked="doi_resolver" in self.cfg.lock,
choices=DOI_RESOLVERS,
choices=["", *DOI_RESOLVERS],
),
'simple_style': EnumStringSetting(
get_setting("ui.theme_args.simple_style"),
@@ -536,6 +536,8 @@ class Preferences:
self.plugins.parse_cookie(input_data.get('disabled_plugins', ''), input_data.get('enabled_plugins', ''))
elif user_setting_name == 'tokens':
self.tokens.parse(user_setting)
if "oa_doi_rewrite" in self.plugins.choices:
self.plugins.choices["oa_doi_rewrite"] = bool(self.get_value("doi_resolver"))
def parse_form(self, input_data: dict[str, str]):
"""Parse formular (``<input>``) data from a ``flask.request.form``"""
@@ -564,6 +566,8 @@ class Preferences:
self.key_value_settings['categories'].parse_form(enabled_categories) # type: ignore
self.engines.parse_form(disabled_engines)
self.plugins.parse_form(disabled_plugins)
if "oa_doi_rewrite" in self.plugins.choices:
self.plugins.choices["oa_doi_rewrite"] = bool(self.get_value("doi_resolver"))
# cannot be used in case of engines or plugins
def get_value(self, user_setting_name: str) -> t.Any:
+2
View File
@@ -182,6 +182,8 @@ class ResultContainer:
def close(self):
self._closed = True
# infobox with images first
self.infoboxes.sort(key=lambda infobox: not infobox.img_src)
for result in self.main_results_map.values():
result.score = calculate_score(result, result.priority)
+5 -3
View File
@@ -126,9 +126,11 @@ class Search:
actual_timeout = min(query_timeout, max_request_timeout)
logger.debug(
"actual_timeout={0} (default_timeout={1}, ?timeout_limit={2}, max_request_timeout={3})".format(
actual_timeout, default_timeout, query_timeout, max_request_timeout
)
"actual_timeout=%s (default_timeout=%s, ?timeout_limit=%s, max_request_timeout=%s)",
actual_timeout,
default_timeout,
query_timeout,
max_request_timeout,
)
return requests, actual_timeout
+2 -2
View File
@@ -71,7 +71,7 @@ class ProcessorMap(dict[str, EngineProcessor]):
eng_type = getattr(eng_obj, "engine_type", "online")
proc_cls = self.processor_types.get(eng_type)
if proc_cls is None:
logger.error("Engine '%s' is of unknown engine_type: %s", eng_type)
logger.error("Engine of name '%s' is of unknown engine_type: %s", eng_name, eng_type)
continue
# initialize (and register) the engine
@@ -94,7 +94,7 @@ class ProcessorMap(dict[str, EngineProcessor]):
# logger.debug("registered engine processor: %s", eng_proc.engine.name)
else:
logger.error(
f"(PID {os.getpid()}) {eng_proc.engine.name}: can't register engines processor (init engine failed)"
"(PID %s) %s: can't register engines processor (init engine failed)", os.getpid(), eng_proc.engine.name
)
return eng_proc_ok
+2 -2
View File
@@ -161,7 +161,7 @@ class EngineProcessor(ABC):
try:
init_ok = self.engine.init(eng_setting)
except Exception as e: # pylint: disable=broad-except
logger.exception(f"(PID {os.getpid()}) {self.engine.name}: engine INIT failed, exception: {e}")
logger.exception("(PID %s) %s: engine INIT failed, exception: %s", os.getpid(), self.engine.name, e)
init_ok = False
# In older engines, None is returned from the init method, which is
@@ -172,7 +172,7 @@ class EngineProcessor(ABC):
init_ok = True
if not init_ok:
logger.error(f"(PID {os.getpid()}) {self.engine.name}: engine init was not successful")
logger.error("(PID %s) %s: engine init was not successful", os.getpid(), self.engine.name)
return init_ok
+1 -1
View File
@@ -470,7 +470,7 @@ engines:
suggestion_xpath: .//a[starts-with(@href, "https://search.ayo.de")]
disabled: true
about:
website: https://serach.ayo.de
website: https://search.ayo.de
use_official_api: false
require_api_key: false
results: HTML
-2
View File
@@ -1,2 +0,0 @@
import{n as e}from"../sxng-core.min.js";import{t}from"./DcK-mo-Y.min.js";var n=t(`search`),r=t(`q`),i=t(`clear_search`),a=window.matchMedia(`(max-width: 50em)`).matches,o=document.querySelector(`main`)?.id===`main_results`,s=Array.from(document.querySelectorAll(`#categories_container button.category`));a||o||r.focus(),a&&e(`focus`,r,()=>{requestAnimationFrame(()=>{let e=r.value.length;r.setSelectionRange(e,e),r.scrollLeft=r.scrollWidth})}),e(`click`,i,e=>{e.preventDefault(),r.value=``,r.focus()});for(let t of s)e(`click`,t,e=>{if(e.shiftKey){e.preventDefault(),t.classList.toggle(`selected`);return}for(let e of s)e.classList.toggle(`selected`,e===t)});if(document.querySelector(`div.search_filters`)){let t=document.getElementById(`safesearch`);t&&e(`change`,t,()=>n.submit());let r=document.getElementById(`time_range`);r&&e(`change`,r,()=>n.submit());let i=document.getElementById(`language`);i&&e(`change`,i,()=>n.submit())}e(`submit`,n,e=>{if(e.preventDefault(),s.length>0){let e=t(`selected-categories`);e.value=s.filter(e=>e.classList.contains(`selected`)).map(e=>e.name.replace(`category_`,``)).join(`,`)}n.submit()});
//# sourceMappingURL=6liDwPCz.min.js.map
@@ -1 +0,0 @@
{"version":3,"file":"6liDwPCz.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/search.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../toolkit.ts\";\nimport { getElement } from \"../util/getElement.ts\";\n\nconst searchForm: HTMLFormElement = getElement<HTMLFormElement>(\"search\");\nconst searchInput: HTMLInputElement = getElement<HTMLInputElement>(\"q\");\nconst searchReset: HTMLButtonElement = getElement<HTMLButtonElement>(\"clear_search\");\n\nconst isMobile: boolean = window.matchMedia(\"(max-width: 50em)\").matches;\nconst isResultsPage: boolean = document.querySelector(\"main\")?.id === \"main_results\";\n\nconst categoryButtons: HTMLButtonElement[] = Array.from(\n document.querySelectorAll<HTMLButtonElement>(\"#categories_container button.category\")\n);\n\n// focus search input on large screens\nif (!(isMobile || isResultsPage)) {\n searchInput.focus();\n}\n\n// On mobile, move cursor to the end of the input on focus\nif (isMobile) {\n listen(\"focus\", searchInput, () => {\n // Defer cursor move until the next frame to prevent a visual jump\n requestAnimationFrame(() => {\n const end = searchInput.value.length;\n searchInput.setSelectionRange(end, end);\n searchInput.scrollLeft = searchInput.scrollWidth;\n });\n });\n}\n\nlisten(\"click\", searchReset, (event: MouseEvent) => {\n event.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n});\n\nfor (const button of categoryButtons) {\n listen(\"click\", button, (event: MouseEvent) => {\n if (event.shiftKey) {\n event.preventDefault();\n button.classList.toggle(\"selected\");\n return;\n }\n\n // deselect all other categories\n for (const categoryButton of categoryButtons) {\n categoryButton.classList.toggle(\"selected\", categoryButton === button);\n }\n });\n}\n\nif (document.querySelector(\"div.search_filters\")) {\n const safesearchElement = document.getElementById(\"safesearch\");\n if (safesearchElement) {\n listen(\"change\", safesearchElement, () => searchForm.submit());\n }\n\n const timeRangeElement = document.getElementById(\"time_range\");\n if (timeRangeElement) {\n listen(\"change\", timeRangeElement, () => searchForm.submit());\n }\n\n const languageElement = document.getElementById(\"language\");\n if (languageElement) {\n listen(\"change\", languageElement, () => searchForm.submit());\n }\n}\n\n// override searchForm submit event\nlisten(\"submit\", searchForm, (event: Event) => {\n event.preventDefault();\n\n if (categoryButtons.length > 0) {\n const searchCategories = getElement<HTMLInputElement>(\"selected-categories\");\n searchCategories.value = categoryButtons\n .filter((button) => button.classList.contains(\"selected\"))\n .map((button) => button.name.replace(\"category_\", \"\"))\n .join(\",\");\n }\n\n searchForm.submit();\n});\n"],"mappings":"yEAKA,IAAM,EAA8B,EAA4B,QAAQ,EAClE,EAAgC,EAA6B,GAAG,EAChE,EAAiC,EAA8B,cAAc,EAE7E,EAAoB,OAAO,WAAW,mBAAmB,CAAC,CAAC,QAC3D,EAAyB,SAAS,cAAc,MAAM,CAAC,EAAE,KAAO,eAEhE,EAAuC,MAAM,KACjD,SAAS,iBAAoC,uCAAuC,CACtF,EAGM,GAAY,GAChB,EAAY,MAAM,EAIhB,GACF,EAAO,QAAS,MAAmB,CAEjC,0BAA4B,CAC1B,IAAM,EAAM,EAAY,MAAM,OAC9B,EAAY,kBAAkB,EAAK,CAAG,EACtC,EAAY,WAAa,EAAY,WACvC,CAAC,CACH,CAAC,EAGH,EAAO,QAAS,EAAc,GAAsB,CAClD,EAAM,eAAe,EACrB,EAAY,MAAQ,GACpB,EAAY,MAAM,CACpB,CAAC,EAED,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,EAAS,GAAsB,CAC7C,GAAI,EAAM,SAAU,CAClB,EAAM,eAAe,EACrB,EAAO,UAAU,OAAO,UAAU,EAClC,MACF,CAGA,IAAK,IAAM,KAAkB,EAC3B,EAAe,UAAU,OAAO,WAAY,IAAmB,CAAM,CAEzE,CAAC,EAGH,GAAI,SAAS,cAAc,oBAAoB,EAAG,CAChD,IAAM,EAAoB,SAAS,eAAe,YAAY,EAC1D,GACF,EAAO,SAAU,MAAyB,EAAW,OAAO,CAAC,EAG/D,IAAM,EAAmB,SAAS,eAAe,YAAY,EACzD,GACF,EAAO,SAAU,MAAwB,EAAW,OAAO,CAAC,EAG9D,IAAM,EAAkB,SAAS,eAAe,UAAU,EACtD,GACF,EAAO,SAAU,MAAuB,EAAW,OAAO,CAAC,CAE/D,CAGA,EAAO,SAAU,EAAa,GAAiB,CAG7C,GAFA,EAAM,eAAe,EAEjB,EAAgB,OAAS,EAAG,CAC9B,IAAM,EAAmB,EAA6B,qBAAqB,EAC3E,EAAiB,MAAQ,EACtB,OAAQ,GAAW,EAAO,UAAU,SAAS,UAAU,CAAC,CAAC,CACzD,IAAK,GAAW,EAAO,KAAK,QAAQ,YAAa,EAAE,CAAC,CAAC,CACrD,KAAK,GAAG,CACb,CAEA,EAAW,OAAO,CACpB,CAAC"}
+2
View File
@@ -0,0 +1,2 @@
import{n as e}from"../sxng-core.min.js";import{t}from"./DcK-mo-Y.min.js";var n=t(`search`),r=t(`q`),i=t(`clear_search`),a=window.matchMedia(`(max-width: 50em)`).matches,o=document.querySelector(`main`)?.id===`main_results`,s=Array.from(document.querySelectorAll(`#categories_container button.category`));a||o||r.focus(),a&&e(`focus`,r,()=>{requestAnimationFrame(()=>{let e=r.value.length;r.setSelectionRange(e,e),r.scrollLeft=r.scrollWidth})}),e(`click`,i,e=>{e.preventDefault(),r.value=``,r.focus()});for(let t of s)e(`click`,t,e=>{if(e.shiftKey)e.preventDefault(),t.classList.toggle(`selected`);else for(let e of s)e.classList.toggle(`selected`,e===t)});if(document.querySelector(`div.search_filters`)){let t=document.getElementById(`safesearch`);t&&e(`change`,t,()=>n.submit());let r=document.getElementById(`time_range`);r&&e(`change`,r,()=>n.submit());let i=document.getElementById(`language`);i&&e(`change`,i,()=>n.submit())}e(`submit`,n,e=>{if(e.preventDefault(),s.length>0){let e=t(`selected-categories`);e.value=s.filter(e=>e.classList.contains(`selected`)).map(e=>e.name.replace(`category_`,``)).join(`,`)}n.submit()});
//# sourceMappingURL=BMrKiJ3q.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"BMrKiJ3q.min.js","names":["listen","searchForm","getElement","searchInput","searchReset","isMobile","isResultsPage","categoryButtons","end","event","button","categoryButton","safesearchElement","timeRangeElement","languageElement","searchCategories"],"sources":["../../../../../client/simple/src/js/main/search.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { listen } from \"../toolkit.ts\";\nimport { getElement } from \"../util/getElement.ts\";\n\nconst searchForm: HTMLFormElement = getElement<HTMLFormElement>(\"search\");\nconst searchInput: HTMLInputElement = getElement<HTMLInputElement>(\"q\");\nconst searchReset: HTMLButtonElement = getElement<HTMLButtonElement>(\"clear_search\");\n\nconst isMobile: boolean = window.matchMedia(\"(max-width: 50em)\").matches;\nconst isResultsPage: boolean = document.querySelector(\"main\")?.id === \"main_results\";\n\nconst categoryButtons: HTMLButtonElement[] = Array.from(\n document.querySelectorAll<HTMLButtonElement>(\"#categories_container button.category\")\n);\n\n// focus search input on large screens\nif (!(isMobile || isResultsPage)) {\n searchInput.focus();\n}\n\n// On mobile, move cursor to the end of the input on focus\nif (isMobile) {\n listen(\"focus\", searchInput, () => {\n // Defer cursor move until the next frame to prevent a visual jump\n requestAnimationFrame(() => {\n const end = searchInput.value.length;\n searchInput.setSelectionRange(end, end);\n searchInput.scrollLeft = searchInput.scrollWidth;\n });\n });\n}\n\nlisten(\"click\", searchReset, (event: MouseEvent) => {\n event.preventDefault();\n searchInput.value = \"\";\n searchInput.focus();\n});\n\nfor (const button of categoryButtons) {\n listen(\"click\", button, (event: MouseEvent) => {\n if (event.shiftKey) {\n event.preventDefault();\n button.classList.toggle(\"selected\");\n return;\n }\n\n // deselect all other categories\n for (const categoryButton of categoryButtons) {\n categoryButton.classList.toggle(\"selected\", categoryButton === button);\n }\n });\n}\n\nif (document.querySelector(\"div.search_filters\")) {\n const safesearchElement = document.getElementById(\"safesearch\");\n if (safesearchElement) {\n listen(\"change\", safesearchElement, () => searchForm.submit());\n }\n\n const timeRangeElement = document.getElementById(\"time_range\");\n if (timeRangeElement) {\n listen(\"change\", timeRangeElement, () => searchForm.submit());\n }\n\n const languageElement = document.getElementById(\"language\");\n if (languageElement) {\n listen(\"change\", languageElement, () => searchForm.submit());\n }\n}\n\n// override searchForm submit event\nlisten(\"submit\", searchForm, (event: Event) => {\n event.preventDefault();\n\n if (categoryButtons.length > 0) {\n const searchCategories = getElement<HTMLInputElement>(\"selected-categories\");\n searchCategories.value = categoryButtons\n .filter((button) => button.classList.contains(\"selected\"))\n .map((button) => button.name.replace(\"category_\", \"\"))\n .join(\",\");\n }\n\n searchForm.submit();\n});\n"],"mappings":"yEAKA,IAAMC,EAA8BC,EAA4B,QAAQ,EAClEC,EAAgCD,EAA6B,GAAG,EAChEE,EAAiCF,EAA8B,cAAc,EAE7EG,EAAoB,OAAO,WAAW,mBAAmB,CAAC,CAAC,QAC3DC,EAAyB,SAAS,cAAc,MAAM,CAAC,EAAE,KAAO,eAEhEC,EAAuC,MAAM,KACjD,SAAS,iBAAoC,uCAAuC,CACtF,EAGMF,GAAYC,GAChBH,EAAY,MAAM,EAIhBE,GACFL,EAAO,QAASG,MAAmB,CAEjC,0BAA4B,CAC1B,IAAMK,EAAML,EAAY,MAAM,OAC9BA,EAAY,kBAAkBK,EAAKA,CAAG,EACtC,EAAY,WAAaL,EAAY,WACvC,CAAC,CACH,CAAC,EAGHH,EAAO,QAASI,EAAc,GAAsB,CAClDK,EAAM,eAAe,EACrB,EAAY,MAAQ,GACpBN,EAAY,MAAM,CACpB,CAAC,EAED,IAAK,IAAMO,KAAUH,EACnBP,EAAO,QAASU,EAAS,GAAsB,CAC7C,GAAID,EAAM,SACRA,EAAM,eAAe,EACrBC,EAAO,UAAU,OAAO,UAAU,OAKpC,IAAK,IAAMC,KAAkBJ,EAC3BI,EAAe,UAAU,OAAO,WAAYA,IAAmBD,CAAM,CAEzE,CAAC,EAGH,GAAI,SAAS,cAAc,oBAAoB,EAAG,CAChD,IAAME,EAAoB,SAAS,eAAe,YAAY,EAC1DA,GACFZ,EAAO,SAAUY,MAAyBX,EAAW,OAAO,CAAC,EAG/D,IAAMY,EAAmB,SAAS,eAAe,YAAY,EACzDA,GACFb,EAAO,SAAUa,MAAwBZ,EAAW,OAAO,CAAC,EAG9D,IAAMa,EAAkB,SAAS,eAAe,UAAU,EACtDA,GACFd,EAAO,SAAUc,MAAuBb,EAAW,OAAO,CAAC,CAE/D,CAGAD,EAAO,SAAUC,EAAa,GAAiB,CAG7C,GAFAQ,EAAM,eAAe,EAEjBF,EAAgB,OAAS,EAAG,CAC9B,IAAMQ,EAAmBb,EAA6B,qBAAqB,EAC3E,EAAiB,MAAQK,EACtB,OAAQ,GAAWG,EAAO,UAAU,SAAS,UAAU,CAAC,CAAC,CACzD,IAAK,GAAWA,EAAO,KAAK,QAAQ,YAAa,EAAE,CAAC,CAAC,CACrD,KAAK,GAAG,CACb,CAEAT,EAAW,OAAO,CACpB,CAAC"}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
{"version":3,"file":"BuurKv-k.min.js","names":[],"sources":["../../../../../client/simple/src/js/Plugin.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/**\n * Base class for client-side plugins.\n *\n * @remarks\n * Handle conditional loading of the plugin in:\n *\n * - client/simple/src/js/router.ts\n *\n * @abstract\n */\nexport abstract class Plugin {\n /**\n * Plugin name.\n */\n protected readonly id: string;\n\n /**\n * @remarks\n * Don't hold references of this instance outside the class.\n */\n protected constructor(id: string) {\n this.id = id;\n\n queueMicrotask(() => this.invoke());\n }\n\n private async invoke(): Promise<void> {\n try {\n console.debug(`[PLUGIN] ${this.id}: Running...`);\n const result = await this.run();\n if (!result) return;\n\n console.debug(`[PLUGIN] ${this.id}: Running post-exec...`);\n // @ts-expect-error\n void (await this.post(result as NonNullable<Awaited<ReturnType<this[\"run\"]>>>));\n } catch (error) {\n console.error(`[PLUGIN] ${this.id}:`, error);\n } finally {\n console.debug(`[PLUGIN] ${this.id}: Done.`);\n }\n }\n\n /**\n * Plugin goes here.\n *\n * @remarks\n * The plugin is already loaded at this point. If you wish to execute\n * conditions to exit early, consider moving the logic to:\n *\n * - client/simple/src/js/router.ts\n *\n * ...to avoid unnecessarily loading this plugin on the client.\n */\n protected abstract run(): Promise<unknown>;\n\n /**\n * Post-execution hook.\n *\n * @remarks\n * The hook is only executed if `#run()` returns a truthy value.\n */\n // @ts-expect-error\n protected abstract post(result: NonNullable<Awaited<ReturnType<this[\"run\"]>>>): Promise<void>;\n}\n"],"mappings":"AAYA,IAAsB,EAAtB,KAA6B,CAI3B,GAMA,YAAsB,EAAY,CAChC,KAAK,GAAK,EAEV,mBAAqB,KAAK,OAAO,CAAC,CACpC,CAEA,MAAc,QAAwB,CACpC,GAAI,CACF,QAAQ,MAAM,YAAY,KAAK,GAAG,aAAa,EAC/C,IAAM,EAAS,MAAM,KAAK,IAAI,EAC9B,GAAI,CAAC,EAAQ,OAEb,QAAQ,MAAM,YAAY,KAAK,GAAG,uBAAuB,EAEzD,MAAY,KAAK,KAAK,CAAuD,CAC/E,OAAS,EAAO,CACd,QAAQ,MAAM,YAAY,KAAK,GAAG,GAAI,CAAK,CAC7C,QAAU,CACR,QAAQ,MAAM,YAAY,KAAK,GAAG,QAAQ,CAC5C,CACF,CAuBF"}
{"version":3,"file":"BuurKv-k.min.js","names":["Plugin","id","result","error"],"sources":["../../../../../client/simple/src/js/Plugin.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/**\n * Base class for client-side plugins.\n *\n * @remarks\n * Handle conditional loading of the plugin in:\n *\n * - client/simple/src/js/router.ts\n *\n * @abstract\n */\nexport abstract class Plugin {\n /**\n * Plugin name.\n */\n protected readonly id: string;\n\n /**\n * @remarks\n * Don't hold references of this instance outside the class.\n */\n protected constructor(id: string) {\n this.id = id;\n\n queueMicrotask(() => this.invoke());\n }\n\n private async invoke(): Promise<void> {\n try {\n console.debug(`[PLUGIN] ${this.id}: Running...`);\n const result = await this.run();\n if (!result) return;\n\n console.debug(`[PLUGIN] ${this.id}: Running post-exec...`);\n // @ts-expect-error\n void (await this.post(result as NonNullable<Awaited<ReturnType<this[\"run\"]>>>));\n } catch (error) {\n console.error(`[PLUGIN] ${this.id}:`, error);\n } finally {\n console.debug(`[PLUGIN] ${this.id}: Done.`);\n }\n }\n\n /**\n * Plugin goes here.\n *\n * @remarks\n * The plugin is already loaded at this point. If you wish to execute\n * conditions to exit early, consider moving the logic to:\n *\n * - client/simple/src/js/router.ts\n *\n * ...to avoid unnecessarily loading this plugin on the client.\n */\n protected abstract run(): Promise<unknown>;\n\n /**\n * Post-execution hook.\n *\n * @remarks\n * The hook is only executed if `#run()` returns a truthy value.\n */\n // @ts-expect-error\n protected abstract post(result: NonNullable<Awaited<ReturnType<this[\"run\"]>>>): Promise<void>;\n}\n"],"mappings":"AAYA,IAAsBA,EAAtB,KAA6B,CAI3B,GAMA,YAAsB,EAAY,CAChC,KAAK,GAAKC,EAEV,mBAAqB,KAAK,OAAO,CAAC,CACpC,CAEA,MAAc,QAAwB,CACpC,GAAI,CACF,QAAQ,MAAM,YAAY,KAAK,GAAG,aAAa,EAC/C,IAAMC,EAAS,MAAM,KAAK,IAAI,EAC9B,GAAI,CAACA,EAAQ,OAEb,QAAQ,MAAM,YAAY,KAAK,GAAG,uBAAuB,EAEzD,MAAY,KAAK,KAAKA,CAAuD,CAC/E,OAASC,EAAO,CACd,QAAQ,MAAM,YAAY,KAAK,GAAG,GAAIA,CAAK,CAC7C,QAAU,CACR,QAAQ,MAAM,YAAY,KAAK,GAAG,QAAQ,CAC5C,CACF,CAuBF"}
+11
View File
@@ -0,0 +1,11 @@
import{i as e,n as t,r as n}from"../sxng-core.min.js";import{t as r}from"./DK4yUVpy.min.js";
/*!
* swiped-events.js - v@version@
* Pure JavaScript swipe events
* https://github.com/john-doherty/swiped-events
* @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element
* @author John Doherty <www.johndoherty.info>
* @license MIT
*/
(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p<d&&(m=i>0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p<d&&(m=a>0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(n&&r){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=`${e.theme_static_path}/img/img_load_error.svg`,o=e=>{i&&clearTimeout(i);let t=e.querySelector(`.result-images-source img`);if(!t)return;let n=e.querySelector(`.image_thumbnail`);if(n){if(n.src===a)return;t.onerror=()=>{t.src=n.src},t.src=n.src}let r=t.getAttribute(`data-src`);r&&(i=setTimeout(()=>{t.src=r,t.removeAttribute(`data-src`)},1e3))},s=e=>{e.classList.contains(`thumbnail`)?e.closest(`a.thumbnail_link`)?.remove():e.classList.contains(`image_thumbnail`)&&e.src!==a&&(e.src=a)};document.addEventListener(`error`,e=>{let t=e.target;t instanceof HTMLImageElement&&s(t)},!0);for(let e of document.querySelectorAll(`img.thumbnail, #urls img.image_thumbnail`))e.complete&&e.naturalWidth===0&&s(e);document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),n.selectImage=e=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,n.scrollPageToSelected?.(),e&&o(e)},n.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),n.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),n=this.getAttribute(`data-target`);if(!(n&&e&&t))return;let i=document.querySelector(n);r(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(r(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);if(r(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}this.dataset.copiedText&&(this.innerText=this.dataset.copiedText)}),t(`click`,`.result-detail-close`,e=>{e.preventDefault(),n.closeDetail?.()}),t(`click`,`.result-detail-previous`,e=>{e.preventDefault(),n.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,e=>{e.preventDefault(),n.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&n.closeDetail?.()});var c=document.querySelectorAll(`.swipe-horizontal`);for(let e of c)t(`swiped-left`,e,()=>{n.selectNext?.(!1)}),t(`swiped-right`,e,()=>{n.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0);
//# sourceMappingURL=CFQtNTi4.min.js.map
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
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
{"version":3,"file":"DK4yUVpy.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/assertElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\ntype AssertElement = <T>(element?: T | null) => asserts element is T;\nexport const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {\n if (!element) {\n throw new Error(\"DOM element not found\");\n }\n};\n"],"mappings":"AAGA,IAAa,EAAmC,GAA6C,CAC3F,GAAI,CAAC,EACH,MAAU,MAAM,uBAAuB,CAE3C"}
{"version":3,"file":"DK4yUVpy.min.js","names":["assertElement","element"],"sources":["../../../../../client/simple/src/js/util/assertElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\ntype AssertElement = <T>(element?: T | null) => asserts element is T;\nexport const assertElement: AssertElement = <T>(element?: T | null): asserts element is T => {\n if (!element) {\n throw new Error(\"DOM element not found\");\n }\n};\n"],"mappings":"AAGA,IAAaA,EAAmC,GAA6C,CAC3F,GAAI,CAACC,EACH,MAAU,MAAM,uBAAuB,CAE3C"}
-11
View File
@@ -1,11 +0,0 @@
import{i as e,n as t,r as n}from"../sxng-core.min.js";import{t as r}from"./DK4yUVpy.min.js";
/*!
* swiped-events.js - v@version@
* Pure JavaScript swipe events
* https://github.com/john-doherty/swiped-events
* @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element
* @author John Doherty <www.johndoherty.info>
* @license MIT
*/
(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p<d&&(m=i>0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p<d&&(m=a>0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(n&&r){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=`${e.theme_static_path}/img/img_load_error.svg`,o=e=>{i&&clearTimeout(i);let t=e.querySelector(`.result-images-source img`);if(!t)return;let n=e.querySelector(`.image_thumbnail`);if(n){if(n.src===a)return;t.onerror=()=>{t.src=n.src},t.src=n.src}let r=t.getAttribute(`data-src`);r&&(i=setTimeout(()=>{t.src=r,t.removeAttribute(`data-src`)},1e3))},s=e=>{if(e.classList.contains(`thumbnail`)){e.closest(`a.thumbnail_link`)?.remove();return}e.classList.contains(`image_thumbnail`)&&e.src!==a&&(e.src=a)};document.addEventListener(`error`,e=>{let t=e.target;t instanceof HTMLImageElement&&s(t)},!0);for(let e of document.querySelectorAll(`img.thumbnail, #urls img.image_thumbnail`))e.complete&&e.naturalWidth===0&&s(e);document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),n.selectImage=e=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,n.scrollPageToSelected?.(),e&&o(e)},n.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),n.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),n=this.getAttribute(`data-target`);if(!(n&&e&&t))return;let i=document.querySelector(n);r(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(r(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);if(r(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}this.dataset.copiedText&&(this.innerText=this.dataset.copiedText)}),t(`click`,`.result-detail-close`,e=>{e.preventDefault(),n.closeDetail?.()}),t(`click`,`.result-detail-previous`,e=>{e.preventDefault(),n.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,e=>{e.preventDefault(),n.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&n.closeDetail?.()});var c=document.querySelectorAll(`.swipe-horizontal`);for(let e of c)t(`swiped-left`,e,()=>{n.selectNext?.(!1)}),t(`swiped-right`,e,()=>{n.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0);
//# sourceMappingURL=DT2OB9zd.min.js.map
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
{"version":3,"file":"DcK-mo-Y.min.js","names":[],"sources":["../../../../../client/simple/src/js/util/getElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement } from \"./assertElement.ts\";\n\ntype Options = {\n assert?: boolean;\n};\n\nexport function getElement<T>(id: string, options?: { assert: true }): T;\nexport function getElement<T>(id: string, options?: { assert: false }): T | null;\nexport function getElement<T>(id: string, options: Options = {}): T | null {\n options.assert ??= true;\n\n const element = document.getElementById(id) as T | null;\n\n if (options.assert) {\n assertElement(element);\n }\n\n return element;\n}\n"],"mappings":"sCAUA,SAAgB,EAAc,EAAY,EAAmB,CAAC,EAAa,CACzE,EAAQ,SAAW,GAEnB,IAAM,EAAU,SAAS,eAAe,CAAE,EAM1C,OAJI,EAAQ,QACV,EAAc,CAAO,EAGhB,CACT"}
{"version":3,"file":"DcK-mo-Y.min.js","names":["assertElement","getElement","element","id","options"],"sources":["../../../../../client/simple/src/js/util/getElement.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement } from \"./assertElement.ts\";\n\ntype Options = {\n assert?: boolean;\n};\n\nexport function getElement<T>(id: string, options?: { assert: true }): T;\nexport function getElement<T>(id: string, options?: { assert: false }): T | null;\nexport function getElement<T>(id: string, options: Options = {}): T | null {\n options.assert ??= true;\n\n const element = document.getElementById(id) as T | null;\n\n if (options.assert) {\n assertElement(element);\n }\n\n return element;\n}\n"],"mappings":"sCAUA,SAAgBC,EAAc,EAAY,EAAmB,CAAC,EAAa,CACzE,EAAQ,SAAW,GAEnB,IAAMC,EAAU,SAAS,eAAeC,CAAE,EAM1C,OAJIC,EAAQ,QACVJ,EAAcE,CAAO,EAGhBA,CACT"}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
{"version":3,"file":"e2-9fzwE.min.js","names":[],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../toolkit.ts\";\nimport { assertElement } from \"../util/assertElement.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n if (this.dataset.copiedText) {\n this.innerText = this.dataset.copiedText;\n }\n});\n"],"mappings":"4FAKA,IAAI,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MAAM,MADT,EAAK,MAAO,0BAA0B,EAAA,CACzB,KAAK,CACtC,OAAS,EAAO,CACd,QAAQ,MAAM,qCAAsC,CAAK,CAC3D,CACK,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,CAAkB,EAAG,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,uBAAuB,EAC3G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,CAEtC,CAVA,CAWF,EAEM,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,EAG9B,EAEM,EAA0C,SAAS,iBAA8B,oBAAoB,EAC3G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,CAAsB,EAG5D,IAAM,EAA8C,SAAS,iBAC3D,mDACF,EAEM,EAA4C,SAAS,iBAA8B,qBAAqB,EAC9G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,CAAa,CAAC,EAGlE,IAAM,EAA6C,SAAS,iBAA8B,sBAAsB,EAChH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,CAAa,CAAC,EAGnE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,KAAK,EAGtE,GAFA,EAAc,CAAM,EAEhB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,SAAS,MAC/C,CACL,IAAM,EAAY,OAAO,aAAa,EACtC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,YAAY,EACnC,EAAM,mBAAmB,CAAM,EAC/B,EAAU,gBAAgB,EAC1B,EAAU,SAAS,CAAK,EACxB,SAAS,YAAY,MAAM,CAC7B,CACF,CAEI,KAAK,QAAQ,aACf,KAAK,UAAY,KAAK,QAAQ,WAElC,CAAC"}
{"version":3,"file":"e2-9fzwE.min.js","names":["settings","listen","http","assertElement","engineDescriptions","loadEngineDescriptions","error","engine_name","description","source","elements","sourceText","element","toggleEngines","engineToggle","engineToggles","enable","engineElements","engineElement","enableAllEngines","engine","disableAllEngines","target","selection","range"],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../toolkit.ts\";\nimport { assertElement } from \"../util/assertElement.ts\";\n\nlet engineDescriptions: Record<string, [string, string]> | undefined;\n\nconst loadEngineDescriptions = async (): Promise<void> => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll<HTMLElement>(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (<i>${settings.translations?.Source}:&nbsp;${source}</i>)`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf<HTMLInputElement>): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf<HTMLInputElement> = document.querySelectorAll<HTMLInputElement>(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf<HTMLElement> = document.querySelectorAll<HTMLElement>(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector<HTMLPreElement>(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n if (this.dataset.copiedText) {\n this.innerText = this.dataset.copiedText;\n }\n});\n"],"mappings":"4FAKA,IAAII,EAEEC,EAAyB,SAA2B,CACxD,GAAI,GACJ,IAAI,CAEF,EAAqB,MAAM,MADTH,EAAK,MAAO,0BAA0B,EAAA,CACzB,KAAK,CACtC,OAASI,EAAO,CACd,QAAQ,MAAM,qCAAsCA,CAAK,CAC3D,CACA,GAAKF,EAEL,IAAK,GAAM,CAACG,EAAa,CAACC,EAAaC,MAAY,OAAO,QAAQL,CAAkB,EAAG,CACrF,IAAMM,EAAW,SAAS,iBAA8B,sBAAsBH,EAAY,uBAAuB,EAC3GI,EAAa,QAAQX,EAAS,cAAc,OAAO,SAASS,EAAO,OAEzE,IAAK,IAAMG,KAAWF,EACpB,EAAQ,UAAYF,EAAcG,CAEtC,CAVA,CAWF,EAEME,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAMC,KAAgBC,EAErBD,EAAa,eACf,EAAa,QAAU,CAACE,EAG9B,EAEMC,EAA0C,SAAS,iBAA8B,oBAAoB,EAC3G,IAAK,IAAMC,KAAiBD,EAC1BhB,EAAO,aAAciB,EAAeb,CAAsB,EAG5D,IAAMU,EAA8C,SAAS,iBAC3D,mDACF,EAEMI,EAA4C,SAAS,iBAA8B,qBAAqB,EAC9G,IAAK,IAAMC,KAAUD,EACnBlB,EAAO,QAASmB,MAAcP,EAAc,GAAME,CAAa,CAAC,EAGlE,IAAMM,EAA6C,SAAS,iBAA8B,sBAAsB,EAChH,IAAK,IAAMD,KAAUC,EACnBpB,EAAO,QAASmB,MAAcP,EAAc,GAAOE,CAAa,CAAC,EAGnEd,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAMqB,EAAS,KAAK,eAAe,cAA8B,KAAK,EAGtE,GAFAnB,EAAcmB,CAAM,EAEhB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAUA,EAAO,SAAS,MAC/C,CACL,IAAMC,EAAY,OAAO,aAAa,EACtC,GAAIA,EAAW,CACb,IAAMC,EAAQ,SAAS,YAAY,EACnCA,EAAM,mBAAmBF,CAAM,EAC/BC,EAAU,gBAAgB,EAC1BA,EAAU,SAASC,CAAK,EACxB,SAAS,YAAY,MAAM,CAC7B,CACF,CAEI,KAAK,QAAQ,aACf,KAAK,UAAY,KAAK,QAAQ,WAElC,CAAC"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -5
View File
@@ -41,7 +41,7 @@
]
},
"src/js/main/keyboard.ts": {
"file": "chunk/CX4Qzp80.min.js",
"file": "chunk/CGcztD0S.min.js",
"name": "keyboard",
"src": "src/js/main/keyboard.ts",
"isDynamicEntry": true,
@@ -61,7 +61,7 @@
]
},
"src/js/main/results.ts": {
"file": "chunk/DT2OB9zd.min.js",
"file": "chunk/CFQtNTi4.min.js",
"name": "results",
"src": "src/js/main/results.ts",
"isDynamicEntry": true,
@@ -71,7 +71,7 @@
]
},
"src/js/main/search.ts": {
"file": "chunk/6liDwPCz.min.js",
"file": "chunk/BMrKiJ3q.min.js",
"name": "search",
"src": "src/js/main/search.ts",
"isDynamicEntry": true,
@@ -81,7 +81,7 @@
]
},
"src/js/plugin/Calculator.ts": {
"file": "chunk/Di4V4jt_.min.js",
"file": "chunk/CGtCrbI8.min.js",
"name": "calculator",
"src": "src/js/plugin/Calculator.ts",
"isDynamicEntry": true,
@@ -103,7 +103,7 @@
]
},
"src/js/plugin/MapView.ts": {
"file": "chunk/zYDfzRRQ.min.js",
"file": "chunk/DAKwL16e.min.js",
"name": "mapview",
"src": "src/js/plugin/MapView.ts",
"isDynamicEntry": true,
+2 -2
View File
@@ -1,3 +1,3 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunk/zYDfzRRQ.min.js","./chunk/BuurKv-k.min.js","./sxng-mapview.min.css","./chunk/D3mcqWOe.min.js","./chunk/DK4yUVpy.min.js","./chunk/DcK-mo-Y.min.js","./chunk/Di4V4jt_.min.js","./chunk/CX4Qzp80.min.js","./chunk/6liDwPCz.min.js","./chunk/Bs-FMxfE.min.js","./chunk/DT2OB9zd.min.js","./chunk/e2-9fzwE.min.js"])))=>i.map(i=>d[i]);
var e={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},t={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0},n=()=>{let t=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return t&&t in e?t:e.unknown},r=()=>{let e=document.querySelector(`script[client_settings]`)?.getAttribute(`client_settings`);if(!e)return{};try{return JSON.parse(atob(e))}catch(e){return console.error(`Failed to load client_settings:`,e),{}}},i=async(e,t,n)=>{let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??3e4),a=await fetch(t,{body:n?.body,method:e,signal:r.signal}).finally(()=>clearTimeout(i));if(!a.ok)throw Error(a.statusText);return a},a=(e,t,n,r)=>{if(typeof t!=`string`){t.addEventListener(e,n,r);return}document.addEventListener(e,e=>{for(let r of e.composedPath())if(r instanceof HTMLElement&&r.matches(t)){try{n.call(r,e)}catch(e){console.error(e)}break}},r)},o=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?a(`DOMContentLoaded`,document,e,{once:!0}):e()},s=n(),c=r(),l=(e,t)=>{u(t)&&e()},u=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(s)}},d=`modulepreload`,f=function(e,t){return new URL(e,t).href},p={},m=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=f(t,n),t=s(t),t in p)return;p[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:d,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(/* @__PURE__ */ Error(`Unable to preload CSS for ${t}`)))})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};o(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),a(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),a(`click`,`.searxng_init_map`,async function(t){t.preventDefault(),this.classList.remove(`searxng_init_map`),l(()=>m(async()=>{let{default:e}=await import(`./chunk/zYDfzRRQ.min.js`);return{default:e}},__vite__mapDeps([0,1,2]),import.meta.url).then(({default:e})=>new e(this)),{on:`endpoint`,where:[e.results]})}),c.plugins?.includes(`infiniteScroll`)&&l(()=>m(async()=>{let{default:e}=await import(`./chunk/D3mcqWOe.min.js`);return{default:e}},__vite__mapDeps([3,1,4,5]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[e.results]}),c.plugins?.includes(`calculator`)&&l(()=>m(async()=>{let{default:e}=await import(`./chunk/Di4V4jt_.min.js`);return{default:e}},__vite__mapDeps([6,1,5,4]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[e.results]})}),o(()=>{m(()=>import(`./chunk/CX4Qzp80.min.js`),__vite__mapDeps([7,4]),import.meta.url),m(()=>import(`./chunk/6liDwPCz.min.js`),__vite__mapDeps([8,5,4]),import.meta.url),c.autocomplete&&m(()=>import(`./chunk/Bs-FMxfE.min.js`),__vite__mapDeps([9,4]),import.meta.url)},{on:[s===e.index]}),o(()=>{m(()=>import(`./chunk/CX4Qzp80.min.js`),__vite__mapDeps([7,4]),import.meta.url),m(()=>import(`./chunk/DT2OB9zd.min.js`),__vite__mapDeps([10,4]),import.meta.url),m(()=>import(`./chunk/6liDwPCz.min.js`),__vite__mapDeps([8,5,4]),import.meta.url),c.autocomplete&&m(()=>import(`./chunk/Bs-FMxfE.min.js`),__vite__mapDeps([9,4]),import.meta.url)},{on:[s===e.results]}),o(()=>{m(()=>import(`./chunk/e2-9fzwE.min.js`),__vite__mapDeps([11,4]),import.meta.url)},{on:[s===e.preferences]});export{c as i,a as n,t as r,i as t};
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunk/DAKwL16e.min.js","./chunk/BuurKv-k.min.js","./sxng-mapview.min.css","./chunk/D3mcqWOe.min.js","./chunk/DK4yUVpy.min.js","./chunk/DcK-mo-Y.min.js","./chunk/CGtCrbI8.min.js","./chunk/CGcztD0S.min.js","./chunk/BMrKiJ3q.min.js","./chunk/Bs-FMxfE.min.js","./chunk/CFQtNTi4.min.js","./chunk/e2-9fzwE.min.js"])))=>i.map(i=>d[i]);
var e={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},t={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0},n=()=>{let t=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return t&&t in e?t:e.unknown},r=()=>{let e=document.querySelector(`script[client_settings]`)?.getAttribute(`client_settings`);if(!e)return{};try{return JSON.parse(atob(e))}catch(e){return console.error(`Failed to load client_settings:`,e),{}}},i=async(e,t,n)=>{let r=new AbortController,i=setTimeout(()=>r.abort(),n?.timeout??3e4),a=await fetch(t,{body:n?.body,method:e,signal:r.signal}).finally(()=>clearTimeout(i));if(!a.ok)throw Error(a.statusText);return a},a=(e,t,n,r)=>{typeof t==`string`?document.addEventListener(e,e=>{for(let r of e.composedPath())if(r instanceof HTMLElement&&r.matches(t)){try{n.call(r,e)}catch(e){console.error(e)}break}},r):t.addEventListener(e,n,r)},o=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?a(`DOMContentLoaded`,document,e,{once:!0}):e()},s=n(),c=r(),l=(e,t)=>{u(t)&&e()},u=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(s)}},d=`modulepreload`,f=function(e,t){return new URL(e,t).href},p={},m=function(e){return e.pathname.endsWith(`.css`)},h=function(e,t,n){if(t in e)return e[t];let r=n();if(!r){e[t]=void 0;return}let i=r.then(()=>{e[t]=void 0},n=>{throw e[t]=void 0,n});return e[t]=i,i},g=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e,i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?new URL(import.meta.resolve(e)):new URL(e,import.meta.url)}r=o(t.map(t=>{t=f(t,n);let r=s(t),i=m(r);return h(p,r.href,()=>{if(e===void 0){e={all:new Set,styles:new Set};let t=document.getElementsByTagName(`link`);for(let n=t.length-1;n>=0;n--){let r=t[n];e.all.add(r.href),r.rel===`stylesheet`&&e.styles.add(r.href)}}if((i?e.styles:e.all).has(r.href))return;let t=document.createElement(`link`);if(t.rel=i?`stylesheet`:d,i||(t.as=`script`),t.crossOrigin=``,t.href=r.href,a&&t.setAttribute(`nonce`,a),document.head.appendChild(t),i)return new Promise((e,n)=>{t.addEventListener(`load`,e),t.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${r}`)))})})}).filter(e=>e!==void 0))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})};o(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),a(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),a(`click`,`.searxng_init_map`,async function(t){t.preventDefault(),this.classList.remove(`searxng_init_map`),l(()=>g(async()=>{let{default:e}=await import(`./chunk/DAKwL16e.min.js`);return{default:e}},__vite__mapDeps([0,1,2]),import.meta.url).then(({default:e})=>new e(this)),{on:`endpoint`,where:[e.results]})}),c.plugins?.includes(`infiniteScroll`)&&l(()=>g(async()=>{let{default:e}=await import(`./chunk/D3mcqWOe.min.js`);return{default:e}},__vite__mapDeps([3,1,4,5]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[e.results]}),c.plugins?.includes(`calculator`)&&l(()=>g(async()=>{let{default:e}=await import(`./chunk/CGtCrbI8.min.js`);return{default:e}},__vite__mapDeps([6,1,5,4]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[e.results]})}),o(()=>{g(()=>import(`./chunk/CGcztD0S.min.js`),__vite__mapDeps([7,4]),import.meta.url),g(()=>import(`./chunk/BMrKiJ3q.min.js`),__vite__mapDeps([8,5,4]),import.meta.url),c.autocomplete&&g(()=>import(`./chunk/Bs-FMxfE.min.js`),__vite__mapDeps([9,4]),import.meta.url)},{on:[s===e.index]}),o(()=>{g(()=>import(`./chunk/CGcztD0S.min.js`),__vite__mapDeps([7,4]),import.meta.url),g(()=>import(`./chunk/CFQtNTi4.min.js`),__vite__mapDeps([10,4]),import.meta.url),g(()=>import(`./chunk/BMrKiJ3q.min.js`),__vite__mapDeps([8,5,4]),import.meta.url),c.autocomplete&&g(()=>import(`./chunk/Bs-FMxfE.min.js`),__vite__mapDeps([9,4]),import.meta.url)},{on:[s===e.results]}),o(()=>{g(()=>import(`./chunk/e2-9fzwE.min.js`),__vite__mapDeps([11,4]),import.meta.url)},{on:[s===e.preferences]});export{c as i,a as n,t as r,i as t};
//# sourceMappingURL=sxng-core.min.js.map
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
+6 -6
View File
@@ -183,16 +183,16 @@
{%- if 'favicon_resolver' not in locked_preferences -%}
{%- include 'simple/preferences/favicon.html' -%}
{%- endif -%}
{%- if 'doi_resolver' not in locked_preferences -%}
{%- include 'simple/preferences/doi_resolver.html' -%}
{%- endif -%}
{% if 'safesearch' not in locked_preferences %}
{%- include 'simple/preferences/safesearch.html' -%}
{%- endif -%}
{%- include 'simple/preferences/tokens.html' -%}
{{- plugin_preferences('general') -}}
{%- if 'doi_resolver' not in locked_preferences %}
{%- include 'simple/preferences/doi_resolver.html' -%}
{%- if engines_with_tokens -%}
{%- include 'simple/preferences/tokens.html' -%}
{%- endif -%}
{{- plugin_preferences('general') -}}
{{- tab_footer() -}}
{# tab: ui #}
@@ -1,20 +1,17 @@
<div class="pref-group">{{- _('Digital Object Identifier (DOI)') -}}</div>
{{- plugin_preferences('general/doi_resolver') -}}
<fieldset>{{- '' -}}
<legend id="pref_doi_resolver">{{- _('Open Access DOI resolver') -}}</legend>{{- '' -}}
<legend id="pref_doi_resolver">{{- _('DOI Resolver') -}}</legend>{{- '' -}}
<div class="value">{{- '' -}}
<select id='doi_resolver' name='doi_resolver' aria-labelledby="pref_doi_resolver">{{- '' -}}
{%- for doi_resolver_name,doi_resolver_url in doi_resolvers.items() -%}
<select name="doi_resolver" aria-labelledby="pref_doi_resolver">{{- '' -}}
<option value=""> - </option>
{%- for doi_resolver_name in doi_resolvers -%}
<option value="{{ doi_resolver_name }}"
{%- if doi_resolver_url == current_doi_resolver %} selected="selected" {%- endif -%}>
{{- doi_resolver_name }} - {{ doi_resolver_url -}}
{%- if doi_resolver_name == current_doi_resolver %} selected="selected" {%- endif -%}>
{{- doi_resolver_name -}}
</option>
{%- endfor -%}
</select>{{- '' -}}
</div>{{- '' -}}
<div class="description">
{{- _('Select service used by DOI rewrite') -}}
{{- _('Avoid paywalls by redirecting to open-access versions of publications when available') -}}
</div>{{- '' -}}
</fieldset>{{- '' -}}
Binary file not shown.
+119 -25
View File
@@ -23,9 +23,9 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"PO-Revision-Date: 2026-06-19 05:08+0000\n"
"Last-Translator: Raithlin <raithlin@noreply.codeberg.org>\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-10-09 14:20+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: af\n"
"Language-Team: Afrikaans "
"<https://translate.codeberg.org/projects/searxng/searxng/af/>\n"
@@ -53,7 +53,7 @@ msgstr "toeps"
#. CATEGORY_NAMES['BLOGS']
#: searx/searxng.msg
msgid "blogs"
msgstr ""
msgstr "webjoernaal"
#. CATEGORY_NAMES['DICTIONARIES']
#: searx/searxng.msg
@@ -73,7 +73,7 @@ msgstr "algemeen"
#. CATEGORY_NAMES['ICONS']
#: searx/searxng.msg
msgid "icons"
msgstr ""
msgstr "ikone"
#. CATEGORY_NAMES['IMAGES']
#: searx/searxng.msg
@@ -98,7 +98,7 @@ msgstr "kaart"
#. CATEGORY_NAMES['MOVIES']
#: searx/searxng.msg
msgid "movies"
msgstr ""
msgstr "flieks"
#. CATEGORY_NAMES['MUSIC']
#: searx/searxng.msg
@@ -158,12 +158,12 @@ msgstr "sagteware wiki's"
#. CATEGORY_NAMES['STOCK_IMAGES']
#: searx/searxng.msg
msgid "stock images"
msgstr ""
msgstr "voorraadbeelde"
#. CATEGORY_NAMES['TRANSLATE']
#: searx/searxng.msg
msgid "translate"
msgstr ""
msgstr "vertaal"
#. CATEGORY_NAMES['TV']
#: searx/searxng.msg
@@ -178,7 +178,7 @@ msgstr "videos"
#. CATEGORY_NAMES['WEATHER']
#: searx/searxng.msg
msgid "weather"
msgstr ""
msgstr "weer"
#. CATEGORY_NAMES['WEB']
#: searx/searxng.msg
@@ -589,15 +589,107 @@ msgstr "Bron"
msgid "Error loading the next page"
msgstr "Fout met die laai van die volgende bladsy"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "Ongeldige opstellings, redigeer asb jou voorkeure"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "Ongeldige opstellings"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "soekfout"
@@ -808,11 +900,12 @@ msgstr ""
"Laai die volgende bladsy outomaties wanneer na die onderkant van die "
"huidige bladsy geblaai word"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "oop toegang DOI oorskryf"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1433,17 +1526,9 @@ msgstr "Voeg gekopieerde voorkeur-hash (sonder URL) in om te herstel"
msgid "Preferences hash"
msgstr "Voorkeure hash"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "Digitale Objekidentifiseerder (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Ooptoegang DOI-oplosser"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "Kies diens wat deur DOI herskryf gebruik word"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2196,3 +2281,12 @@ msgstr "versteek video"
#~ msgid "Engine"
#~ msgstr "Enjin"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "Digitale Objekidentifiseerder (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "Ooptoegang DOI-oplosser"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "Kies diens wat deur DOI herskryf gebruik word"
Binary file not shown.
+111 -17
View File
@@ -33,7 +33,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-06-28 18:07+0000\n"
"Last-Translator: nebras <nebras@noreply.codeberg.org>\n"
"Language: ar\n"
@@ -600,15 +600,107 @@ msgstr "مصدر"
msgid "Error loading the next page"
msgstr "خطأ في تحميل الصفحة التالية"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "إعدادات غير صالحة، يرجى تعديل تفضيلاتك"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "إعدادات غير صالحة"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "خطأ في البحث"
@@ -815,11 +907,12 @@ msgid ""
" page"
msgstr "يحمل الصفحة التالية تلقائيًا عند التمرير إلى أسفل الصفحة الحالية"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "فتح الوصول معرف الكائن الرقمي إعادة كتابة"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1441,17 +1534,9 @@ msgstr "أدخل تجزئة التفضيلات المنسوخة (بدون عنو
msgid "Preferences hash"
msgstr "تجزئة التفضيلات"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "معرف الكائن الرقمي (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "فتح الوصول إلى محلل DOI"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "حدد الخدمة التي يستخدمه المعرف الرقمي. (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2434,3 +2519,12 @@ msgstr "إخفاء الفيديو"
#~ msgid "Engine"
#~ msgstr "محرك"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "معرف الكائن الرقمي (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "فتح الوصول إلى محلل DOI"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "حدد الخدمة التي يستخدمه المعرف الرقمي. (DOI)"
Binary file not shown.
+112 -18
View File
@@ -23,7 +23,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-08-21 20:54+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: bg\n"
@@ -589,15 +589,107 @@ msgstr "Източник"
msgid "Error loading the next page"
msgstr "Грешка при зареждането на следващата страница"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "Неправилни настройки, моля редактирайте предпочитанията си"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "Невалидни настройки"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "Грешка при търсенето"
@@ -804,11 +896,12 @@ msgid ""
" page"
msgstr "Автоматично зареждане на следващата страница"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "Отворен достъп DOI пренаписване"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1429,19 +1522,9 @@ msgstr "Вмъкнете копирания хеш на предпочитани
msgid "Preferences hash"
msgstr "Хеш на предпочитанията"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "Идентификатор на цифров обект (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Дигитален идентификатор на обекти с отворен достъп"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
"Изберете услуга използвана от \"Идентификатор на дигитален обект“ (DOI) "
"пренаписване"
#: searx/templates/simple/preferences/engines.html:9
#, fuzzy
@@ -2434,3 +2517,14 @@ msgstr "скрий видеото"
#~ msgid "Engine"
#~ msgstr "Търсачка"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "Идентификатор на цифров обект (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "Дигитален идентификатор на обекти с отворен достъп"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr ""
#~ "Изберете услуга използвана от \"Идентификатор"
#~ " на дигитален обект“ (DOI) пренаписване"
Binary file not shown.
+111 -17
View File
@@ -29,7 +29,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-05-19 12:07+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: bn\n"
@@ -595,15 +595,107 @@ msgstr "উৎস"
msgid "Error loading the next page"
msgstr "পরবর্তী পৃষ্ঠাটি লোড করায় ত্রুটি দেখা যাচ্ছে"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "অকেজো সেটিংস, অনুগ্রহ করে আপনার পছন্দগুলি সম্পাদনা করুন"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "অকেজো সেটিংস"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "সার্চ ত্রুটি"
@@ -810,11 +902,12 @@ msgstr ""
"বর্তমান পৃষ্ঠার নীচে স্ক্রোল করার সময় স্বয়ংক্রিয়ভাবে পরবর্তী পৃষ্ঠা "
"লোড করুন"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "পুনর্লিখিত DOI উন্মুক্ত প্রবেশ"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1429,17 +1522,9 @@ msgstr "অনুলিপিকৃত হ্যাশ এখানে দাও
msgid "Preferences hash"
msgstr "পছন্দসমূহের হ্যাশ"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "ডিজিটাল অবজেক্ট আইডেন্টিফায়ার (ডই)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "উন্মুক্ত প্রবেশাধিকারযুক্ত DOI সমাধানদাতা"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "পুনর্লিখিত DOI দ্বারা ব্যবহৃত সার্ভিস নির্বাচিত করুণ"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2192,3 +2277,12 @@ msgstr "ভিডিও লুকিয়ে ফেলুন"
#~ msgid "Engine"
#~ msgstr "ইঞ্জিন"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "ডিজিটাল অবজেক্ট আইডেন্টিফায়ার (ডই)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "উন্মুক্ত প্রবেশাধিকারযুক্ত DOI সমাধানদাতা"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "পুনর্লিখিত DOI দ্বারা ব্যবহৃত সার্ভিস নির্বাচিত করুণ"
Binary file not shown.
+110 -16
View File
@@ -13,7 +13,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-05-19 12:07+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: bo\n"
@@ -579,15 +579,107 @@ msgstr ""
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།ཁྱེད་ཀྱིས་གདམ་ཀ་ལ་བཅོས་སྒྲིག་གཏོང་རོགས།"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "འཚོལ་བཤེར་ལ་ནོར་འཁྲུལ་བྱུང་།"
@@ -784,11 +876,12 @@ msgid ""
" page"
msgstr "དྲ་ངོས་མར་འདྲུད་ནས་ཞབས་ལ་སླེབས་དུས་རང་འགུལ་སྒོས་འཕྲོ་མའི་ནང་དོན་འཆར་པར་བྱེད།"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr ""
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1394,16 +1487,8 @@ msgstr ""
msgid "Preferences hash"
msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
@@ -2325,3 +2410,12 @@ msgstr "རྙན་ཟློས་སྦས།"
#~ msgid "Engine"
#~ msgstr ""
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr ""
#~ msgid "Open Access DOI resolver"
#~ msgstr ""
#~ msgid "Select service used by DOI rewrite"
#~ msgstr ""
Binary file not shown.
+111 -17
View File
@@ -31,7 +31,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-09-19 15:25+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: ca\n"
@@ -597,15 +597,107 @@ msgstr "Origen"
msgid "Error loading the next page"
msgstr "S'ha produït un error en carregar la següent pàgina"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "La configuració no és vàlida, editeu-la"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "La configuració no és vàlida"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "error de cerca"
@@ -811,11 +903,12 @@ msgstr ""
"Carrega automàticament la pàgina següent en desplaçar-se al final de la "
"pàgina actual"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "Reescriptura de l'Open Access DOI"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1433,17 +1526,9 @@ msgstr "Inserta el hash de preferències copiat (sense URL) per restaurar"
msgid "Preferences hash"
msgstr "Hash de preferències"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "Identificador d'objecte digital (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Resolució del DOI"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "Selecciona el servei utilitzat per a la reescriptura del DOI"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2454,3 +2539,12 @@ msgstr "oculta el vídeo"
#~ msgid "Engine"
#~ msgstr "Cercador"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "Identificador d'objecte digital (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "Resolució del DOI"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "Selecciona el servei utilitzat per a la reescriptura del DOI"
Binary file not shown.
+113 -19
View File
@@ -27,8 +27,8 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"PO-Revision-Date: 2026-09-22 13:25+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-10-10 22:27+0000\n"
"Last-Translator: Fjuro <fjuro@noreply.codeberg.org>\n"
"Language: cs\n"
"Language-Team: Czech <https://translate.codeberg.org/projects/searxng/"
@@ -39,7 +39,7 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.18.0\n"
"X-Generator: Weblate 2026.9.1\n"
"X-Generator: Weblate 2026.10.1\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
#: searx/searxng.msg
@@ -595,15 +595,107 @@ msgstr "zdroj"
msgid "Error loading the next page"
msgstr "Chyba při načítání další stránky"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr "zavřít"
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr "Jak se pohybovat v SearXNG pomocí klávesových zkratek"
#: searx/webapp.py:334
msgid "Control"
msgstr "Ovládání"
#: searx/webapp.py:335
msgid "Results"
msgstr "Výsledky"
#: searx/webapp.py:336
msgid "Navigation"
msgstr "Navigace"
#: searx/webapp.py:337
msgid "Other"
msgstr "Ostatní"
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr "odebrat zaměření ze zaměřeného vstupu"
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr "zkopírovat adresu zvoleného výsledku do schránky"
#: searx/webapp.py:340
msgid "toggle help window"
msgstr "přepnout okno nápovědy"
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr "zaměřit na vyhledávací pole"
#: searx/webapp.py:342
msgid "go to next page"
msgstr "přejít na další stránku"
#: searx/webapp.py:343
msgid "open search result"
msgstr "otevřít výsledek vyhledávání"
#: searx/webapp.py:344
msgid "go to previous page"
msgstr "přejít na předchozí stránku"
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr "znovu načíst stránku ze serveru"
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr "otevřít výsledek v novém panelu"
#: searx/webapp.py:347
msgid "select previous search result"
msgstr "zvolit předchozí výsledek vyhledávání"
#: searx/webapp.py:348
msgid "select next search result"
msgstr "zvolit další výsledek vyhledávání"
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr "posunout o stránku nahoru"
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr "posunout o půl stránky dolů"
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr "posunout o stránku dolů"
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr "posunout na vršek stránky"
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr "posunout o půl stránky nahoru"
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr "posunout na spodek stránky"
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "Neplatné nastavení, upravte své předvolby"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "Neplatné nastavení"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "chyba vyhledávání"
@@ -811,11 +903,12 @@ msgid ""
" page"
msgstr "Automaticky načte další stránku při posunutí na konec aktuální stránky"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "Přesměrování na Open Access DOI"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1436,17 +1529,9 @@ msgstr "Pro obnovení vložte zkopírovaný hash předvoleb (bez adresy URL)"
msgid "Preferences hash"
msgstr "Hash předvoleb"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "Digital Object Identifier (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Web pro přesměrování na Open Access DOI"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "Výběr služby použité při přepisu DOI"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr "Resolver DOI"
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2440,3 +2525,12 @@ msgstr "skrýt video"
#~ msgid "Engine"
#~ msgstr "Vyhledávač"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "Digital Object Identifier (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "Web pro přesměrování na Open Access DOI"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "Výběr služby použité při přepisu DOI"
Binary file not shown.
+110 -16
View File
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-05-19 12:07+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language: cy\n"
@@ -587,15 +587,107 @@ msgstr "Ffynhonnell"
msgid "Error loading the next page"
msgstr "Gwall wrth lwytho'r dudalen nesaf"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "Gosodiadau annilys, golygwch eich dewisiadau"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "Gosodiadau annilys"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "gwall chwilio"
@@ -802,11 +894,12 @@ msgstr ""
"Llwytho'r dudalen nesaf yn awtomatig wrth gyrraedd gwaelod y dudalen "
"bresennol"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "Disodli DOI Open Access"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1422,18 +1515,10 @@ msgstr "Rhowch hash dewisiadau yma (heb URL) i'w adfer"
msgid "Preferences hash"
msgstr "Hash dewisiadau"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Atebydd DOI Open Access"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "Dewis y gwasanaeth i'r disodlydd DOI ddefnyddio"
#: searx/templates/simple/preferences/engines.html:9
msgid ""
"This tab does not exist in the user interface, but you can search with "
@@ -2393,3 +2478,12 @@ msgstr "cuddio'r fideo"
#~ msgid "Engine"
#~ msgstr "Peiriant"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr ""
#~ msgid "Open Access DOI resolver"
#~ msgstr "Atebydd DOI Open Access"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "Dewis y gwasanaeth i'r disodlydd DOI ddefnyddio"
Binary file not shown.
+111 -17
View File
@@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2026-09-21 12:54+0000\n"
"POT-Creation-Date: 2026-10-09 19:03+0000\n"
"PO-Revision-Date: 2026-06-16 07:07+0000\n"
"Last-Translator: AndersNordh <andersnordh@noreply.codeberg.org>\n"
"Language: da\n"
@@ -585,15 +585,107 @@ msgstr "Kilde"
msgid "Error loading the next page"
msgstr "Fejl ved indlæsning af den næste side"
#: searx/webapp.py:478 searx/webapp.py:878
#: searx/webapp.py:332
msgid "close"
msgstr ""
#: searx/webapp.py:333
msgid "How to navigate SearXNG with hotkeys"
msgstr ""
#: searx/webapp.py:334
msgid "Control"
msgstr ""
#: searx/webapp.py:335
msgid "Results"
msgstr ""
#: searx/webapp.py:336
msgid "Navigation"
msgstr ""
#: searx/webapp.py:337
msgid "Other"
msgstr ""
#: searx/webapp.py:338
msgid "remove focus from the focused input"
msgstr ""
#: searx/webapp.py:339
msgid "copy url of the selected result to the clipboard"
msgstr ""
#: searx/webapp.py:340
msgid "toggle help window"
msgstr ""
#: searx/webapp.py:341
msgid "focus on the search input"
msgstr ""
#: searx/webapp.py:342
msgid "go to next page"
msgstr ""
#: searx/webapp.py:343
msgid "open search result"
msgstr ""
#: searx/webapp.py:344
msgid "go to previous page"
msgstr ""
#: searx/webapp.py:345
msgid "reload page from the server"
msgstr ""
#: searx/webapp.py:346
msgid "open the result in a new tab"
msgstr ""
#: searx/webapp.py:347
msgid "select previous search result"
msgstr ""
#: searx/webapp.py:348
msgid "select next search result"
msgstr ""
#: searx/webapp.py:349
msgid "scroll one page up"
msgstr ""
#: searx/webapp.py:350
msgid "scroll half a page down"
msgstr ""
#: searx/webapp.py:351
msgid "scroll one page down"
msgstr ""
#: searx/webapp.py:352
msgid "scroll to the top of the page"
msgstr ""
#: searx/webapp.py:353
msgid "scroll half a page up"
msgstr ""
#: searx/webapp.py:354
msgid "scroll to the bottom of the page"
msgstr ""
#: searx/webapp.py:502 searx/webapp.py:902
msgid "Invalid settings, please edit your preferences"
msgstr "Ugyldige indstillinger, redigér venligst dine valg"
#: searx/webapp.py:494
#: searx/webapp.py:518
msgid "Invalid settings"
msgstr "Ugyldig indstilling"
#: searx/webapp.py:570 searx/webapp.py:660
#: searx/webapp.py:594 searx/webapp.py:684
msgid "search error"
msgstr "søgefejl"
@@ -804,11 +896,12 @@ msgstr ""
"ndlæser automatisk næste side, når du scroller til bunden af den aktuelle"
" side"
#: searx/plugins/oa_doi_rewrite.py:54
#: searx/plugins/oa_doi_rewrite.py:55
msgid "Open Access DOI rewrite"
msgstr "Open Access DOI-omskrivning"
#: searx/plugins/oa_doi_rewrite.py:55
#: searx/plugins/oa_doi_rewrite.py:56
#: searx/templates/simple/preferences/doi_resolver.html:15
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications "
"when available"
@@ -1430,17 +1523,9 @@ msgstr "Indsæt kopieret indstillinger-hash (uden URL) for at gendanne"
msgid "Preferences hash"
msgstr "Præference hash"
#: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)"
msgstr "Digital Object Identifier (DOI)"
#: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver"
msgstr "Open Access DOI-forløser"
#: searx/templates/simple/preferences/doi_resolver.html:18
msgid "Select service used by DOI rewrite"
msgstr "Vælg service brugt af DOI-omskrivning"
#: searx/templates/simple/preferences/doi_resolver.html:2
msgid "DOI Resolver"
msgstr ""
#: searx/templates/simple/preferences/engines.html:9
msgid ""
@@ -2449,3 +2534,12 @@ msgstr "skjul video"
#~ msgid "Engine"
#~ msgstr "Maskine"
#~ msgid "Digital Object Identifier (DOI)"
#~ msgstr "Digital Object Identifier (DOI)"
#~ msgid "Open Access DOI resolver"
#~ msgstr "Open Access DOI-forløser"
#~ msgid "Select service used by DOI rewrite"
#~ msgstr "Vælg service brugt af DOI-omskrivning"

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