19 Commits

Author SHA1 Message Date
dependabot[bot]
81c9c23862 [upd] github-actions: Bump actions/setup-node from 6.4.0 to 7.0.0 (#6423)
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](48b55a011b...8207627860)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.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-07-17 14:27:38 +02:00
dependabot[bot]
6913fba208 [upd] pypi: Bump the minor group with 2 updates (#6425)
Bumps the minor group with 2 updates: [typer](https://github.com/fastapi/typer) and [selenium](https://github.com/SeleniumHQ/Selenium).


Updates `typer` from 0.26.8 to 0.27.0
- [Release notes](https://github.com/fastapi/typer/releases)
- [Changelog](https://github.com/fastapi/typer/blob/master/docs/release-notes.md)
- [Commits](https://github.com/fastapi/typer/compare/0.26.8...0.27.0)

Updates `selenium` from 4.45.0 to 4.46.0
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/compare/selenium-4.45.0...selenium-4.46.0)
2026-07-17 14:24:17 +02:00
Bnyro
2daa4d4815 [fix] qwant: can't fetch engine traits because engine.about is no longer a dict 2026-07-17 12:27:46 +02:00
Bnyro
de8f73f434 [fix] cache: maintenance period comment duration 2h instead of 1h 2026-07-17 12:24:47 +02:00
Bnyro
9f9c00819e [fix] tiger: remember auth cookie for 2 months, not 1 day 2026-07-16 23:31:59 +02:00
Bnyro
b72a87676f [fix] public domain image archive: crashes upon response
The code used here has always been "bad" because `about` shouldn't be used
as data source, but the engine probably broke when type checks / dataclasses
for the about parameter in engines has been added with
<https://github.com/searxng/searxng/pull/6258>.

Error log:
```
WARNING searx.engines.public domain im: ErrorContext('searx/engines/public_domain_image_archive.py', 143, '\'url\': _clean_url(f"{about[\'website\']}/images/{result[\'objectID\']}"),', 'TypeError', None, ("'EngineAbout' object is not subscriptable",)) False
ERROR   searx.engines.public domain im: exception : 'EngineAbout' object is not subscriptable
Traceback (most recent call last):
  File "/home/bnyro/Projects/searxng/searx/search/processors/online.py", line 253, in search
    search_results = self._search_basic(query, params)
  File "/home/bnyro/Projects/searxng/searx/search/processors/online.py", line 239, in _search_basic
    return self.engine.response(response)
           ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/home/bnyro/Projects/searxng/searx/engines/public_domain_image_archive.py", line 143, in response
    'url': _clean_url(f"{about['website']}/images/{result['objectID']}"),
                         ~~~~~^^^^^^^^^^^
TypeError: 'EngineAbout' object is not subscriptable
```
2026-07-16 15:50:58 +02:00
Bnyro
f2432e33d6 [fix] engine cache: return default value when cache entry expired
It's possible that it's expired but has not yet been automatically deleted
by the periodic maintenance because that, by default, runs only every 2 hours.

Related:
- see https://github.com/searxng/searxng/pull/6409#discussion_r3584712601
2026-07-16 12:37:26 +02:00
vojkovic
7b2199ecdf [fix] autocomplete suggestrelevance scores for chromium 2026-07-15 23:45:25 +08:00
Bnyro
4a9c19d7bf [mod] images.html: display all engines that found the result, not only the first one 2026-07-15 17:44:11 +02:00
Bnyro
5cb4cb2bc5 [fix] qwant: engine blocked with captcha
Qwant now requires a `datadome` cookie that it returns
in the first search response as a `Set-Cookie`.

This cookie has to be sent for all requests, otherwise they
will be blocked.

This means that now, the first search request is blocked (results in CAPTCHA),
and only the subsequent searches work (same happens on the Qwant website for me).

However, I don't think it's worth repeating the same
search request multiple times very quickly because
that also makes us more suspicious.
2026-07-15 17:39:10 +02:00
Bnyro
5a448596ab [feat] engines: add avalw.org (general)
The index is very small, so it often doesn't find anything.

It also has news support, but the results are unusable.
2026-07-15 17:38:46 +02:00
Bnyro
9c49b7e0d7 [fix] results: display internationalized domain names human-friendly
Some search engines encode domains that contain special characters
in the IDN format, i.e. by using Punycode (https://en.wikipedia.org/wiki/Punycode).

This means that domains formatted like that are not human readable
and thus very unintuive.

Also, as only some engines use Punycode, this often causes a result
to appear multiple times, once with a Punycode domain (e.g. xn--allestrungen-9ib.de),
and once with a normal domain (e.g. allestörungen.de).

Always formatting the domains nicely has the caveat that the official sites
are harder to distinguish from malicious clones that just swapped out some
characters by using Punycode, e.g. replaced `a` with `á`. I don't think
that will be much of an issue though - I don't think Punycode results
previously stopped users from clicking the link, and I also think that
most search engines filter out such bad results or don't even have them
indexed.
2026-07-15 17:38:04 +02:00
Bnyro
58e02a01ae [fix] tusksearch: fix engine blocked by bot protection
Changes:
- the `embed.js` request now requires a user agent header
- we include a user agent in the "actual" request (I dropped it by accident)
- we only send the first 4 decimal places of the location
  instead of 7+ (not required, but harder to detect)
2026-07-14 20:32:31 +02:00
Bnyro
7fa9f16225 [fix] heexy: blocked by cloudflare 2026-07-14 20:32:18 +02:00
Onev
9e25585aec [refactor] engines: use datetime.fromisoformat instead of datetime.strptime where possible (#6394)
Refactor engines that parse ISO 8601 dates with strptime to use
fromisoformat instead. In most cases this is a direct replacement of
strptime(text, "format") with fromisoformat(text).

For engines where the source has a trailing "Z" that strptime consumed
as a literal (e.g. "%Y-%m-%dT%H:%M:%S.%fZ" in huggingface.py), add
rstrip("Z") to keep the output naive and preserve the existing behavior.

In sogou.py the date is extracted with a regular expression, which can
yield strings like "2026-7-11". strptime accepts this via its format
string, but fromisoformat does not. To preserve the existing behavior
and satisfy the format fromisoformat expects, add zero-padding for the
month and day.

Closes: #6098
---------
Signed-off-by: OneVth <onebrotravel@gmail.com>
2026-07-13 17:46:56 +02:00
Ivan Gabaldon
c19d86faa3 [mod] ci: remove security (#6400)
Manage alerts directly from Docker Hub
2026-07-12 13:09:19 +02:00
Ivan Gabaldon
21fa7b0be1 [mod] ci: rework workflows (#6401)
* [mod] ci: rework workflows

mod: ubuntu 26.04 runners
mod: remove podman-static
mod: longer container cache
mod: remove useless pr information
mod: sort steps
fix: use commit ref on workflow_run children
fix: missing container workflow_run check
fix: nodejs cache

* [fix] ci: apply suggestions

https://github.com/searxng/searxng/pull/6401#discussion_r3566147961
https://github.com/searxng/searxng/pull/6401#discussion_r3566147970
2026-07-12 13:04:11 +02:00
dependabot[bot]
74b4e7c8d1 [upd] web-client (simple): Bump the minor group across 1 directory with 5 updates (#6399)
Bumps the minor group with 5 updates in the /client/simple directory:

| Package | From | To |
| --- | --- | --- |
| [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) | `2.5.2` | `2.5.3` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.1.0` | `26.1.1` |
| [browserslist](https://github.com/browserslist/browserslist) | `4.28.4` | `4.28.6` |
| [svgo](https://github.com/svg/svgo) | `4.0.1` | `4.0.2` |
| [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.1.3` | `8.1.4` |

Updates `@biomejs/biome` from 2.5.2 to 2.5.3
- [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.3/packages/@biomejs/biome)

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

Updates `browserslist` from 4.28.4 to 4.28.6
- [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.28.4...4.28.6)

Updates `svgo` from 4.0.1 to 4.0.2
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](https://github.com/svg/svgo/compare/v4.0.1...v4.0.2)

Updates `vite` from 8.1.3 to 8.1.4
- [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.1.4/packages/vite)

---
updated-dependencies:
- dependency-name: "@biomejs/biome"
  dependency-version: 2.5.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: browserslist
  dependency-version: 4.28.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: svgo
  dependency-version: 4.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor
- dependency-name: vite
  dependency-version: 8.1.4
  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-07-12 08:56:35 +02:00
dependabot[bot]
39f4dd24a5 [upd] web-client (simple): Bump typescript in /client/simple (#6391)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 6.0.3 to 7.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  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-07-12 08:44:34 +02:00
48 changed files with 644 additions and 310 deletions

View File

@@ -25,25 +25,21 @@ env:
jobs: jobs:
build: build:
if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch' if: |
github.event_name == 'workflow_dispatch'
|| (github.repository_owner == 'searxng' && github.event.workflow_run.conclusion == 'success')
name: Build (${{ matrix.arch }}) name: Build (${{ matrix.arch }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.runner }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- arch: amd64 - runner: ubuntu-26.04
march: amd64 arch: amd64
os: ubuntu-24.04 - runner: ubuntu-26.04-arm
emulation: false arch: arm64
- arch: arm64 - runner: ubuntu-26.04-arm
march: arm64 arch: armv7
os: ubuntu-24.04-arm
emulation: false
- arch: armv7
march: arm64
os: ubuntu-24.04-arm
emulation: true
permissions: permissions:
packages: write packages: write
@@ -53,33 +49,25 @@ jobs:
git_url: ${{ steps.build.outputs.git_url }} git_url: ${{ steps.build.outputs.git_url }}
steps: steps:
# yamllint disable rule:line-length - name: Login to GHCR
- name: Setup podman uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
env: with:
PODMAN_VERSION: "v5.7.1" registry: "ghcr.io"
run: | username: "${{ github.repository_owner }}"
sudo apt-get purge -y podman runc crun conmon password: "${{ secrets.GITHUB_TOKEN }}"
curl -fsSLO "https://github.com/mgoltzsche/podman-static/releases/download/${{ env.PODMAN_VERSION }}/podman-linux-${{ matrix.march }}.tar.gz"
curl -fsSLO "https://github.com/mgoltzsche/podman-static/releases/download/${{ env.PODMAN_VERSION }}/podman-linux-${{ matrix.march }}.tar.gz.asc"
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 0CCF102C4F95D89E583FF1D4F8B5AF50344BB503
gpg --batch --verify "podman-linux-${{ matrix.march }}.tar.gz.asc" "podman-linux-${{ matrix.march }}.tar.gz"
tar -xzf "podman-linux-${{ matrix.march }}.tar.gz"
sudo cp -rfv ./podman-linux-${{ matrix.march }}/etc/. /etc/
sudo cp -rfv ./podman-linux-${{ matrix.march }}/usr/. /usr/
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# yamllint enable rule:line-length
- name: Setup Python - name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with: with:
python-version: "${{ env.PYTHON_VERSION }}" python-version: "${{ env.PYTHON_VERSION }}"
- name: Setup QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Checkout - name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: "${{ github.event.workflow_run.head_sha || github.sha }}"
persist-credentials: "false" persist-credentials: "false"
fetch-depth: "0" fetch-depth: "0"
@@ -91,64 +79,36 @@ jobs:
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}- python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
path: "./local/" path: "./local/"
- name: Get date
id: date
run: echo "date=$(date +'%Y%m%d')" >>$GITHUB_OUTPUT
- name: Setup cache container - name: Setup cache container
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
key: "container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-${{ hashFiles('./requirements*.txt') }}" key: "container-${{ matrix.arch }}-${{ hashFiles('./requirements*.txt') }}"
restore-keys: | restore-keys: |
container-${{ matrix.arch }}-${{ steps.date.outputs.date }}-
container-${{ matrix.arch }}- container-${{ matrix.arch }}-
path: "/var/tmp/buildah-cache-*/*" path: "/var/tmp/buildah-cache-*/*"
- if: ${{ matrix.emulation }}
name: Setup QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Login to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: "ghcr.io"
username: "${{ github.repository_owner }}"
password: "${{ secrets.GITHUB_TOKEN }}"
- name: Build - name: Build
id: build id: build
env: env:
OVERRIDE_ARCH: "${{ matrix.arch }}" OVERRIDE_ARCH: "${{ matrix.arch }}"
run: make podman.build run: make container.build
test: test:
name: Test (${{ matrix.arch }}) name: Test (${{ matrix.arch }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.runner }}
needs: build needs: build
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- arch: amd64 - runner: ubuntu-26.04
os: ubuntu-24.04 arch: amd64
emulation: false - runner: ubuntu-26.04-arm
- arch: arm64 arch: arm64
os: ubuntu-24.04-arm - runner: ubuntu-26.04-arm
emulation: false arch: armv7
- arch: armv7
os: ubuntu-24.04-arm
emulation: true
steps: steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: "false"
- if: ${{ matrix.emulation }}
name: Setup QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with: with:
@@ -156,6 +116,15 @@ jobs:
username: "${{ github.repository_owner }}" username: "${{ github.repository_owner }}"
password: "${{ secrets.GITHUB_TOKEN }}" password: "${{ secrets.GITHUB_TOKEN }}"
- name: Setup QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: "${{ github.event.workflow_run.head_sha || github.sha }}"
persist-credentials: "false"
- name: Test - name: Test
env: env:
OVERRIDE_ARCH: "${{ matrix.arch }}" OVERRIDE_ARCH: "${{ matrix.arch }}"
@@ -165,7 +134,7 @@ jobs:
release: release:
if: github.repository_owner == 'searxng' && github.ref_name == 'master' if: github.repository_owner == 'searxng' && github.ref_name == 'master'
name: Release name: Release
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
needs: needs:
- build - build
- test - test
@@ -174,10 +143,12 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout - name: Login to Docker Hub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with: with:
persist-credentials: "false" registry: "docker.io"
username: "${{ secrets.DOCKER_USER }}"
password: "${{ secrets.DOCKER_TOKEN }}"
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
@@ -186,12 +157,11 @@ jobs:
username: "${{ github.repository_owner }}" username: "${{ github.repository_owner }}"
password: "${{ secrets.GITHUB_TOKEN }}" password: "${{ secrets.GITHUB_TOKEN }}"
- name: Login to Docker Hub - name: Checkout
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
registry: "docker.io" ref: "${{ github.event.workflow_run.head_sha || github.sha }}"
username: "${{ secrets.DOCKER_USER }}" persist-credentials: "false"
password: "${{ secrets.DOCKER_TOKEN }}"
- name: Release - name: Release
env: env:

View File

@@ -21,7 +21,7 @@ jobs:
data: data:
if: github.repository_owner == 'searxng' if: github.repository_owner == 'searxng'
name: ${{ matrix.fetch }} name: ${{ matrix.fetch }}
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -64,23 +64,17 @@ jobs:
run: V=1 ./manage pyenv.cmd python "./searxng_extra/update/${{ matrix.fetch }}" run: V=1 ./manage pyenv.cmd python "./searxng_extra/update/${{ matrix.fetch }}"
- name: Create PR - name: Create PR
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with: with:
author: "searxng-bot <searxng-bot@users.noreply.github.com>" author: "searxng-bot <searxng-bot@users.noreply.github.com>"
committer: "searxng-bot <searxng-bot@users.noreply.github.com>" committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
title: "[data] update searx.data - ${{ matrix.fetch }}" title: "[mod] data: update searx.data - ${{ matrix.fetch }}"
commit-message: "[data] update searx.data - ${{ matrix.fetch }}" commit-message: "[mod] data: update searx.data - ${{ matrix.fetch }}"
branch: "update_data_${{ matrix.fetch }}" branch: "ci-data-${{ matrix.fetch }}"
delete-branch: "true" delete-branch: "true"
draft: "false" draft: "false"
signoff: "false" signoff: "false"
body: | body: |
[data] update searx.data - ${{ matrix.fetch }} Update searx.data - ${{ matrix.fetch }}
labels: | labels: |
data data
- name: Display information
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"

View File

@@ -25,7 +25,7 @@ jobs:
release: release:
if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch' if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch'
name: Release name: Release
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
permissions: permissions:
# for JamesIves/github-pages-deploy-action to push # for JamesIves/github-pages-deploy-action to push
contents: write contents: write
@@ -65,7 +65,7 @@ jobs:
with: with:
folder: "dist/docs" folder: "dist/docs"
branch: "gh-pages" branch: "gh-pages"
commit-message: "[doc] build from commit ${{ github.sha }}" commit-message: "[mod] docs: build from commit ${{ github.sha }}"
# Automatically remove deleted files from the deploy branch # Automatically remove deleted files from the deploy branch
clean: "true" clean: "true"
single-commit: "true" single-commit: "true"

View File

@@ -23,7 +23,7 @@ env:
jobs: jobs:
test: test:
name: Python ${{ matrix.python-version }} name: Python ${{ matrix.python-version }}
runs-on: ubuntu-24.04 runs-on: ubuntu-26.04
strategy: strategy:
matrix: matrix:
python-version: python-version:
@@ -59,29 +59,24 @@ jobs:
theme: theme:
name: Theme name: Theme
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
steps: steps:
- name: Setup Python - name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with: with:
python-version: "${{ env.PYTHON_VERSION }}" python-version: "${{ env.PYTHON_VERSION }}"
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "26"
check-latest: "true"
- name: Checkout - name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: "false" persist-credentials: "false"
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: "./.nvmrc"
- name: Setup cache Node.js
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: "nodejs-${{ runner.arch }}-${{ hashFiles('./.nvmrc', './package.json') }}"
path: "./client/simple/node_modules/"
- name: Setup cache Python - name: Setup cache Python
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with: with:
@@ -90,6 +85,14 @@ jobs:
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}- python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
path: "./local/" path: "./local/"
- name: Setup cache Node.js
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
key: "nodejs-${{ runner.arch }}-${{ hashFiles('**/package-lock.json') }}"
restore-keys: |
nodejs-${{ runner.arch }}-
path: "./client/simple/node_modules/"
- name: Setup venv - name: Setup venv
run: make V=1 install run: make V=1 install

View File

@@ -26,9 +26,9 @@ env:
jobs: jobs:
update: update:
if: github.repository_owner == 'searxng' && github.event.workflow_run.conclusion == 'success' if: github.event.workflow_run.conclusion == 'success' && github.repository_owner == 'searxng'
name: Update name: Update
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
permissions: permissions:
# For "make V=1 weblate.push.translations" # For "make V=1 weblate.push.translations"
contents: write contents: write
@@ -59,7 +59,7 @@ jobs:
- name: Setup Weblate - name: Setup Weblate
run: | run: |
mkdir -p ~/.config mkdir -p ~/.config
echo "${{ secrets.WEBLATE_CONFIG }}" > ~/.config/weblate echo "${{ secrets.WEBLATE_CONFIG }}" >~/.config/weblate
- name: Setup Git - name: Setup Git
run: | run: |
@@ -74,7 +74,7 @@ jobs:
github.repository_owner == 'searxng' github.repository_owner == 'searxng'
&& (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule')
name: Pull Request name: Pull Request
runs-on: ubuntu-24.04-arm runs-on: ubuntu-26.04-arm
permissions: permissions:
# For "make V=1 weblate.translations.commit" # For "make V=1 weblate.translations.commit"
contents: write contents: write
@@ -107,7 +107,7 @@ jobs:
- name: Setup Weblate - name: Setup Weblate
run: | run: |
mkdir -p ~/.config mkdir -p ~/.config
echo "${{ secrets.WEBLATE_CONFIG }}" > ~/.config/weblate echo "${{ secrets.WEBLATE_CONFIG }}" >~/.config/weblate
- name: Setup Git - name: Setup Git
run: | run: |
@@ -118,23 +118,17 @@ jobs:
run: make V=1 weblate.translations.commit run: make V=1 weblate.translations.commit
- name: Create PR - name: Create PR
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with: with:
author: "searxng-bot <searxng-bot@users.noreply.github.com>" author: "searxng-bot <searxng-bot@users.noreply.github.com>"
committer: "searxng-bot <searxng-bot@users.noreply.github.com>" committer: "searxng-bot <searxng-bot@users.noreply.github.com>"
title: "[l10n] update translations from Weblate" title: "[mod] i18n: update translations from Weblate"
commit-message: "[l10n] update translations from Weblate" commit-message: "[mod] i18n: update translations from Weblate"
branch: "translations_update" branch: "translations_update"
delete-branch: "true" delete-branch: "true"
draft: "false" draft: "false"
signoff: "false" signoff: "false"
body: | body: |
[l10n] update translations from Weblate Update translations from Weblate
labels: | labels: |
area:i18n area:i18n
- name: Display information
run: |
echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"

View File

@@ -1,46 +0,0 @@
---
name: Security
# yamllint disable-line rule:truthy
on:
workflow_dispatch:
schedule:
- cron: "42 05 * * *"
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false
permissions:
contents: read
jobs:
container:
if: github.repository_owner == 'searxng'
name: Container
runs-on: ubuntu-24.04-arm
permissions:
security-events: write
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: "false"
- name: Sync GHCS from Docker Scout
uses: docker/scout-action@2688993af7bafd6ba8c6a74ec652442be91dd82b # v1.23.1
with:
organization: "searxng"
dockerhub-user: "${{ secrets.DOCKER_USER }}"
dockerhub-password: "${{ secrets.DOCKER_TOKEN }}"
image: "registry://ghcr.io/searxng/searxng:latest"
command: "cves"
sarif-file: "./scout.sarif"
exit-code: "false"
write-comment: "false"
- name: Upload SARIFs
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
sarif_file: "./scout.sarif"

View File

@@ -15,9 +15,9 @@
"swiped-events": "1.2.0" "swiped-events": "1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.5.2", "@biomejs/biome": "2.5.3",
"@types/node": "^26.1.0", "@types/node": "^26.1.1",
"browserslist": "^4.28.4", "browserslist": "^4.28.6",
"browserslist-to-esbuild": "^2.1.1", "browserslist-to-esbuild": "^2.1.1",
"edge.js": "^6.5.1", "edge.js": "^6.5.1",
"less": "^4.6.7", "less": "^4.6.7",
@@ -27,9 +27,9 @@
"stylelint": "^17.14.0", "stylelint": "^17.14.0",
"stylelint-config-standard-less": "^4.1.0", "stylelint-config-standard-less": "^4.1.0",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^4.0.1", "svgo": "^4.0.2",
"typescript": "~6.0.3", "typescript": "~7.0.2",
"vite": "^8.1.3", "vite": "^8.1.4",
"vite-bundle-analyzer": "^1.3.8" "vite-bundle-analyzer": "^1.3.8"
} }
}, },
@@ -69,9 +69,9 @@
} }
}, },
"node_modules/@biomejs/biome": { "node_modules/@biomejs/biome": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.3.tgz",
"integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", "integrity": "sha512-MrJswFdei9EfDwwUy2tQrPDpK0AO+RmMFvBoaaJ6ayBc3sUbHdCE+XG5N8vp+5So41ZupZJQm0roHFFhMGVD7A==",
"dev": true, "dev": true,
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"bin": { "bin": {
@@ -85,20 +85,20 @@
"url": "https://opencollective.com/biome" "url": "https://opencollective.com/biome"
}, },
"optionalDependencies": { "optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.5.2", "@biomejs/cli-darwin-arm64": "2.5.3",
"@biomejs/cli-darwin-x64": "2.5.2", "@biomejs/cli-darwin-x64": "2.5.3",
"@biomejs/cli-linux-arm64": "2.5.2", "@biomejs/cli-linux-arm64": "2.5.3",
"@biomejs/cli-linux-arm64-musl": "2.5.2", "@biomejs/cli-linux-arm64-musl": "2.5.3",
"@biomejs/cli-linux-x64": "2.5.2", "@biomejs/cli-linux-x64": "2.5.3",
"@biomejs/cli-linux-x64-musl": "2.5.2", "@biomejs/cli-linux-x64-musl": "2.5.3",
"@biomejs/cli-win32-arm64": "2.5.2", "@biomejs/cli-win32-arm64": "2.5.3",
"@biomejs/cli-win32-x64": "2.5.2" "@biomejs/cli-win32-x64": "2.5.3"
} }
}, },
"node_modules/@biomejs/cli-darwin-arm64": { "node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.3.tgz",
"integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", "integrity": "sha512-QhYP9muVQ0nUO5zztFuPbEwi4+94sJWVjaZds9aMi1l/KNZBiUjdiSUrGHsTaMGDXrYl+r4AS2sUKfgH3w+V3g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -113,9 +113,9 @@
} }
}, },
"node_modules/@biomejs/cli-darwin-x64": { "node_modules/@biomejs/cli-darwin-x64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.3.tgz",
"integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", "integrity": "sha512-NC1Ss13UaW7QZX+y8j44bF7AP0jSJdBl6iRhe0MAkvaSqZy+mWg3GaXsrb+eSoHoGDBtaXWEbMVV0iVN2cZ7cQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -130,9 +130,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64": { "node_modules/@biomejs/cli-linux-arm64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.3.tgz",
"integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", "integrity": "sha512-ksx1KWeyYW18ILL04msF/J4ZBtBDN33znYK8Z/aNv/vlBVxL9/g3mGP+omgHJKy4+KWbK87vcmmpmurfNjSgiA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -150,9 +150,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-arm64-musl": { "node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.3.tgz",
"integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", "integrity": "sha512-fccix0w6xp6csCXgxeC0dU/3ecgRQal0y+cv2SP9ajNlhe7Yrk2Ug7UDe2j9AT9ZDYitkXpvUKgZjjuoYeP4Vg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -170,9 +170,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64": { "node_modules/@biomejs/cli-linux-x64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.3.tgz",
"integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", "integrity": "sha512-yMkJtilsgvILDcVkh187aVLTb64xYsrxYajx5kym+r1ULkO5HUOfu9AYKLGQbOVLwJtT2utNw7hhFNg+17mUYA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -190,9 +190,9 @@
} }
}, },
"node_modules/@biomejs/cli-linux-x64-musl": { "node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.3.tgz",
"integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", "integrity": "sha512-O/yU9YKRUiHhmcjF2f38PSjseVk3G4VLWYc0G2HWpzdBVREV6G8IGWIVEFf7MFPfWIzNUIvPsEjeAZQIOgnLcQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -210,9 +210,9 @@
} }
}, },
"node_modules/@biomejs/cli-win32-arm64": { "node_modules/@biomejs/cli-win32-arm64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.3.tgz",
"integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", "integrity": "sha512-cX5z+GYwRcqEok0AH3KSfQGgqYd0Nomfp6Fbe1uiTtELE38hdH2k842wQ9wLNaF/JJ7r4rjJQ4VR+ce+fRmQbw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -227,9 +227,9 @@
} }
}, },
"node_modules/@biomejs/cli-win32-x64": { "node_modules/@biomejs/cli-win32-x64": {
"version": "2.5.2", "version": "2.5.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.3.tgz",
"integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", "integrity": "sha512-ExSaJWi4/u6+GXCszlSKpWSjKNbDseAYqqkCznsCsZ/4uidZ/BEqsCc5/3ctlq6dfIubdIIRSVLC/PG9xPl70Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1648,9 +1648,9 @@
} }
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "26.1.0", "version": "26.1.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz",
"integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1670,6 +1670,346 @@
"integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==", "integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript/typescript-aix-ppc64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-darwin-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-darwin-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-freebsd-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-freebsd-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-arm": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-loong64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-mips64el": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-ppc64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-riscv64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-s390x": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-linux-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-netbsd-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-netbsd-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-openbsd-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-openbsd-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-sunos-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-win32-arm64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@typescript/typescript-win32-x64": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/@zarrita/storage": { "node_modules/@zarrita/storage": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@zarrita/storage/-/storage-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@zarrita/storage/-/storage-0.2.0.tgz",
@@ -1767,9 +2107,9 @@
} }
}, },
"node_modules/baseline-browser-mapping": { "node_modules/baseline-browser-mapping": {
"version": "2.10.41", "version": "2.10.43",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.41.tgz", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz",
"integrity": "sha512-WwS7MHhqGHHlaVsqRZnhvCEMS0owDX+SxRlve7JkuH7My1Ara3ZriTmCQupPfYjxMZ8I/tgxtJYr2t7taHaH4A==", "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -1800,9 +2140,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.28.4", "version": "4.28.6",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
"integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -1820,10 +2160,10 @@
], ],
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.10.38", "baseline-browser-mapping": "^2.10.42",
"caniuse-lite": "^1.0.30001799", "caniuse-lite": "^1.0.30001803",
"electron-to-chromium": "^1.5.376", "electron-to-chromium": "^1.5.389",
"node-releases": "^2.0.48", "node-releases": "^2.0.51",
"update-browserslist-db": "^1.2.3" "update-browserslist-db": "^1.2.3"
}, },
"bin": { "bin": {
@@ -1877,9 +2217,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001800", "version": "1.0.30001805",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz",
"integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==", "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -2295,9 +2635,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.385", "version": "1.5.389",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.385.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
"integrity": "sha512-78sa/M08MNAYHQfjoWMvOlKQqZ0ElhSm/L5HNUc96VZ3b+KvDVnngFm8sYQy0XrhTRgAhggHr5abA7yTvRdo4Q==", "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
@@ -3430,9 +3770,9 @@
} }
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.50", "version": "2.0.51",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
"integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4388,9 +4728,9 @@
"dev": true "dev": true
}, },
"node_modules/svgo": { "node_modules/svgo": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz",
"integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4561,17 +4901,38 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "6.0.3", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc"
"tsserver": "bin/tsserver"
}, },
"engines": { "engines": {
"node": ">=14.17" "node": ">=16.20.0"
},
"optionalDependencies": {
"@typescript/typescript-aix-ppc64": "7.0.2",
"@typescript/typescript-darwin-arm64": "7.0.2",
"@typescript/typescript-darwin-x64": "7.0.2",
"@typescript/typescript-freebsd-arm64": "7.0.2",
"@typescript/typescript-freebsd-x64": "7.0.2",
"@typescript/typescript-linux-arm": "7.0.2",
"@typescript/typescript-linux-arm64": "7.0.2",
"@typescript/typescript-linux-loong64": "7.0.2",
"@typescript/typescript-linux-mips64el": "7.0.2",
"@typescript/typescript-linux-ppc64": "7.0.2",
"@typescript/typescript-linux-riscv64": "7.0.2",
"@typescript/typescript-linux-s390x": "7.0.2",
"@typescript/typescript-linux-x64": "7.0.2",
"@typescript/typescript-netbsd-arm64": "7.0.2",
"@typescript/typescript-netbsd-x64": "7.0.2",
"@typescript/typescript-openbsd-arm64": "7.0.2",
"@typescript/typescript-openbsd-x64": "7.0.2",
"@typescript/typescript-sunos-x64": "7.0.2",
"@typescript/typescript-win32-arm64": "7.0.2",
"@typescript/typescript-win32-x64": "7.0.2"
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
@@ -4642,16 +5003,16 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "8.1.3", "version": "8.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz",
"integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.4", "picomatch": "^4.0.5",
"postcss": "^8.5.16", "postcss": "^8.5.16",
"rolldown": "~1.1.3", "rolldown": "~1.1.4",
"tinyglobby": "^0.2.17" "tinyglobby": "^0.2.17"
}, },
"bin": { "bin": {
@@ -4730,9 +5091,9 @@
} }
}, },
"node_modules/vite/node_modules/picomatch": { "node_modules/vite/node_modules/picomatch": {
"version": "4.0.4", "version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {

View File

@@ -29,9 +29,9 @@
"swiped-events": "1.2.0" "swiped-events": "1.2.0"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.5.2", "@biomejs/biome": "2.5.3",
"@types/node": "^26.1.0", "@types/node": "^26.1.1",
"browserslist": "^4.28.4", "browserslist": "^4.28.6",
"browserslist-to-esbuild": "^2.1.1", "browserslist-to-esbuild": "^2.1.1",
"edge.js": "^6.5.1", "edge.js": "^6.5.1",
"less": "^4.6.7", "less": "^4.6.7",
@@ -41,9 +41,9 @@
"stylelint": "^17.14.0", "stylelint": "^17.14.0",
"stylelint-config-standard-less": "^4.1.0", "stylelint-config-standard-less": "^4.1.0",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^4.0.1", "svgo": "^4.0.2",
"typescript": "~6.0.3", "typescript": "~7.0.2",
"vite": "^8.1.3", "vite": "^8.1.4",
"vite-bundle-analyzer": "^1.3.8" "vite-bundle-analyzer": "^1.3.8"
} }
} }

View File

@@ -4,7 +4,7 @@ cov-core==1.15.0
black==25.9.0 black==25.9.0
pylint==4.0.6 pylint==4.0.6
splinter==0.21.0 splinter==0.21.0
selenium==4.45.0 selenium==4.46.0
Sphinx==8.2.3;python_version <= "3.11" Sphinx==8.2.3;python_version <= "3.11"
Sphinx==9.1.0; python_version > "3.11" Sphinx==9.1.0; python_version > "3.11"
sphinx-issues==6.0.0 sphinx-issues==6.0.0

View File

@@ -13,7 +13,7 @@ sniffio==1.3.1
valkey==6.1.1 valkey==6.1.1
markdown-it-py==4.2.0 markdown-it-py==4.2.0
msgspec==0.21.1 msgspec==0.21.1
typer==0.26.8 typer==0.27.0
isodate==0.7.2 isodate==0.7.2
whitenoise==6.12.0 whitenoise==6.12.0
typing-extensions==4.16.0 typing-extensions==4.16.0

View File

@@ -48,7 +48,7 @@ class ExpireCacheCfg(msgspec.Struct): # pylint: disable=too-few-public-methods
MAXHOLD_TIME: int = 60 * 60 * 24 * 7 # 7 days MAXHOLD_TIME: int = 60 * 60 * 24 * 7 # 7 days
"""Hold time (default in sec.), after which a value is removed from the cache.""" """Hold time (default in sec.), after which a value is removed from the cache."""
MAINTENANCE_PERIOD: int = 60 * 60 # 2h MAINTENANCE_PERIOD: int = 60 * 60 # 1h
"""Maintenance period in seconds / when :py:obj:`MAINTENANCE_MODE` is set to """Maintenance period in seconds / when :py:obj:`MAINTENANCE_MODE` is set to
``auto``.""" ``auto``."""
@@ -458,12 +458,22 @@ class ExpireCacheSQLite(sqlitedb.SQLiteAppl, ExpireCache):
# Before values are taken from the table, a maintenance interval may # Before values are taken from the table, a maintenance interval may
# need to be carried out. # need to be carried out.
self.maintenance() self.maintenance()
sql = f"SELECT value FROM {table} WHERE key = ?" sql = f"SELECT value, expire FROM {table} WHERE key = ?"
row = self.DB.execute(sql, (key,)).fetchone() row = self.DB.execute(sql, (key,)).fetchone()
if row is None: if row is None:
return default return default
return self.deserialize(row[0]) # Check if value is expired. It's possible that it's expired but has not
# yet been automatically deleted by the periodic maintenance
(value, expire) = row
now = time.time()
if expire < now:
# The record is deleted during the maintenance interval. Deleting
# the record at this point offers no advantage, as a SELECT
# statement must be executed for every cache.get request anyways.
return default
return self.deserialize(value)
def pairs(self, ctx: str) -> Iterator[tuple[str, typing.Any]]: def pairs(self, ctx: str) -> Iterator[tuple[str, typing.Any]]:
"""Iterate over key/value pairs from table given by argument ``ctx``. """Iterate over key/value pairs from table given by argument ``ctx``.

View File

@@ -47,7 +47,7 @@ ENGINES_CACHE: ExpireCacheSQLite = ExpireCacheSQLite.build_cache(
ExpireCacheCfg( ExpireCacheCfg(
name="ENGINES_CACHE", name="ENGINES_CACHE",
MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days
MAINTENANCE_PERIOD=60 * 60, # 2h MAINTENANCE_PERIOD=60 * 60, # 1h
MAX_VALUE_LEN=1024 * 1024 * 1024, # 1MB MAX_VALUE_LEN=1024 * 1024 * 1024, # 1MB
) )
) )

View File

@@ -83,7 +83,7 @@ def extract_video_data(video_block):
published_date = None published_date = None
if create_time: if create_time:
try: try:
published_date = datetime.strptime(create_time.strip(), "%Y-%m-%d") published_date = datetime.fromisoformat(create_time.strip())
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass

View File

@@ -109,7 +109,7 @@ def response(resp: "SXNG_Response") -> EngineResults:
comments_elements = eval_xpath_getindex(entry, xpath_comment, 0, default=None) comments_elements = eval_xpath_getindex(entry, xpath_comment, 0, default=None)
comments: str = "" if comments_elements is None else comments_elements.text comments: str = "" if comments_elements is None else comments_elements.text
publishedDate = datetime.strptime(eval_xpath_getindex(entry, xpath_published, 0).text, "%Y-%m-%dT%H:%M:%SZ") publishedDate = datetime.fromisoformat(eval_xpath_getindex(entry, xpath_published, 0).text.rstrip("Z"))
res.add( res.add(
res.types.Paper( res.types.Paper(

View File

@@ -186,7 +186,7 @@ def parse_images(data):
img_date = item.get("bdImgnewsDate") img_date = item.get("bdImgnewsDate")
publishedDate = None publishedDate = None
if img_date: if img_date:
publishedDate = datetime.strptime(img_date, "%Y-%m-%d %H:%M") publishedDate = datetime.fromisoformat(img_date)
results.append( results.append(
{ {
"template": "images.html", "template": "images.html",

View File

@@ -44,7 +44,7 @@ def response(resp):
"url": 'https://www.bitchute.com/video/' + item['video_id'], "url": 'https://www.bitchute.com/video/' + item['video_id'],
"content": html_to_text(item['description']), "content": html_to_text(item['description']),
"author": item['channel']['channel_name'], "author": item['channel']['channel_name'],
"publishedDate": datetime.strptime(item["date_published"], "%Y-%m-%dT%H:%M:%S.%fZ"), "publishedDate": datetime.fromisoformat(item["date_published"].rstrip("Z")),
"length": item['duration'], "length": item['duration'],
"views": item['view_count'], "views": item['view_count'],
"thumbnail": item['thumbnail_url'], "thumbnail": item['thumbnail_url'],

View File

@@ -104,7 +104,7 @@ def response(resp: "SXNG_Response") -> EngineResults:
title=_remove_keyword_marker(result["Subject"]), title=_remove_keyword_marker(result["Subject"]),
content=_remove_keyword_marker(result["Text"]), content=_remove_keyword_marker(result["Text"]),
url=result["Url"], url=result["Url"],
publishedDate=datetime.strptime(result["Published"], "%Y-%m-%d %H:%M:%S"), publishedDate=datetime.fromisoformat(result["Published"]),
metadata=gettext.gettext("Posted by {author}").format(author=result["Author"]), metadata=gettext.gettext("Posted by {author}").format(author=result["Author"]),
) )
) )

View File

@@ -43,7 +43,7 @@ def response(resp):
publishedDate = None publishedDate = None
if recipe['submissionDate']: if recipe['submissionDate']:
publishedDate = datetime.strptime(result['recipe']['submissionDate'][:19], "%Y-%m-%dT%H:%M:%S") publishedDate = datetime.fromisoformat(result['recipe']['submissionDate'][:19])
content = [ content = [
f"Schwierigkeitsstufe (1-3): {recipe['difficulty']}", f"Schwierigkeitsstufe (1-3): {recipe['difficulty']}",

View File

@@ -37,7 +37,7 @@ def response(resp):
for item in search_res: for item in search_res:
img = 'https://s3.thehackerblog.com/findthatmeme/' + item['image_path'] img = 'https://s3.thehackerblog.com/findthatmeme/' + item['image_path']
thumb = 'https://s3.thehackerblog.com/findthatmeme/thumb/' + item.get('thumbnail', '') thumb = 'https://s3.thehackerblog.com/findthatmeme/thumb/' + item.get('thumbnail', '')
date = datetime.strptime(item["updated_at"].split("T")[0], "%Y-%m-%d") date = datetime.fromisoformat(item["updated_at"].split("T")[0])
formatted_date = datetime.fromtimestamp(date.timestamp()) formatted_date = datetime.fromtimestamp(date.timestamp())
results.append( results.append(

View File

@@ -47,7 +47,7 @@ def response(resp: "SXNG_Response"):
title=result["title"], title=result["title"],
content=result["description"], content=result["description"],
thumbnail=result["smallImageURL"], thumbnail=result["smallImageURL"],
publishedDate=datetime.strptime(result["status_since"], "%Y-%m-%d %H:%M:%S"), publishedDate=datetime.fromisoformat(result["status_since"]),
metadata=f"Rank: {result['rank']} || {result['episode_count']} episodes", metadata=f"Rank: {result['rank']} || {result['episode_count']} episodes",
) )
) )

View File

@@ -68,7 +68,9 @@ def _get_api_token(query: str) -> str:
if cached_token: if cached_token:
return cached_token return cached_token
resp = get(f"{base_url}/search?q={query}", headers={"User-Agent": gen_useragent()}) resp = get(
f"{base_url}/search?q={query}", headers={"User-Agent": gen_useragent(), "Accept-Language": "en-US,en:q=0.9"}
)
if not resp.ok: if not resp.ok:
raise SearxEngineAPIException("failed to obtain request token: invalid response code") raise SearxEngineAPIException("failed to obtain request token: invalid response code")
@@ -76,7 +78,7 @@ def _get_api_token(query: str) -> str:
if not token: if not token:
raise SearxEngineAPIException("failed to obtain request token: no token found") raise SearxEngineAPIException("failed to obtain request token: no token found")
CACHE.set("token", token) CACHE.set("token", token, expire=3 * 60)
return token return token

View File

@@ -91,7 +91,7 @@ def response(resp) -> EngineResults:
published_date = None published_date = None
try: try:
published_date = datetime.strptime(entry["createdAt"], "%Y-%m-%dT%H:%M:%S.%fZ") published_date = datetime.fromisoformat(entry["createdAt"].rstrip("Z"))
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass

View File

@@ -43,7 +43,7 @@ def _result(video: dict[str, typing.Any], album_info: dict[str, typing.Any]):
release_time = album_info.get("releaseTime", {}).get("value") release_time = album_info.get("releaseTime", {}).get("value")
if release_time: if release_time:
try: try:
published_date = datetime.strptime(release_time, "%Y-%m-%d") published_date = datetime.fromisoformat(release_time)
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass

View File

@@ -92,7 +92,7 @@ def _get_communities(json):
'title': result['community']['title'], 'title': result['community']['title'],
'content': markdown_to_text(result['community'].get('description', '')), 'content': markdown_to_text(result['community'].get('description', '')),
'thumbnail': result['community'].get('icon', result['community'].get('banner')), 'thumbnail': result['community'].get('icon', result['community'].get('banner')),
'publishedDate': datetime.strptime(counts['published'][:19], '%Y-%m-%dT%H:%M:%S'), 'publishedDate': datetime.fromisoformat(counts['published'][:19]),
'metadata': metadata, 'metadata': metadata,
} }
) )
@@ -141,7 +141,7 @@ def _get_posts(json):
'title': result['post']['name'], 'title': result['post']['name'],
'content': content, 'content': content,
'thumbnail': thumbnail, 'thumbnail': thumbnail,
'publishedDate': datetime.strptime(result['post']['published'][:19], '%Y-%m-%dT%H:%M:%S'), 'publishedDate': datetime.fromisoformat(result['post']['published'][:19]),
'metadata': metadata, 'metadata': metadata,
} }
) )
@@ -170,7 +170,7 @@ def _get_comments(json):
'url': result['comment']['ap_id'], 'url': result['comment']['ap_id'],
'title': result['post']['name'], 'title': result['post']['name'],
'content': markdown_to_text(result['comment']['content']), 'content': markdown_to_text(result['comment']['content']),
'publishedDate': datetime.strptime(result['comment']['published'][:19], '%Y-%m-%dT%H:%M:%S'), 'publishedDate': datetime.fromisoformat(result['comment']['published'][:19]),
'metadata': metadata, 'metadata': metadata,
} }
) )

View File

@@ -60,7 +60,7 @@ def response(resp):
'title': result['username'] + f" ({result['followers_count']} followers)", 'title': result['username'] + f" ({result['followers_count']} followers)",
'content': result['note'], 'content': result['note'],
'thumbnail': result.get('avatar'), 'thumbnail': result.get('avatar'),
'publishedDate': datetime.strptime(result['created_at'][:10], "%Y-%m-%d"), 'publishedDate': datetime.fromisoformat(result['created_at'][:10]),
} }
) )
elif mastodon_type == "hashtags": elif mastodon_type == "hashtags":

View File

@@ -44,7 +44,7 @@ def response(resp) -> EngineResults:
cve_id = item["cve"]["id"] cve_id = item["cve"]["id"]
description = item["cve"]["descriptions"][0]["value"] description = item["cve"]["descriptions"][0]["value"]
date = datetime.strptime(item["cve"]["published"], "%Y-%m-%dT%H:%M:%S.%f") date = datetime.fromisoformat(item["cve"]["published"])
# Extract severity (Low, Medium, High, or Critical) and CVSS score, if available # Extract severity (Low, Medium, High, or Critical) and CVSS score, if available
info = item["cve"].get("metrics", {}).get("cvssMetricV31", [{}])[0].get("cvssData", {}) info = item["cve"].get("metrics", {}).get("cvssMetricV31", [{}])[0].get("cvssData", {})

View File

@@ -76,7 +76,7 @@ def response(resp):
release_time = item["release_time"] release_time = item["release_time"]
duration = item["duration"] duration = item["duration"]
release_date = datetime.strptime(release_time.split("T")[0], "%Y-%m-%d") release_date = datetime.fromisoformat(release_time.split("T")[0])
formatted_date = datetime.fromtimestamp(release_date.timestamp()) formatted_date = datetime.fromtimestamp(release_date.timestamp())
url = f"https://odysee.com/{name}:{claim_id}" url = f"https://odysee.com/{name}:{claim_id}"

View File

@@ -54,7 +54,7 @@ def response(resp: "SXNG_Response"):
title=result["title"], title=result["title"],
content=result["description"], content=result["description"],
thumbnail=result["image_url"], thumbnail=result["image_url"],
publishedDate=datetime.strptime(result["created_at"], "%Y-%m-%d %H:%M:%S"), publishedDate=datetime.fromisoformat(result["created_at"]),
metadata=" | ".join(metadata), metadata=" | ".join(metadata),
) )
) )

View File

@@ -140,7 +140,7 @@ def response(resp):
results.append( results.append(
{ {
'template': 'images.html', 'template': 'images.html',
'url': _clean_url(f"{about['website']}/images/{result['objectID']}"), 'url': _clean_url(f"{pdia_base_url}/images/{result['objectID']}"),
'img_src': _clean_url(base_image_url), 'img_src': _clean_url(base_image_url),
'thumbnail_src': _clean_url(base_image_url + THUMBNAIL_SUFFIX), 'thumbnail_src': _clean_url(base_image_url + THUMBNAIL_SUFFIX),
'title': f"{result['title'].strip()} by {result['artist']} {result.get('displayYear', '')}", 'title': f"{result['title'].strip()} by {result['artist']} {result.get('displayYear', '')}",

View File

@@ -292,7 +292,7 @@ def parse_news_uchq(data):
results = [] results = []
for item in data.get('feed', []): for item in data.get('feed', []):
try: try:
published_date = datetime.strptime(item.get('time'), "%Y-%m-%d") published_date = datetime.fromisoformat(item.get('time'))
except (ValueError, TypeError): except (ValueError, TypeError):
# Sometime Quark will return non-standard format like "1天前", set published_date as None # Sometime Quark will return non-standard format like "1天前", set published_date as None
published_date = None published_date = None

View File

@@ -51,6 +51,7 @@ from urllib.parse import urlencode
import babel import babel
from flask_babel import gettext # pyright: ignore[reportUnknownVariableType] from flask_babel import gettext # pyright: ignore[reportUnknownVariableType]
from searx.enginelib import EngineCache
from searx.enginelib.traits import EngineTraits from searx.enginelib.traits import EngineTraits
from searx.exceptions import ( from searx.exceptions import (
SearxEngineAccessDeniedException, SearxEngineAccessDeniedException,
@@ -105,9 +106,19 @@ qwant_news_locales = [
] ]
# fmt: on # fmt: on
base_url = "https://www.qwant.com"
api_url = "https://api.qwant.com/v3/search/" api_url = "https://api.qwant.com/v3/search/"
"""URL of Qwant's API (JSON)""" """URL of Qwant's API (JSON)"""
CACHE: EngineCache
"""Cache for storing the ``datadome`` cookie."""
def setup(engine_settings: dict[str, t.Any]) -> bool:
global CACHE # pylint: disable=global-statement
CACHE = EngineCache(engine_settings["name"])
return True
def request(query: str, params: "OnlineParams") -> None: def request(query: str, params: "OnlineParams") -> None:
"""Qwant search request""" """Qwant search request"""
@@ -129,24 +140,29 @@ def request(query: str, params: "OnlineParams") -> None:
"tgp": test_group_value, "tgp": test_group_value,
"device": "desktop", "device": "desktop",
"safesearch": params["safesearch"], "safesearch": params["safesearch"],
"display": True, # True would be encoded to "True", instead of "true", which makes the request
"llm": True, # easier to detect and block
"displayed": "true",
"llm": "true",
} }
# shuffle query parameters to be harder to fingerprint
args = list(args.items())
random.shuffle(args)
args = dict(args)
params["raise_for_httperror"] = False params["raise_for_httperror"] = False
params["url"] = f"{api_url}{qwant_categ}?{urlencode(args)}" params["url"] = f"{api_url}{qwant_categ}?{urlencode(args)}"
params["cookies"]["datadome"] = CACHE.get("datadome")
params["headers"].update({"Accept": "application/json", "Referer": f"{base_url}/", "Origin": base_url})
def response(resp: "SXNG_Response") -> EngineResults: def response(resp: "SXNG_Response") -> EngineResults:
"""Parse results from Qwant's API""" """Parse results from Qwant's API"""
# pylint: disable=too-many-locals, too-many-branches, too-many-statements # pylint: disable=too-many-locals, too-many-branches, too-many-statements
# cache datadome cookie - changes on each request
datadome = resp.cookies.get("datadome")
if datadome:
CACHE.set("datadome", datadome)
res = EngineResults() res = EngineResults()
# Try to load JSON result # Try to load JSON result
@@ -163,8 +179,8 @@ def response(resp: "SXNG_Response") -> EngineResults:
error_code = data.get("error_code") error_code = data.get("error_code")
if error_code == 24: if error_code == 24:
raise SearxEngineTooManyRequestsException() raise SearxEngineTooManyRequestsException()
if search_results.get("data", {}).get("error_data", {}).get("captchaUrl") is not None: if search_results.get("url") is not None:
raise SearxEngineCaptchaException() raise SearxEngineCaptchaException(suspended_time=0)
if resp.status_code == 403: if resp.status_code == 403:
raise SearxEngineAccessDeniedException() raise SearxEngineAccessDeniedException()
msg = ",".join(data.get("message", ["unknown"])) msg = ",".join(data.get("message", ["unknown"]))
@@ -302,7 +318,7 @@ def fetch_traits(engine_traits: EngineTraits):
from searx.utils import extr from searx.utils import extr
resp = get( resp = get(
about["website"], # pyright: ignore[reportArgumentType] base_url, # pyright: ignore[reportArgumentType]
timeout=5, timeout=5,
) )
if not resp.ok: if not resp.ok:

View File

@@ -58,7 +58,7 @@ def response(resp):
title = extract_text(result_dom.xpath(title_xpath)) title = extract_text(result_dom.xpath(title_xpath))
p_date = extract_text(result_dom.xpath(published_date)) p_date = extract_text(result_dom.xpath(published_date))
# fix offset date for line 644 webapp.py check # fix offset date for line 644 webapp.py check
fixed_date = datetime.strptime(p_date, '%Y-%m-%dT%H:%M:%S%z') fixed_date = datetime.fromisoformat(p_date)
earned = extract_text(result_dom.xpath(earned_xpath)) earned = extract_text(result_dom.xpath(earned_xpath))
views = extract_text(result_dom.xpath(views_xpath)) views = extract_text(result_dom.xpath(views_xpath))
rumbles = extract_text(result_dom.xpath(rumbles_xpath)) rumbles = extract_text(result_dom.xpath(rumbles_xpath))

View File

@@ -120,7 +120,7 @@ def response(resp: "SXNG_Response") -> EngineResults:
publishedDate: datetime | None publishedDate: datetime | None
if "pubDate" in result: if "pubDate" in result:
publishedDate = datetime.strptime(result["pubDate"], "%Y-%m-%d") publishedDate = datetime.fromisoformat(result["pubDate"])
else: else:
publishedDate = None publishedDate = None

View File

@@ -95,7 +95,8 @@ def _parse_date(text):
date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", text) date_match = re.search(r"(\d{4}-\d{1,2}-\d{1,2})", text)
if date_match: if date_match:
try: try:
return datetime.strptime(date_match.group(1), "%Y-%m-%d") y, m, d = date_match.group(1).split("-")
return datetime(year=int(y), month=int(m), day=int(d))
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
return None return None

View File

@@ -54,7 +54,7 @@ def response(resp):
published_date = None published_date = None
if entry.get("date") and entry.get("duration"): if entry.get("date") and entry.get("duration"):
try: try:
published_date = datetime.strptime(entry['date'], "%Y-%m-%d") published_date = datetime.fromisoformat(entry['date'])
except (ValueError, TypeError): except (ValueError, TypeError):
published_date = None published_date = None

View File

@@ -134,7 +134,7 @@ def response(resp: "SXNG_Response") -> EngineResults:
return str(record.get(k, "")) return str(record.get(k, ""))
for record in json_data["records"]: for record in json_data["records"]:
published = datetime.strptime(record["publicationDate"], "%Y-%m-%d") published = datetime.fromisoformat(record["publicationDate"])
authors: list[str] = [" ".join(author["creator"].split(", ")[::-1]) for author in record["creators"]] authors: list[str] = [" ".join(author["creator"].split(", ")[::-1]) for author in record["creators"]]
pdf_url = "" pdf_url = ""

View File

@@ -81,7 +81,7 @@ def _story(item):
return { return {
'title': item['title'], 'title': item['title'],
'thumbnail': item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'), 'thumbnail': item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'),
'publishedDate': datetime.strptime(item['date'][:19], '%Y-%m-%dT%H:%M:%S'), 'publishedDate': datetime.fromisoformat(item['date'][:19]),
'content': item.get('firstSentence'), 'content': item.get('firstSentence'),
'url': item['shareURL'] if use_source_url else item['detailsweb'], 'url': item['shareURL'] if use_source_url else item['detailsweb'],
} }
@@ -103,7 +103,7 @@ def _video(item):
'template': 'videos.html', 'template': 'videos.html',
'title': title, 'title': title,
'thumbnail': item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'), 'thumbnail': item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'),
'publishedDate': datetime.strptime(item['date'][:19], '%Y-%m-%dT%H:%M:%S'), 'publishedDate': datetime.fromisoformat(item['date'][:19]),
'content': item.get('firstSentence', ''), 'content': item.get('firstSentence', ''),
'iframe_src': video_url, 'iframe_src': video_url,
'url': url, 'url': url,

View File

@@ -117,7 +117,7 @@ def _obtain_session_code() -> str:
if not code: if not code:
raise SearxEngineAPIException("failed to obtain session code") raise SearxEngineAPIException("failed to obtain session code")
CACHE.set("session", code, expire=60 * 24 * 60) # cookie is valid for two months CACHE.set("session", code, expire=60 * 24 * 60 * 60) # cookie is valid for two months
return code return code

View File

@@ -121,7 +121,7 @@ def parse_tineye_match(match_json):
crawl_date = backlink_json.get("crawl_date") crawl_date = backlink_json.get("crawl_date")
if crawl_date: if crawl_date:
crawl_date = datetime.strptime(crawl_date, '%Y-%m-%d') crawl_date = datetime.fromisoformat(crawl_date)
else: else:
crawl_date = datetime.min crawl_date = datetime.min

View File

@@ -51,7 +51,7 @@ def response(resp):
'title': title, 'title': title,
'content': html_to_text(result['content']), 'content': html_to_text(result['content']),
'thumbnail': thumbnail, 'thumbnail': thumbnail,
'publishedDate': datetime.strptime(result['created_at'], '%Y-%m-%d %H:%M:%S'), 'publishedDate': datetime.fromisoformat(result['created_at']),
} }
) )

View File

@@ -13,7 +13,7 @@ from dateutil import parser
from searx.exceptions import SearxEngineAPIException from searx.exceptions import SearxEngineAPIException
from searx.network import get from searx.network import get
from searx.utils import html_to_text from searx.utils import gen_useragent, html_to_text
from searx.result_types import EngineResults from searx.result_types import EngineResults
if t.TYPE_CHECKING: if t.TYPE_CHECKING:
@@ -52,7 +52,7 @@ def _obtain_x_sid() -> tuple[str, str]:
The header key is usually called `x-sid-{UUIDv4}`, and the value is The header key is usually called `x-sid-{UUIDv4}`, and the value is
usually a plain UUIDv4 (but a different one than in the header key). usually a plain UUIDv4 (but a different one than in the header key).
""" """
resp = get(f"{api_url}/revcontent/embed.js") resp = get(f"{api_url}/revcontent/embed.js", headers={"User-Agent": gen_useragent()})
if not resp.ok: if not resp.ok:
raise SearxEngineAPIException("failed to obtain request x-sid token") raise SearxEngineAPIException("failed to obtain request x-sid token")
@@ -89,12 +89,14 @@ def request(query: str, params: "OnlineParams") -> None:
params["url"] = f"{api_url}/Search/Web?{urlencode(args)}" params["url"] = f"{api_url}/Search/Web?{urlencode(args)}"
x_sid_header, x_sid_value = _obtain_x_sid() x_sid_header, x_sid_value = _obtain_x_sid()
params["headers"] = { params["headers"].update(
x_sid_header: x_sid_value, {
# required - we send a random longitude and latitude instead of the actual user location x_sid_header: x_sid_value,
'x-lon': str(random.random() * 90), # required - we send a random longitude and latitude instead of the actual user location
'x-lat': str(random.random() * 90), "x-lon": str(round(random.random() * 90, 4)),
} "x-lat": str(round(random.random() * 90, 4)),
}
)
def response(resp: "SXNG_Response"): def response(resp: "SXNG_Response"):

View File

@@ -79,7 +79,7 @@ def response(resp):
'img_src': result['path'], 'img_src': result['path'],
'thumbnail_src': result['thumbs']['small'], 'thumbnail_src': result['thumbs']['small'],
'resolution': result['resolution'].replace('x', ' x '), 'resolution': result['resolution'].replace('x', ' x '),
'publishedDate': datetime.strptime(result['created_at'], '%Y-%m-%d %H:%M:%S'), 'publishedDate': datetime.fromisoformat(result['created_at']),
'img_format': result['file_type'], 'img_format': result['file_type'],
'filesize': humanize_bytes(result['file_size']), 'filesize': humanize_bytes(result['file_size']),
} }

View File

@@ -52,6 +52,12 @@ def _normalize_url_fields(result: "Result | LegacyResult"):
result.parsed_url = urllib.parse.urlparse(result.url) result.parsed_url = urllib.parse.urlparse(result.url)
if result.parsed_url: if result.parsed_url:
# properly format special characters (e.g. "ä", "ö") in IDN domains
# e.g. "xn--strung-xxa.de" becomes "störung.de"
if result.parsed_url.netloc.startswith("xn--"):
netloc = result.parsed_url.netloc.encode().decode("idna")
result.parsed_url = result.parsed_url._replace(netloc=netloc)
result.parsed_url = result.parsed_url._replace( result.parsed_url = result.parsed_url._replace(
# if the result has no scheme, use http as default # if the result has no scheme, use http as default
scheme=result.parsed_url.scheme or "http", scheme=result.parsed_url.scheme or "http",

View File

@@ -477,6 +477,24 @@ engines:
engine: arxiv engine: arxiv
shortcut: arx shortcut: arx
- name: avalw
engine: json_engine
shortcut: av
categories: general
paging: true
search_url: https://avalw.org/api/search?q={query}&page={pageno}&limit=10&tbm=all
results_query: results
url_query: url
title_query: title
content_query: snippet
thumbnail: image_url
disabled: true
inactive: true
about:
website: https://avalw.com
description: "Search engine from the Romanian news company AVALW S.R.L."
results: JSON
- name: ayo - name: ayo
engine: xpath engine: xpath
categories: general categories: general

View File

@@ -1,3 +1,3 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunk/BnnvKC7b.min.js","./sxng-mapview.min.css","./chunk/DpvWr1cn.min.js","./chunk/DK4yUVpy.min.js","./chunk/DcK-mo-Y.min.js","./chunk/C8c7HJzp.min.js","./chunk/C93hSkpT.min.js","./chunk/5Ako-qGW.min.js","./chunk/DvCYLbJr.min.js","./chunk/od7pNHfk.min.js","./chunk/e2-9fzwE.min.js"])))=>i.map(i=>d[i]); const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunk/BnnvKC7b.min.js","./sxng-mapview.min.css","./chunk/DpvWr1cn.min.js","./chunk/DK4yUVpy.min.js","./chunk/DcK-mo-Y.min.js","./chunk/C8c7HJzp.min.js","./chunk/C93hSkpT.min.js","./chunk/5Ako-qGW.min.js","./chunk/DvCYLbJr.min.js","./chunk/od7pNHfk.min.js","./chunk/e2-9fzwE.min.js"])))=>i.map(i=>d[i]);
var e=class{id;constructor(e){this.id=e,queueMicrotask(()=>this.invoke())}async invoke(){try{console.debug(`[PLUGIN] ${this.id}: Running...`);let e=await this.run();if(!e)return;console.debug(`[PLUGIN] ${this.id}: Running post-exec...`),await this.post(e)}catch(e){console.error(`[PLUGIN] ${this.id}:`,e)}finally{console.debug(`[PLUGIN] ${this.id}: Done.`)}}},t={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},n={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0},r=()=>{let e=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return e&&e in t?e:t.unknown},i=()=>{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),{}}},a=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},o=(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)},s=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?o(`DOMContentLoaded`,document,e,{once:!0}):e()},c=r(),l=i(),u=(e,t)=>{d(t)&&e()},d=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(c)}},f=`modulepreload`,p=function(e,t){return new URL(e,t).href},m={},h=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,new URL(`../../../src/node/plugins/importAnalysisBuild.ts`,import.meta.url)).href}r=o(t.map(t=>{if(t=p(t,n),t=s(t),t in m)return;m[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`:f,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(Error(`Unable to preload CSS for ${t}`)))})}))}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)})};s(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),o(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),o(`click`,`.searxng_init_map`,async function(e){e.preventDefault(),this.classList.remove(`searxng_init_map`),u(()=>h(async()=>{let{default:e}=await import(`./chunk/BnnvKC7b.min.js`);return{default:e}},__vite__mapDeps([0,1]),import.meta.url).then(({default:e})=>new e(this)),{on:`endpoint`,where:[t.results]})}),l.plugins?.includes(`infiniteScroll`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DpvWr1cn.min.js`);return{default:e}},__vite__mapDeps([2,3,4]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]}),l.plugins?.includes(`calculator`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/C8c7HJzp.min.js`);return{default:e}},__vite__mapDeps([5,4,3]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]})}),s(()=>{h(()=>import(`./chunk/C93hSkpT.min.js`),__vite__mapDeps([6,3]),import.meta.url),h(()=>import(`./chunk/5Ako-qGW.min.js`),__vite__mapDeps([7,4,3]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/DvCYLbJr.min.js`),__vite__mapDeps([8,3]),import.meta.url)},{on:[c===t.index]}),s(()=>{h(()=>import(`./chunk/C93hSkpT.min.js`),__vite__mapDeps([6,3]),import.meta.url),h(()=>import(`./chunk/od7pNHfk.min.js`),__vite__mapDeps([9,3]),import.meta.url),h(()=>import(`./chunk/5Ako-qGW.min.js`),__vite__mapDeps([7,4,3]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/DvCYLbJr.min.js`),__vite__mapDeps([8,3]),import.meta.url)},{on:[c===t.results]}),s(()=>{h(()=>import(`./chunk/e2-9fzwE.min.js`),__vite__mapDeps([10,3]),import.meta.url)},{on:[c===t.preferences]});export{e as a,l as i,o as n,n as r,a as t}; var e=class{id;constructor(e){this.id=e,queueMicrotask(()=>this.invoke())}async invoke(){try{console.debug(`[PLUGIN] ${this.id}: Running...`);let e=await this.run();if(!e)return;console.debug(`[PLUGIN] ${this.id}: Running post-exec...`),await this.post(e)}catch(e){console.error(`[PLUGIN] ${this.id}:`,e)}finally{console.debug(`[PLUGIN] ${this.id}: Done.`)}}},t={index:`index`,results:`results`,preferences:`preferences`,unknown:`unknown`},n={closeDetail:void 0,scrollPageToSelected:void 0,selectImage:void 0,selectNext:void 0,selectPrevious:void 0},r=()=>{let e=document.querySelector(`meta[name="endpoint"]`)?.getAttribute(`content`);return e&&e in t?e:t.unknown},i=()=>{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),{}}},a=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},o=(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)},s=(e,t)=>{for(let e of t?.on??[])if(!e)return;document.readyState===`loading`?o(`DOMContentLoaded`,document,e,{once:!0}):e()},c=r(),l=i(),u=(e,t)=>{d(t)&&e()},d=e=>{switch(e.on){case`global`:return!0;case`endpoint`:return!!e.where.includes(c)}},f=`modulepreload`,p=function(e,t){return new URL(e,t).href},m={},h=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=p(t,n),t=s(t),t in m)return;m[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`:f,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(Error(`Unable to preload CSS for ${t}`)))})}))}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)})};s(()=>{document.documentElement.classList.remove(`no-js`),document.documentElement.classList.add(`js`),o(`click`,`.close`,function(){this.parentNode?.classList.add(`invisible`)}),o(`click`,`.searxng_init_map`,async function(e){e.preventDefault(),this.classList.remove(`searxng_init_map`),u(()=>h(async()=>{let{default:e}=await import(`./chunk/BnnvKC7b.min.js`);return{default:e}},__vite__mapDeps([0,1]),import.meta.url).then(({default:e})=>new e(this)),{on:`endpoint`,where:[t.results]})}),l.plugins?.includes(`infiniteScroll`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/DpvWr1cn.min.js`);return{default:e}},__vite__mapDeps([2,3,4]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]}),l.plugins?.includes(`calculator`)&&u(()=>h(async()=>{let{default:e}=await import(`./chunk/C8c7HJzp.min.js`);return{default:e}},__vite__mapDeps([5,4,3]),import.meta.url).then(({default:e})=>new e),{on:`endpoint`,where:[t.results]})}),s(()=>{h(()=>import(`./chunk/C93hSkpT.min.js`),__vite__mapDeps([6,3]),import.meta.url),h(()=>import(`./chunk/5Ako-qGW.min.js`),__vite__mapDeps([7,4,3]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/DvCYLbJr.min.js`),__vite__mapDeps([8,3]),import.meta.url)},{on:[c===t.index]}),s(()=>{h(()=>import(`./chunk/C93hSkpT.min.js`),__vite__mapDeps([6,3]),import.meta.url),h(()=>import(`./chunk/od7pNHfk.min.js`),__vite__mapDeps([9,3]),import.meta.url),h(()=>import(`./chunk/5Ako-qGW.min.js`),__vite__mapDeps([7,4,3]),import.meta.url),l.autocomplete&&h(()=>import(`./chunk/DvCYLbJr.min.js`),__vite__mapDeps([8,3]),import.meta.url)},{on:[c===t.results]}),s(()=>{h(()=>import(`./chunk/e2-9fzwE.min.js`),__vite__mapDeps([10,3]),import.meta.url)},{on:[c===t.preferences]});export{e as a,l as i,o as n,n as r,a as t};
//# sourceMappingURL=sxng-core.min.js.map //# sourceMappingURL=sxng-core.min.js.map

File diff suppressed because one or more lines are too long

View File

@@ -60,7 +60,7 @@
</p> </p>
<p class="result-filesize">{{ _label(_("Filesize"), result.filesize) }}</p> <p class="result-filesize">{{ _label(_("Filesize"), result.filesize) }}</p>
<p class="result-source">{{ _label(_("Source"), result.source) }}</p> <p class="result-source">{{ _label(_("Source"), result.source) }}</p>
<p class="result-engine">{{ _label(_("Engine"), result.engine) }}</p> <p class="result-engines">{{ _label(_("Engines"), ", ".join(result.engines)) }}</p>
<p class="result-url"><span>{{ _("View source") }}:</span>{{- "" -}} <p class="result-url"><span>{{ _("View source") }}:</span>{{- "" -}}
<a {{ _target(results_on_new_tab) }} href="{{ result.url }}">{{ result.url }}</a> <a {{ _target(results_on_new_tab) }} href="{{ result.url }}">{{ result.url }}</a>
</p> </p>

View File

@@ -845,7 +845,10 @@ def autocompleter():
mimetype = 'application/json' mimetype = 'application/json'
else: else:
# the suggestion request comes from browser's URL bar # the suggestion request comes from browser's URL bar
suggestions = json.dumps([sug_prefix, results]) relevances = {
'google:suggestrelevance': [600 - i for i in range(len(results))]
} # chromium only shows 3 suggestions unless we attach relevances
suggestions = json.dumps([sug_prefix, results, [], [], relevances])
mimetype = 'application/x-suggestions+json' mimetype = 'application/x-suggestions+json'
suggestions = escape(suggestions, False) suggestions = escape(suggestions, False)