mirror of
https://github.com/searxng/searxng.git
synced 2026-06-12 12:57:50 +02:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1ddccaf35 | |||
| 4dd0bf4867 | |||
| 1957876dd6 | |||
| ab13451086 | |||
| a1490676e3 | |||
| 3a382cb3f3 | |||
| 9d9d605b15 | |||
| de03f4eb11 | |||
| 00f7c68a6f | |||
| 41c98b3b41 | |||
| f4c63c8eb0 | |||
| 26801e92af | |||
| f3fab143be | |||
| 72a827ae93 | |||
| 6ca9d3784c | |||
| 63f264220b | |||
| 41fcf0be4b | |||
| 86903a2c66 | |||
| 70de3cc561 | |||
| 51b6fd4f23 | |||
| 9d49a9f344 | |||
| e260a732c8 | |||
| 0429198415 | |||
| e7cf57e9ae | |||
| ed369ac0ec | |||
| 94bdbb5c63 | |||
| 465b5229c6 | |||
| cbf97fd262 | |||
| 37187dc2d8 | |||
| 2f049cb037 | |||
| eb39bc0dc1 | |||
| 007a4e2155 | |||
| 13ce187e64 | |||
| 26fa181b84 | |||
| 0f35ef7cd6 | |||
| b1ae576b2d | |||
| e6559c9ad6 | |||
| 5bae05514b | |||
| 00ca5776f2 | |||
| 577f5f2f30 | |||
| 253dc86c10 | |||
| 3066bc19eb | |||
| e964708c00 | |||
| 7159b8aed3 | |||
| 246f5a5499 | |||
| 300695de5c | |||
| bd863f16b1 | |||
| 4ac822fd7f | |||
| e1d25c5078 | |||
| 01159b82fe | |||
| 780ee32564 | |||
| 217c9a1597 | |||
| 70e810bd7b | |||
| baab1c160a | |||
| dd4664e03a | |||
| 4ea5c57a84 | |||
| 6917395dc1 | |||
| 128e28fe3f | |||
| fb3ed5b081 | |||
| 4ebe6b90d6 | |||
| 0657217a3e | |||
| 0037d43d87 | |||
| f5be39e245 | |||
| 1574939441 | |||
| f1a22dec9e | |||
| 3db8b424a8 | |||
| a16a3dedb4 | |||
| c629dd4f3c | |||
| 28ef4f7447 | |||
| cb4b70ac50 | |||
| e29e861e2c | |||
| 89b89a88fe | |||
| 46071a011a | |||
| b0d8af96bf | |||
| dd27fce3b7 | |||
| efc305b7f9 | |||
| 323ce76004 | |||
| dfc2da707b | |||
| fc90c5b09c | |||
| c57f772ad0 | |||
| b8498d7891 | |||
| d791a3906a | |||
| 295e0bffaf | |||
| b9340f50c2 | |||
| d3deacc6d4 | |||
| d8f74af3d1 | |||
| 24b1a1b6a8 | |||
| d7e8b7cd18 | |||
| f26e450778 | |||
| dce3bb69bb | |||
| de49d27846 | |||
| 16a7537bfd | |||
| afafca93f3 | |||
| 240f403d93 | |||
| 9b30ae005b | |||
| 790683bbd7 | |||
| 52b446b4ad | |||
| 6cee4b8947 | |||
| 8e5aa9d394 | |||
| cf4d7e31c4 | |||
| 471f2b205f | |||
| 09829b1ccc | |||
| df1f24fb7f | |||
| 0cba32c15f | |||
| 849e17e431 | |||
| d8ab61a9e0 | |||
| 7eb130b1a8 |
@@ -1,163 +0,0 @@
|
|||||||
;;; .dir-locals.el
|
|
||||||
;;
|
|
||||||
;; Per-Directory Local Variables:
|
|
||||||
;; https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html
|
|
||||||
;;
|
|
||||||
;; For full fledge developer tools install emacs packages:
|
|
||||||
;;
|
|
||||||
;; M-x package-install ...
|
|
||||||
;;
|
|
||||||
;; magit gitconfig
|
|
||||||
;; nvm lsp-mode lsp-pyright lsp-eslint
|
|
||||||
;; pyvenv pylint pip-requirements
|
|
||||||
;; jinja2-mode
|
|
||||||
;; json-mode
|
|
||||||
;; company company-jedi company-quickhelp company-shell
|
|
||||||
;; realgud
|
|
||||||
;; sphinx-doc markdown-mode graphviz-dot-mode
|
|
||||||
;; apache-mode nginx-mode
|
|
||||||
;;
|
|
||||||
;; To setup a developer environment, build target::
|
|
||||||
;;
|
|
||||||
;; $ make node.env.dev pyenv.install
|
|
||||||
;;
|
|
||||||
;; Some buffer locals are referencing the project environment:
|
|
||||||
;;
|
|
||||||
;; - prj-root --> <repo>/
|
|
||||||
;; - nvm-dir --> <repo>/.nvm
|
|
||||||
;; - python-environment-directory --> <repo>/local
|
|
||||||
;; - python-environment-default-root-name --> py3
|
|
||||||
;; - python-shell-virtualenv-root --> <repo>/local/py3
|
|
||||||
;; When this variable is set with the path of the virtualenv to use,
|
|
||||||
;; `process-environment' and `exec-path' get proper values in order to run
|
|
||||||
;; shells inside the specified virtualenv, example::
|
|
||||||
;; (setq python-shell-virtualenv-root "/path/to/env/")
|
|
||||||
;; - python-shell-interpreter --> <repo>/local/py3/bin/python
|
|
||||||
;;
|
|
||||||
;; Python development:
|
|
||||||
;;
|
|
||||||
;; Jedi, flycheck & other python stuff should use the 'python-shell-interpreter'
|
|
||||||
;; from the local py3 environment.
|
|
||||||
;;
|
|
||||||
|
|
||||||
((nil
|
|
||||||
. ((fill-column . 80)
|
|
||||||
(indent-tabs-mode . nil)
|
|
||||||
(eval . (progn
|
|
||||||
|
|
||||||
(add-to-list 'auto-mode-alist '("\\.html\\'" . jinja2-mode))
|
|
||||||
|
|
||||||
;; project root folder is where the `.dir-locals.el' is located
|
|
||||||
(setq-local prj-root
|
|
||||||
(locate-dominating-file default-directory ".dir-locals.el"))
|
|
||||||
|
|
||||||
(setq-local python-environment-directory
|
|
||||||
(expand-file-name "./local" prj-root))
|
|
||||||
|
|
||||||
;; to get in use of NVM environment, install https://github.com/rejeep/nvm.el
|
|
||||||
(setq-local nvm-dir (expand-file-name "./.nvm" prj-root))
|
|
||||||
|
|
||||||
;; use nodejs from the (local) NVM environment (see nvm-dir)
|
|
||||||
(nvm-use-for-buffer)
|
|
||||||
(ignore-errors (require 'lsp))
|
|
||||||
(setq-local lsp-server-install-dir (car (cdr nvm-current-version)))
|
|
||||||
(setq-local lsp-enable-file-watchers nil)
|
|
||||||
|
|
||||||
;; use 'py3' environment as default
|
|
||||||
(setq-local python-environment-default-root-name
|
|
||||||
"py3")
|
|
||||||
|
|
||||||
(setq-local python-shell-virtualenv-root
|
|
||||||
(expand-file-name
|
|
||||||
python-environment-default-root-name python-environment-directory))
|
|
||||||
|
|
||||||
(setq-local python-shell-interpreter
|
|
||||||
(expand-file-name
|
|
||||||
"bin/python" python-shell-virtualenv-root))))))
|
|
||||||
(makefile-gmake-mode
|
|
||||||
. ((indent-tabs-mode . t)))
|
|
||||||
|
|
||||||
(yaml-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
|
|
||||||
;; flycheck should use the local py3 environment
|
|
||||||
(setq-local flycheck-yaml-yamllint-executable
|
|
||||||
(expand-file-name "bin/yamllint" python-shell-virtualenv-root))
|
|
||||||
|
|
||||||
(setq-local flycheck-yamllintrc
|
|
||||||
(expand-file-name ".yamllint.yml" prj-root))
|
|
||||||
|
|
||||||
(flycheck-checker . yaml-yamllint)))))
|
|
||||||
|
|
||||||
(json-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(setq-local js-indent-level 4)
|
|
||||||
(flycheck-checker . json-python-json)))))
|
|
||||||
|
|
||||||
(js-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(ignore-errors (require 'lsp-eslint))
|
|
||||||
(setq-local js-indent-level 2)
|
|
||||||
;; flycheck should use the eslint checker from developer tools
|
|
||||||
(setq-local flycheck-javascript-eslint-executable
|
|
||||||
(expand-file-name "node_modules/.bin/eslint" prj-root))
|
|
||||||
;; (flycheck-mode)
|
|
||||||
|
|
||||||
(if (featurep 'lsp-eslint)
|
|
||||||
(lsp))
|
|
||||||
))))
|
|
||||||
|
|
||||||
(python-mode
|
|
||||||
. ((eval . (progn
|
|
||||||
(ignore-errors (require 'jedi-core))
|
|
||||||
(ignore-errors (require 'lsp-pyright))
|
|
||||||
(ignore-errors (sphinx-doc-mode))
|
|
||||||
(setq-local python-environment-virtualenv
|
|
||||||
(list (expand-file-name "bin/virtualenv" python-shell-virtualenv-root)
|
|
||||||
;;"--system-site-packages"
|
|
||||||
"--quiet"))
|
|
||||||
|
|
||||||
(setq-local pylint-command
|
|
||||||
(expand-file-name "bin/pylint" python-shell-virtualenv-root))
|
|
||||||
|
|
||||||
(if (featurep 'lsp-pyright)
|
|
||||||
(lsp))
|
|
||||||
|
|
||||||
;; pylint will find the '.pylintrc' file next to the CWD
|
|
||||||
;; https://pylint.readthedocs.io/en/latest/user_guide/run.html#command-line-options
|
|
||||||
(setq-local flycheck-pylintrc
|
|
||||||
".pylintrc")
|
|
||||||
|
|
||||||
;; flycheck & other python stuff should use the local py3 environment
|
|
||||||
(setq-local flycheck-python-pylint-executable
|
|
||||||
python-shell-interpreter)
|
|
||||||
|
|
||||||
;; use 'M-x jedi:show-setup-info' and 'M-x epc:controller' to inspect jedi server
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:environment-root -- You
|
|
||||||
;; can specify a full path instead of a name (relative path). In that case,
|
|
||||||
;; python-environment-directory is ignored and Python virtual environment
|
|
||||||
;; is created at the specified path.
|
|
||||||
(setq-local jedi:environment-root
|
|
||||||
python-shell-virtualenv-root)
|
|
||||||
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:server-command
|
|
||||||
(setq-local jedi:server-command
|
|
||||||
(list python-shell-interpreter
|
|
||||||
jedi:server-script))
|
|
||||||
|
|
||||||
;; jedi:environment-virtualenv --> see above 'python-environment-virtualenv'
|
|
||||||
;; is set buffer local! No need to setup jedi:environment-virtualenv:
|
|
||||||
;;
|
|
||||||
;; Virtualenv command to use. A list of string. If it is nil,
|
|
||||||
;; python-environment-virtualenv is used instead. You must set non-nil
|
|
||||||
;; value to jedi:environment-root in order to make this setting work.
|
|
||||||
;;
|
|
||||||
;; https://tkf.github.io/emacs-jedi/latest/#jedi:environment-virtualenv
|
|
||||||
;;
|
|
||||||
;; (setq-local jedi:environment-virtualenv
|
|
||||||
;; (list (expand-file-name "bin/virtualenv" python-shell-virtualenv-root)
|
|
||||||
;; "--python"
|
|
||||||
;; "/usr/bin/python3.4"
|
|
||||||
;; ))
|
|
||||||
))))
|
|
||||||
)
|
|
||||||
@@ -78,7 +78,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
@@ -106,10 +106,10 @@ jobs:
|
|||||||
|
|
||||||
- if: ${{ matrix.emulation }}
|
- if: ${{ matrix.emulation }}
|
||||||
name: Setup QEMU
|
name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
@@ -141,16 +141,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- if: ${{ matrix.emulation }}
|
- if: ${{ matrix.emulation }}
|
||||||
name: Setup QEMU
|
name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
@@ -175,19 +175,19 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "ghcr.io"
|
registry: "ghcr.io"
|
||||||
username: "${{ github.repository_owner }}"
|
username: "${{ github.repository_owner }}"
|
||||||
password: "${{ secrets.GITHUB_TOKEN }}"
|
password: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||||
with:
|
with:
|
||||||
registry: "docker.io"
|
registry: "docker.io"
|
||||||
username: "${{ secrets.DOCKER_USER }}"
|
username: "${{ secrets.DOCKER_USER }}"
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
@@ -50,11 +50,14 @@ jobs:
|
|||||||
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-
|
||||||
path: "./local/"
|
path: "./local/"
|
||||||
|
|
||||||
|
- name: Setup dependencies
|
||||||
|
run: sudo ./utils/searxng.sh install buildhost
|
||||||
|
|
||||||
- name: Setup venv
|
- name: Setup venv
|
||||||
run: make V=1 install
|
run: make V=1 install
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: make V=1 docs.clean docs.html
|
run: make V=1 docs.html
|
||||||
|
|
||||||
- if: github.ref_name == 'master'
|
- if: github.ref_name == 'master'
|
||||||
name: Release
|
name: Release
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
python-version: "${{ env.PYTHON_VERSION }}"
|
python-version: "${{ env.PYTHON_VERSION }}"
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
token: "${{ secrets.WEBLATE_GITHUB_TOKEN }}"
|
||||||
fetch-depth: "0"
|
fetch-depth: "0"
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||||
with:
|
with:
|
||||||
persist-credentials: "false"
|
persist-credentials: "false"
|
||||||
|
|
||||||
- name: Sync GHCS from Docker Scout
|
- name: Sync GHCS from Docker Scout
|
||||||
uses: docker/scout-action@bacf462e8d090c09660de30a6ccc718035f961e3 # v1.20.4
|
uses: docker/scout-action@cd72f264beff1cd72735de31148b9d3244a0234a # v1.21.0
|
||||||
with:
|
with:
|
||||||
organization: "searxng"
|
organization: "searxng"
|
||||||
dockerhub-user: "${{ secrets.DOCKER_USER }}"
|
dockerhub-user: "${{ secrets.DOCKER_USER }}"
|
||||||
@@ -41,6 +41,6 @@ jobs:
|
|||||||
write-comment: "false"
|
write-comment: "false"
|
||||||
|
|
||||||
- name: Upload SARIFs
|
- name: Upload SARIFs
|
||||||
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
||||||
with:
|
with:
|
||||||
sarif_file: "./scout.sarif"
|
sarif_file: "./scout.sarif"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[[language]]
|
||||||
|
name = "python"
|
||||||
|
language-servers = ["basedpyright", "pylsp"]
|
||||||
|
auto-format = true
|
||||||
|
|
||||||
|
[language-server.pylsp.config.pylsp]
|
||||||
|
plugins.pylint.enabled = true
|
||||||
|
plugins.isort.enabled = true
|
||||||
|
plugins.black.enabled = true
|
||||||
|
plugins.black.skip_string_normalization = true
|
||||||
|
plugins.black.line_length = 120
|
||||||
Generated
+186
-197
@@ -15,21 +15,21 @@
|
|||||||
"swiped-events": "1.2.0"
|
"swiped-events": "1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.4.13",
|
"@biomejs/biome": "2.4.16",
|
||||||
"@types/node": "^25.6.0",
|
"@types/node": "^25.9.1",
|
||||||
"browserslist": "^4.28.2",
|
"browserslist": "^4.28.2",
|
||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"edge.js": "^6.5.0",
|
"edge.js": "^6.5.1",
|
||||||
"less": "^4.6.4",
|
"less": "^4.6.4",
|
||||||
"mathjs": "^15.2.0",
|
"mathjs": "^15.2.0",
|
||||||
"sharp": "~0.34.5",
|
"sharp": "~0.34.5",
|
||||||
"sort-package-json": "^3.6.1",
|
"sort-package-json": "^3.6.1",
|
||||||
"stylelint": "^17.9.1",
|
"stylelint": "^17.12.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.1",
|
||||||
"typescript": "~6.0.3",
|
"typescript": "~6.0.3",
|
||||||
"vite": "^8.0.10",
|
"vite": "^8.0.16",
|
||||||
"vite-bundle-analyzer": "^1.3.8"
|
"vite-bundle-analyzer": "^1.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -69,9 +69,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/biome": {
|
"node_modules/@biomejs/biome": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.16.tgz",
|
||||||
"integrity": "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA==",
|
"integrity": "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT OR Apache-2.0",
|
"license": "MIT OR Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -85,20 +85,20 @@
|
|||||||
"url": "https://opencollective.com/biome"
|
"url": "https://opencollective.com/biome"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@biomejs/cli-darwin-arm64": "2.4.13",
|
"@biomejs/cli-darwin-arm64": "2.4.16",
|
||||||
"@biomejs/cli-darwin-x64": "2.4.13",
|
"@biomejs/cli-darwin-x64": "2.4.16",
|
||||||
"@biomejs/cli-linux-arm64": "2.4.13",
|
"@biomejs/cli-linux-arm64": "2.4.16",
|
||||||
"@biomejs/cli-linux-arm64-musl": "2.4.13",
|
"@biomejs/cli-linux-arm64-musl": "2.4.16",
|
||||||
"@biomejs/cli-linux-x64": "2.4.13",
|
"@biomejs/cli-linux-x64": "2.4.16",
|
||||||
"@biomejs/cli-linux-x64-musl": "2.4.13",
|
"@biomejs/cli-linux-x64-musl": "2.4.16",
|
||||||
"@biomejs/cli-win32-arm64": "2.4.13",
|
"@biomejs/cli-win32-arm64": "2.4.16",
|
||||||
"@biomejs/cli-win32-x64": "2.4.13"
|
"@biomejs/cli-win32-x64": "2.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.16.tgz",
|
||||||
"integrity": "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg==",
|
"integrity": "sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -113,9 +113,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-darwin-x64": {
|
"node_modules/@biomejs/cli-darwin-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.16.tgz",
|
||||||
"integrity": "sha512-BKrJklbaFN4p1Ts4kPBczo+PkbsHQg57kmJ+vON9u2t6uN5okYHaSr7h/MutPCWQgg2lglaWoSmm+zhYW+oOkg==",
|
"integrity": "sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -130,9 +130,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64": {
|
"node_modules/@biomejs/cli-linux-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.16.tgz",
|
||||||
"integrity": "sha512-NzkUDSqfvMBrPplKgVr3aXLHZ2NEELvvF4vZxXulEylKWIGqlvNEcwUcj9OLrn75TD3lJ/GIqCVlBwd1MZCuYQ==",
|
"integrity": "sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -147,9 +147,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.16.tgz",
|
||||||
"integrity": "sha512-U5MsuBQW25dXaYtqWWSPM3P96H6Y+fHuja3TQpMNnylocHW0tEbtFTDlUj6oM+YJLntvEkQy4grBvQNUD4+RCg==",
|
"integrity": "sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -164,9 +164,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64": {
|
"node_modules/@biomejs/cli-linux-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.16.tgz",
|
||||||
"integrity": "sha512-Az3ZZedYRBo9EQzNnD9SxFcR1G5QsGo6VEc2hIyVPZ1rdKwee/7E9oeBBZFpE8Z44ekxsDQBqbiWGW5ShOhUSQ==",
|
"integrity": "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -181,9 +181,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.16.tgz",
|
||||||
"integrity": "sha512-Z601MienRgTBDza/+u2CH3RSrWoXo9rtr8NK6A4KJzqGgfxx+H3VlyLgTJ4sRo40T3pIsqpTmiOQEvYzQvBRvQ==",
|
"integrity": "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -198,9 +198,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-arm64": {
|
"node_modules/@biomejs/cli-win32-arm64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.16.tgz",
|
||||||
"integrity": "sha512-Px9PS2B5/Q183bUwy/5VHqp3J2lzdOCeVGzMpphYfl8oSa7VDCqenBdqWpy6DCy/en4Rbf/Y1RieZF6dJPcc9A==",
|
"integrity": "sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -215,9 +215,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@biomejs/cli-win32-x64": {
|
"node_modules/@biomejs/cli-win32-x64": {
|
||||||
"version": "2.4.13",
|
"version": "2.4.16",
|
||||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.16.tgz",
|
||||||
"integrity": "sha512-tTcMkXyBrmHi9BfrD2VNHs/5rYIUKETqsBlYOvSAABwBkJhSDVb5e7wPukftsQbO3WzQkXe6kaztC6WtUOXSoQ==",
|
"integrity": "sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -232,13 +232,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cacheable/memory": {
|
"node_modules/@cacheable/memory": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.9.tgz",
|
||||||
"integrity": "sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==",
|
"integrity": "sha512-HdMx6DoGywB30vacDbBsITbIX4pgFqj1zsrV58jZBUw3klzkNoXhj7qOqAgledhxG7YZI5rBSJg7Zp8/VG0DuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cacheable/utils": "^2.4.0",
|
"@cacheable/utils": "^2.4.1",
|
||||||
"@keyv/bigmap": "^1.3.1",
|
"@keyv/bigmap": "^1.3.1",
|
||||||
"hookified": "^1.15.1",
|
"hookified": "^1.15.1",
|
||||||
"keyv": "^5.6.0"
|
"keyv": "^5.6.0"
|
||||||
@@ -1023,9 +1023,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@oxc-project/types": {
|
"node_modules/@oxc-project/types": {
|
||||||
"version": "0.127.0",
|
"version": "0.133.0",
|
||||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz",
|
||||||
"integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
|
"integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
@@ -1107,9 +1107,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-android-arm64": {
|
"node_modules/@rolldown/binding-android-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==",
|
"integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1124,9 +1124,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==",
|
"integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1141,9 +1141,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-darwin-x64": {
|
"node_modules/@rolldown/binding-darwin-x64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz",
|
||||||
"integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==",
|
"integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1158,9 +1158,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz",
|
||||||
"integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==",
|
"integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1175,9 +1175,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz",
|
||||||
"integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==",
|
"integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -1192,9 +1192,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==",
|
"integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1209,9 +1209,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz",
|
||||||
"integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==",
|
"integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1226,9 +1226,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==",
|
"integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -1243,9 +1243,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==",
|
"integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -1260,9 +1260,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz",
|
||||||
"integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==",
|
"integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1277,9 +1277,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz",
|
||||||
"integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==",
|
"integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1294,9 +1294,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz",
|
||||||
"integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==",
|
"integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1311,9 +1311,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz",
|
||||||
"integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==",
|
"integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"wasm32"
|
"wasm32"
|
||||||
],
|
],
|
||||||
@@ -1330,9 +1330,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz",
|
||||||
"integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==",
|
"integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -1347,9 +1347,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz",
|
||||||
"integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==",
|
"integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -1364,9 +1364,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||||
"integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==",
|
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -1511,9 +1511,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
||||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@@ -1522,13 +1522,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "25.6.0",
|
"version": "25.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||||
"integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==",
|
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.19.0"
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/pluralize": {
|
"node_modules/@types/pluralize": {
|
||||||
@@ -1727,17 +1727,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cacheable": {
|
"node_modules/cacheable": {
|
||||||
"version": "2.3.4",
|
"version": "2.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.5.tgz",
|
||||||
"integrity": "sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew==",
|
"integrity": "sha512-EQfaKe09tl615iNvq/TBRWTFf1AKJNXYQSsMx0Z3EI0nA+pVsVPS8wJhnRlkbdacKPh1d0qVIhwTc2zsQNFEEg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cacheable/memory": "^2.0.8",
|
"@cacheable/memory": "^2.0.8",
|
||||||
"@cacheable/utils": "^2.4.0",
|
"@cacheable/utils": "^2.4.1",
|
||||||
"hookified": "^1.15.0",
|
"hookified": "^1.15.0",
|
||||||
"keyv": "^5.6.0",
|
"keyv": "^5.6.0",
|
||||||
"qified": "^0.9.0"
|
"qified": "^0.10.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/callsites": {
|
"node_modules/callsites": {
|
||||||
@@ -2118,48 +2118,48 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/edge-lexer": {
|
"node_modules/edge-lexer": {
|
||||||
"version": "6.0.4",
|
"version": "6.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/edge-lexer/-/edge-lexer-6.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/edge-lexer/-/edge-lexer-6.0.5.tgz",
|
||||||
"integrity": "sha512-rHlTSZUQfBu/fwnAjoaLCGGmDzpRPgUC8FEqNdJtpPEjBRCqU3a4Le7iJ8KSQfY2WvWx6NTGAwti62xj3eIz1w==",
|
"integrity": "sha512-paSprHn8GRzOUWTVLapgacqDBSOpJgOY60/V5QQ8bbNLHRKUMSxp8Z2oOO0WKtcKmb5+sAlmvG3izhbFulp19A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"edge-error": "^4.0.2"
|
"edge-error": "^4.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.16.0"
|
"node": ">=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/edge-parser": {
|
"node_modules/edge-parser": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/edge-parser/-/edge-parser-9.1.1.tgz",
|
||||||
"integrity": "sha512-Z7sEbRNjjGuUVch3ELHMbjgksVjQlAjUASCwUWe+1I+nJ0mVBmUD2rn6zyes/+EjLssvEGQcIWMjLMNn1ChXgQ==",
|
"integrity": "sha512-kem/vInJgTzHCABdFe060WGjyGva8j23QwVbZkNFXkNAQhRz3mga5kNBuPFYQ3t73nPT2vZ8WOGV4OCS0P16Cw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": "^8.15.0",
|
"acorn": "^8.16.0",
|
||||||
"astring": "^1.9.0",
|
"astring": "^1.9.0",
|
||||||
"edge-error": "^4.0.2",
|
"edge-error": "^4.0.2",
|
||||||
"edge-lexer": "^6.0.4",
|
"edge-lexer": "^6.0.5",
|
||||||
"js-stringify": "^1.0.2"
|
"js-stringify": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.16.0"
|
"node": ">=24.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/edge.js": {
|
"node_modules/edge.js": {
|
||||||
"version": "6.5.0",
|
"version": "6.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/edge.js/-/edge.js-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/edge.js/-/edge.js-6.5.1.tgz",
|
||||||
"integrity": "sha512-WEXNseOSK6n5+Maf6dBPCMgsOuw4mpOqItMniXmdILVCH5PcjQ/CZDfw8IYyMwAjhshoznG+8WjsERy4+56xhA==",
|
"integrity": "sha512-+NgWA8KunEdhVB247GWdmqUZ3MSIExeUCAbWNgNvA0ONH43UL+a/l6AE8GsevT4beqVsbp4Q6USwSpfbnMqBkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@poppinss/inspect": "^1.0.1",
|
"@poppinss/inspect": "^1.0.1",
|
||||||
"@poppinss/macroable": "^1.1.0",
|
"@poppinss/macroable": "^1.1.2",
|
||||||
"@poppinss/utils": "^7.0.0-next.4",
|
"@poppinss/utils": "^7.0.1",
|
||||||
"edge-error": "^4.0.2",
|
"edge-error": "^4.0.2",
|
||||||
"edge-lexer": "^6.0.4",
|
"edge-lexer": "^6.0.5",
|
||||||
"edge-parser": "^9.0.4",
|
"edge-parser": "^9.1.1",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"property-information": "^7.1.0",
|
"property-information": "^7.1.0",
|
||||||
"stringify-attributes": "^4.0.0"
|
"stringify-attributes": "^4.0.0"
|
||||||
@@ -2334,13 +2334,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "11.1.2",
|
"version": "11.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.3.tgz",
|
||||||
"integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==",
|
"integrity": "sha512-oMbq0PD6VIiIwMF6LIa7MEwd/l9huKwmqRKXqmrkqIZv8CvRbfowL+L0ryAl8h//HfAS0zS+4SbYoRyAoA6BJA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flat-cache": "^6.1.20"
|
"flat-cache": "^6.1.22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
@@ -2434,9 +2434,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-east-asian-width": {
|
"node_modules/get-east-asian-width": {
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
|
||||||
"integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
|
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2747,16 +2747,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-plain-object": {
|
|
||||||
"version": "5.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
|
||||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-what": {
|
"node_modules/is-what": {
|
||||||
"version": "4.1.16",
|
"version": "4.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
|
||||||
@@ -3268,9 +3258,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3475,9 +3465,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.10",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3495,7 +3485,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.12",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.1"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
@@ -3627,9 +3617,9 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/qified": {
|
"node_modules/qified": {
|
||||||
"version": "0.9.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/qified/-/qified-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz",
|
||||||
"integrity": "sha512-n7mar4T0xQ+39dE2vGTAlbxUEpndwPANH0kDef1/MYsB8Bba9wshkybIRx74qgcvKQPEWErf9AqAdYjhzY2Ilg==",
|
"integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3640,9 +3630,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qified/node_modules/hookified": {
|
"node_modules/qified/node_modules/hookified": {
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/hookified/-/hookified-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz",
|
||||||
"integrity": "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA==",
|
"integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -3741,14 +3731,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rolldown": {
|
"node_modules/rolldown": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz",
|
||||||
"integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==",
|
"integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oxc-project/types": "=0.127.0",
|
"@oxc-project/types": "=0.133.0",
|
||||||
"@rolldown/pluginutils": "1.0.0-rc.17"
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rolldown": "bin/cli.mjs"
|
"rolldown": "bin/cli.mjs"
|
||||||
@@ -3757,21 +3747,21 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rolldown/binding-android-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-android-arm64": "1.0.3",
|
||||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-darwin-arm64": "1.0.3",
|
||||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.17",
|
"@rolldown/binding-darwin-x64": "1.0.3",
|
||||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.17",
|
"@rolldown/binding-freebsd-x64": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17",
|
"@rolldown/binding-linux-arm64-musl": "1.0.3",
|
||||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-ppc64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-s390x-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17",
|
"@rolldown/binding-linux-x64-gnu": "1.0.3",
|
||||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.17",
|
"@rolldown/binding-linux-x64-musl": "1.0.3",
|
||||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.17",
|
"@rolldown/binding-openharmony-arm64": "1.0.3",
|
||||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.17",
|
"@rolldown/binding-wasm32-wasi": "1.0.3",
|
||||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17",
|
"@rolldown/binding-win32-arm64-msvc": "1.0.3",
|
||||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17"
|
"@rolldown/binding-win32-x64-msvc": "1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/run-parallel": {
|
"node_modules/run-parallel": {
|
||||||
@@ -4010,9 +4000,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
|
||||||
"integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
|
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4059,9 +4049,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint": {
|
"node_modules/stylelint": {
|
||||||
"version": "17.9.1",
|
"version": "17.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.12.0.tgz",
|
||||||
"integrity": "sha512-THTmnAPJTrg/JhkTWZlSyrO+HUYMx6ELthIHeMyD2WOKqXIJUFQv2Yxn91bvUrZdbBJaW2dUuQdPST2wcQ6C3g==",
|
"integrity": "sha512-KIlzWXMHUvgfPUR0R7TK3H80yCIi0uoivUwf+6Az4yrHJD1Q3c1qIkh/H5Z0i/K3QXgtq/UMEkWyBUSUwnpnOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4089,24 +4079,23 @@
|
|||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
"file-entry-cache": "^11.1.2",
|
"file-entry-cache": "^11.1.3",
|
||||||
"global-modules": "^2.0.0",
|
"global-modules": "^2.0.0",
|
||||||
"globby": "^16.2.0",
|
"globby": "^16.2.0",
|
||||||
"globjoin": "^0.1.4",
|
"globjoin": "^0.1.4",
|
||||||
"html-tags": "^5.1.0",
|
"html-tags": "^5.1.0",
|
||||||
"ignore": "^7.0.5",
|
"ignore": "^7.0.5",
|
||||||
"import-meta-resolve": "^4.2.0",
|
"import-meta-resolve": "^4.2.0",
|
||||||
"is-plain-object": "^5.0.0",
|
|
||||||
"mathml-tag-names": "^4.0.0",
|
"mathml-tag-names": "^4.0.0",
|
||||||
"meow": "^14.1.0",
|
"meow": "^14.1.0",
|
||||||
"micromatch": "^4.0.8",
|
"micromatch": "^4.0.8",
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
"postcss": "^8.5.9",
|
"postcss": "^8.5.14",
|
||||||
"postcss-safe-parser": "^7.0.1",
|
"postcss-safe-parser": "^7.0.1",
|
||||||
"postcss-selector-parser": "^7.1.1",
|
"postcss-selector-parser": "^7.1.1",
|
||||||
"postcss-value-parser": "^4.2.0",
|
"postcss-value-parser": "^4.2.0",
|
||||||
"string-width": "^8.2.0",
|
"string-width": "^8.2.1",
|
||||||
"supports-hyperlinks": "^4.4.0",
|
"supports-hyperlinks": "^4.4.0",
|
||||||
"svg-tags": "^1.0.0",
|
"svg-tags": "^1.0.0",
|
||||||
"table": "^6.9.0",
|
"table": "^6.9.0",
|
||||||
@@ -4385,9 +4374,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.16",
|
"version": "0.2.17",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||||
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4478,9 +4467,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.19.2",
|
"version": "7.24.6",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||||
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -4545,17 +4534,17 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "8.0.10",
|
"version": "8.0.16",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz",
|
||||||
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
"integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
"postcss": "^8.5.10",
|
"postcss": "^8.5.15",
|
||||||
"rolldown": "1.0.0-rc.17",
|
"rolldown": "1.0.3",
|
||||||
"tinyglobby": "^0.2.16"
|
"tinyglobby": "^0.2.17"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -4571,7 +4560,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/node": "^20.19.0 || >=22.12.0",
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
"@vitejs/devtools": "^0.1.0",
|
"@vitejs/devtools": "^0.1.18",
|
||||||
"esbuild": "^0.27.0 || ^0.28.0",
|
"esbuild": "^0.27.0 || ^0.28.0",
|
||||||
"jiti": ">=1.21.0",
|
"jiti": ">=1.21.0",
|
||||||
"less": "^4.0.0",
|
"less": "^4.0.0",
|
||||||
|
|||||||
@@ -29,21 +29,21 @@
|
|||||||
"swiped-events": "1.2.0"
|
"swiped-events": "1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.4.13",
|
"@biomejs/biome": "2.4.16",
|
||||||
"@types/node": "^25.6.0",
|
"@types/node": "^25.9.1",
|
||||||
"browserslist": "^4.28.2",
|
"browserslist": "^4.28.2",
|
||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"edge.js": "^6.5.0",
|
"edge.js": "^6.5.1",
|
||||||
"less": "^4.6.4",
|
"less": "^4.6.4",
|
||||||
"mathjs": "^15.2.0",
|
"mathjs": "^15.2.0",
|
||||||
"sharp": "~0.34.5",
|
"sharp": "~0.34.5",
|
||||||
"sort-package-json": "^3.6.1",
|
"sort-package-json": "^3.6.1",
|
||||||
"stylelint": "^17.9.1",
|
"stylelint": "^17.12.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.1",
|
||||||
"typescript": "~6.0.3",
|
"typescript": "~6.0.3",
|
||||||
"vite": "^8.0.10",
|
"vite": "^8.0.16",
|
||||||
"vite-bundle-analyzer": "^1.3.8"
|
"vite-bundle-analyzer": "^1.3.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type { KeyBindingLayout } from "./main/keyboard.ts";
|
|||||||
// synced with searx/webapp.py get_client_settings
|
// synced with searx/webapp.py get_client_settings
|
||||||
type Settings = {
|
type Settings = {
|
||||||
plugins?: string[];
|
plugins?: string[];
|
||||||
advanced_search?: boolean;
|
|
||||||
autocomplete?: string;
|
autocomplete?: string;
|
||||||
autocomplete_min?: number;
|
autocomplete_min?: number;
|
||||||
doi_resolver?: string;
|
doi_resolver?: string;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
@import "mixins.less";
|
@import "mixins.less";
|
||||||
@import "toolkit.less";
|
@import "toolkit.less";
|
||||||
@import "autocomplete.less";
|
@import "autocomplete.less";
|
||||||
@import "detail.less";
|
|
||||||
@import "animations.less";
|
@import "animations.less";
|
||||||
@import "embedded.less";
|
@import "embedded.less";
|
||||||
@import "info.less";
|
@import "info.less";
|
||||||
@@ -1165,3 +1164,4 @@ pre code {
|
|||||||
@import "result_types/code.less";
|
@import "result_types/code.less";
|
||||||
@import "result_types/paper.less";
|
@import "result_types/paper.less";
|
||||||
@import "result_types/file.less";
|
@import "result_types/file.less";
|
||||||
|
@import "result_types/image.less";
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Settings
|
|||||||
settings_search
|
settings_search
|
||||||
settings_server
|
settings_server
|
||||||
settings_ui
|
settings_ui
|
||||||
|
settings_preferences
|
||||||
settings_redis
|
settings_redis
|
||||||
settings_valkey
|
settings_valkey
|
||||||
settings_outgoing
|
settings_outgoing
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.. _settings preferences:
|
||||||
|
|
||||||
|
================
|
||||||
|
``preferences:``
|
||||||
|
================
|
||||||
|
|
||||||
|
.. autoclass:: searx._settings.SettingsPref
|
||||||
|
:members:
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
activated:
|
activated:
|
||||||
|
|
||||||
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
|
- :py:obj:`searx.botdetection.link_token` in the :ref:`limiter`
|
||||||
|
- :ref:`image_proxy`
|
||||||
|
|
||||||
.. _image_proxy:
|
.. _image_proxy:
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ ${fedora_build}
|
|||||||
$ sudo -H -u ${SERVICE_USER} -i
|
$ sudo -H -u ${SERVICE_USER} -i
|
||||||
(${SERVICE_USER})$ cd ${SEARXNG_SRC}
|
(${SERVICE_USER})$ cd ${SEARXNG_SRC}
|
||||||
(${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\"
|
(${SERVICE_USER})$ export SEARXNG_SETTINGS_PATH=\"${SEARXNG_SETTINGS_PATH}\"
|
||||||
(${SERVICE_USER})$ python searx/webapp.py
|
(${SERVICE_USER})$ python -m searx.webapp
|
||||||
|
|
||||||
# disable debug
|
# disable debug
|
||||||
$ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\"
|
$ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARXNG_SETTINGS_PATH\"
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ module:
|
|||||||
======================= =========== ===========================================
|
======================= =========== ===========================================
|
||||||
base_url string base-url, can be overwritten to use same
|
base_url string base-url, can be overwritten to use same
|
||||||
engine on other URL
|
engine on other URL
|
||||||
number_of_results int maximum number of results per request
|
page_size int maximum number of results per request
|
||||||
language string ISO code of language and country like en_US
|
language string ISO code of language and country like en_US
|
||||||
api_key string api-key if required by engine
|
api_key string api-key if required by engine
|
||||||
======================= =========== ===========================================
|
======================= =========== ===========================================
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.. _500px engine:
|
||||||
|
|
||||||
|
=====
|
||||||
|
500px
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. automodule:: searx.engines.500px
|
||||||
|
:members:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.. _cara engine:
|
||||||
|
|
||||||
|
===========
|
||||||
|
Cara Images
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. automodule:: searx.engines.cara
|
||||||
|
:members:
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
.. _karmasearch engine:
|
|
||||||
|
|
||||||
===========
|
|
||||||
Karmasearch
|
|
||||||
===========
|
|
||||||
|
|
||||||
.. automodule:: searx.engines.karmasearch
|
|
||||||
:members:
|
|
||||||
+19
-1
@@ -60,11 +60,29 @@ into the developer environment and start a python based HTTP server by::
|
|||||||
|
|
||||||
$ ./manage dev.env
|
$ ./manage dev.env
|
||||||
...
|
...
|
||||||
(dev.env)$ SEARXNG_DEBUG=1 python -m searx.webapp
|
(dev.env)$ SEARXNG_DEBUG=1 searxng-run
|
||||||
|
|
||||||
Since this is a pure Python solution, you can set breakpoints in your code with
|
Since this is a pure Python solution, you can set breakpoints in your code with
|
||||||
``pdb.set_trace()`` and the debugger will wait for you in the terminal prompt.
|
``pdb.set_trace()`` and the debugger will wait for you in the terminal prompt.
|
||||||
|
|
||||||
|
Any other script or command line provided by SearXNG can also be used in the
|
||||||
|
same environment, here are a few examples::
|
||||||
|
|
||||||
|
# tools related to favicons
|
||||||
|
(dev.env)$ python -m searx.favicons
|
||||||
|
|
||||||
|
# tools related to DATA stored in searx/data
|
||||||
|
(dev.env)$ python -m searx.data --help
|
||||||
|
|
||||||
|
# tools related to engines
|
||||||
|
(dev.env)$ python -m searx.enginelib --help
|
||||||
|
|
||||||
|
# to test one of the update scripts
|
||||||
|
(dev.env)$ searxng_extra/update/update_engine_traits.py --help
|
||||||
|
|
||||||
|
# to test the update of the wikidata units
|
||||||
|
(dev.env)$ searxng_extra/update/update_wikidata_units.py
|
||||||
|
|
||||||
|
|
||||||
.. sidebar:: further read
|
.. sidebar:: further read
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
.. _result_types.image:
|
||||||
|
|
||||||
|
=============
|
||||||
|
Image Results
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. automodule:: searx.result_types.image
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
.. _result_types.mainresult:
|
.. _result_types.mainresult:
|
||||||
|
|
||||||
|
============
|
||||||
|
Main Results
|
||||||
|
============
|
||||||
|
|
||||||
.. autoclass:: searx.result_types._base.MainResult
|
.. autoclass:: searx.result_types._base.MainResult
|
||||||
:members:
|
:members:
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ following types have been implemented so far ..
|
|||||||
main/code
|
main/code
|
||||||
main/paper
|
main/paper
|
||||||
main/file
|
main/file
|
||||||
|
main/image
|
||||||
|
|
||||||
The :ref:`LegacyResult <LegacyResult>` is used internally for the results that
|
The :ref:`LegacyResult <LegacyResult>` is used internally for the results that
|
||||||
have not yet been typed. The templates can be used as orientation until the
|
have not yet been typed. The templates can be used as orientation until the
|
||||||
final typing is complete.
|
final typing is complete.
|
||||||
|
|
||||||
- :ref:`template default` / :py:obj:`Result`
|
- :ref:`template default` / :py:obj:`Result`
|
||||||
- :ref:`template images`
|
|
||||||
- :ref:`template videos`
|
- :ref:`template videos`
|
||||||
- :ref:`template torrent`
|
- :ref:`template torrent`
|
||||||
- :ref:`template map`
|
- :ref:`template map`
|
||||||
|
|||||||
@@ -129,53 +129,6 @@ audio_src : uri,
|
|||||||
URL of an embedded ``<audio controls>``.
|
URL of an embedded ``<audio controls>``.
|
||||||
|
|
||||||
|
|
||||||
.. _template images:
|
|
||||||
|
|
||||||
``images.html``
|
|
||||||
---------------
|
|
||||||
|
|
||||||
The images are displayed as small thumbnails in the main results list.
|
|
||||||
|
|
||||||
title : :py:class:`str`
|
|
||||||
Title of the image.
|
|
||||||
|
|
||||||
thumbnail_src : :py:class:`str`
|
|
||||||
URL of a preview of the image.
|
|
||||||
|
|
||||||
resolution :py:class:`str`
|
|
||||||
The resolution of the image (e.g. ``1920 x 1080`` pixel)
|
|
||||||
|
|
||||||
|
|
||||||
Image labels
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Clicking on the preview opens a gallery view in which all further metadata for
|
|
||||||
the image is displayed. Addition fields used in the :origin:`images.html
|
|
||||||
<searx/templates/simple/result_templates/images.html>`:
|
|
||||||
|
|
||||||
img_src : :py:class:`str`
|
|
||||||
URL of the full size image.
|
|
||||||
|
|
||||||
content: :py:class:`str`
|
|
||||||
Description of the image.
|
|
||||||
|
|
||||||
author: :py:class:`str`
|
|
||||||
Name of the author of the image.
|
|
||||||
|
|
||||||
img_format : :py:class:`str`
|
|
||||||
The format of the image (e.g. ``png``).
|
|
||||||
|
|
||||||
source : :py:class:`str`
|
|
||||||
Source of the image.
|
|
||||||
|
|
||||||
filesize: :py:class:`str`
|
|
||||||
Size of bytes in :py:obj:`human readable <searx.humanize_bytes>` notation
|
|
||||||
(e.g. ``MB`` for 1024 \* 1024 Bytes filesize).
|
|
||||||
|
|
||||||
url : :py:class:`str`
|
|
||||||
URL of the page from where the images comes from (source).
|
|
||||||
|
|
||||||
|
|
||||||
.. _template videos:
|
.. _template videos:
|
||||||
|
|
||||||
``videos.html``
|
``videos.html``
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ cov-core==1.15.0
|
|||||||
black==25.9.0
|
black==25.9.0
|
||||||
pylint==4.0.5
|
pylint==4.0.5
|
||||||
splinter==0.21.0
|
splinter==0.21.0
|
||||||
selenium==4.43.0
|
selenium==4.44.0
|
||||||
Sphinx==8.2.3;python_version <= "3.11"
|
Sphinx==8.2.3;python_version <= "3.11"
|
||||||
Sphinx==9.1.0; python_version > "3.11"
|
Sphinx==9.1.0; python_version > "3.11"
|
||||||
sphinx-issues==6.0.0
|
sphinx-issues==6.0.0
|
||||||
@@ -23,6 +23,6 @@ coloredlogs==15.0.1
|
|||||||
docutils>=0.21.2;python_version <= "3.11"
|
docutils>=0.21.2;python_version <= "3.11"
|
||||||
docutils>=0.22.4; python_version > "3.11"
|
docutils>=0.22.4; python_version > "3.11"
|
||||||
parameterized==0.9.0
|
parameterized==0.9.0
|
||||||
granian[reload]==2.7.4
|
granian[reload]==2.7.6
|
||||||
basedpyright==1.39.3
|
basedpyright==1.39.7
|
||||||
types-lxml==2026.2.16
|
types-lxml==2026.2.16
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
granian==2.7.4
|
granian==2.7.6
|
||||||
granian[pname]==2.7.4
|
granian[pname]==2.7.6
|
||||||
|
|||||||
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
certifi==2026.4.22
|
certifi==2026.5.20
|
||||||
babel==2.18.0
|
babel==2.18.0
|
||||||
flask-babel==4.0.0
|
flask-babel==4.0.0
|
||||||
flask==3.1.3
|
flask==3.1.3
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
lxml==6.1.0
|
lxml==6.1.1
|
||||||
pygments==2.20.0
|
pygments==2.20.0
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
pyyaml==6.0.3
|
pyyaml==6.0.3
|
||||||
@@ -11,9 +11,9 @@ httpx[http2]==0.28.1
|
|||||||
httpx-socks[asyncio]==0.10.0
|
httpx-socks[asyncio]==0.10.0
|
||||||
sniffio==1.3.1
|
sniffio==1.3.1
|
||||||
valkey==6.1.1
|
valkey==6.1.1
|
||||||
markdown-it-py==4.0.0
|
markdown-it-py==4.2.0
|
||||||
msgspec==0.21.1
|
msgspec==0.21.1
|
||||||
typer==0.25.1
|
typer==0.26.7
|
||||||
isodate==0.7.2
|
isodate==0.7.2
|
||||||
whitenoise==6.12.0
|
whitenoise==6.12.0
|
||||||
typing-extensions==4.15.0
|
typing-extensions==4.15.0
|
||||||
|
|||||||
+8
-1
@@ -10,6 +10,7 @@ from os.path import dirname, abspath
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import msgspec
|
import msgspec
|
||||||
|
from ._settings import SettingsPref
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
LOG_FORMAT_DEBUG: str = '%(levelname)-7s %(name)-30.30s: %(message)s'
|
LOG_FORMAT_DEBUG: str = '%(levelname)-7s %(name)-30.30s: %(message)s'
|
||||||
@@ -47,6 +48,12 @@ def init_settings():
|
|||||||
settings.clear()
|
settings.clear()
|
||||||
settings.update(cfg)
|
settings.update(cfg)
|
||||||
|
|
||||||
|
if get_setting("server.public_instance"):
|
||||||
|
# enable image proxy for public instances #6125
|
||||||
|
settings["server"]["image_proxy"] = True
|
||||||
|
pref: SettingsPref = get_setting("preferences")
|
||||||
|
pref.lock.add("image_proxy")
|
||||||
|
|
||||||
sxng_debug = get_setting("general.debug")
|
sxng_debug = get_setting("general.debug")
|
||||||
if sxng_debug:
|
if sxng_debug:
|
||||||
_logging_config_debug()
|
_logging_config_debug()
|
||||||
@@ -66,7 +73,7 @@ def init_settings():
|
|||||||
if settings['server']['public_instance']:
|
if settings['server']['public_instance']:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Be aware you have activated features intended only for public instances. "
|
"Be aware you have activated features intended only for public instances. "
|
||||||
"This force the usage of the limiter and link_token / "
|
"This force the usage of the limiter, link_token and image proxy / "
|
||||||
"see https://docs.searxng.org/admin/searx.limiter.html"
|
"see https://docs.searxng.org/admin/searx.limiter.html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Implementation of the :py:obj:`preference <searx.preference>` settings."""
|
||||||
|
# pylint: disable = too-few-public-methods
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
import msgspec
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsPref(msgspec.Struct, kw_only=True, forbid_unknown_fields=True):
|
||||||
|
"""Options for configuring the preferences
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
preferences:
|
||||||
|
lock:
|
||||||
|
- favicon_resolver
|
||||||
|
- image_proxy
|
||||||
|
- method
|
||||||
|
# ...
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
lock: set[
|
||||||
|
t.Literal[
|
||||||
|
"categories",
|
||||||
|
"language",
|
||||||
|
"locale",
|
||||||
|
"autocomplete",
|
||||||
|
"favicon_resolver",
|
||||||
|
"image_proxy",
|
||||||
|
"method",
|
||||||
|
"safesearch",
|
||||||
|
"theme",
|
||||||
|
"results_on_new_tab",
|
||||||
|
"doi_resolver",
|
||||||
|
"simple_style",
|
||||||
|
"center_alignment",
|
||||||
|
"query_in_title",
|
||||||
|
"search_on_category_select",
|
||||||
|
]
|
||||||
|
] = set()
|
||||||
|
"""Lock arbitrary settings on the preferences page."""
|
||||||
+15
-6
@@ -114,7 +114,13 @@ class ExpireCacheStats:
|
|||||||
if expire:
|
if expire:
|
||||||
valid_until = datetime.datetime.fromtimestamp(expire).strftime("%Y-%m-%d %H:%M:%S")
|
valid_until = datetime.datetime.fromtimestamp(expire).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
c_kv += 1
|
c_kv += 1
|
||||||
lines.append(f"[{ctx_name:20s}] {valid_until} {key:12}" f" --> ({type(value).__name__}) {value} ")
|
value_str = str(value)
|
||||||
|
if len(value_str) > 120:
|
||||||
|
value_str = f"{value_str[:120]} ..."
|
||||||
|
lines.append(
|
||||||
|
f"[{ctx_name:20s}] {valid_until} {key:12}"
|
||||||
|
f" --> ({type(value).__name__}:{len(value)}) {value_str} "
|
||||||
|
)
|
||||||
|
|
||||||
lines.append(f"Number of contexts: {c_ctx}")
|
lines.append(f"Number of contexts: {c_ctx}")
|
||||||
lines.append(f"number of key/value pairs: {c_kv}")
|
lines.append(f"number of key/value pairs: {c_kv}")
|
||||||
@@ -438,12 +444,10 @@ class ExpireCacheSQLite(sqlitedb.SQLiteAppl, ExpireCache):
|
|||||||
def get(self, key: str, default: typing.Any = None, ctx: str | None = None) -> typing.Any:
|
def get(self, key: str, default: typing.Any = None, ctx: str | None = None) -> typing.Any:
|
||||||
"""Get value of ``key`` from table given by argument ``ctx``. If
|
"""Get value of ``key`` from table given by argument ``ctx``. If
|
||||||
``ctx`` argument is ``None`` (the default), a table name is generated
|
``ctx`` argument is ``None`` (the default), a table name is generated
|
||||||
from the :py:obj:`ExpireCacheCfg.name`. If ``key`` not exists (in
|
from the :py:obj:`ExpireCacheCfg.name`. If ``key`` not exists in
|
||||||
table), the ``default`` value is returned.
|
the table or the table not exists, the ``default`` value is returned.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
table = ctx
|
table = ctx
|
||||||
self.maintenance()
|
|
||||||
|
|
||||||
if not table:
|
if not table:
|
||||||
table = self.normalize_name(self.cfg.name)
|
table = self.normalize_name(self.cfg.name)
|
||||||
@@ -451,6 +455,9 @@ class ExpireCacheSQLite(sqlitedb.SQLiteAppl, ExpireCache):
|
|||||||
if table not in self.table_names:
|
if table not in self.table_names:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
# Before values are taken from the table, a maintenance interval may
|
||||||
|
# need to be carried out.
|
||||||
|
self.maintenance()
|
||||||
sql = f"SELECT value FROM {table} WHERE key = ?"
|
sql = f"SELECT value 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:
|
||||||
@@ -463,12 +470,14 @@ class ExpireCacheSQLite(sqlitedb.SQLiteAppl, ExpireCache):
|
|||||||
If ``ctx`` argument is ``None`` (the default), a table name is
|
If ``ctx`` argument is ``None`` (the default), a table name is
|
||||||
generated from the :py:obj:`ExpireCacheCfg.name`."""
|
generated from the :py:obj:`ExpireCacheCfg.name`."""
|
||||||
table = ctx
|
table = ctx
|
||||||
self.maintenance()
|
|
||||||
|
|
||||||
if not table:
|
if not table:
|
||||||
table = self.normalize_name(self.cfg.name)
|
table = self.normalize_name(self.cfg.name)
|
||||||
|
|
||||||
if table in self.table_names:
|
if table in self.table_names:
|
||||||
|
# Before values are taken from the table, a maintenance interval may
|
||||||
|
# need to be carried out.
|
||||||
|
self.maintenance()
|
||||||
for row in self.DB.execute(f"SELECT key, value FROM {table}"):
|
for row in self.DB.execute(f"SELECT key, value FROM {table}"):
|
||||||
yield row[0], self.deserialize(row[1])
|
yield row[0], self.deserialize(row[1])
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+61
-23
@@ -84,6 +84,7 @@
|
|||||||
"sr": "УАЕ дирхам",
|
"sr": "УАЕ дирхам",
|
||||||
"sv": "Emiratisk dirham",
|
"sv": "Emiratisk dirham",
|
||||||
"ta": "ஐக்கிய அரபு அமீரக திர்கம்",
|
"ta": "ஐக்கிய அரபு அமீரக திர்கம்",
|
||||||
|
"te": "యూఏఈ దిర్హామ్",
|
||||||
"tr": "Birleşik Arap Emirlikleri dirhemi",
|
"tr": "Birleşik Arap Emirlikleri dirhemi",
|
||||||
"tt": "БГӘ дирһәме",
|
"tt": "БГӘ дирһәме",
|
||||||
"uk": "дирхам ОАЕ"
|
"uk": "дирхам ОАЕ"
|
||||||
@@ -165,7 +166,7 @@
|
|||||||
"pap": "lek albanes",
|
"pap": "lek albanes",
|
||||||
"pl": "lek",
|
"pl": "lek",
|
||||||
"pt": "lek",
|
"pt": "lek",
|
||||||
"ro": "Lek",
|
"ro": "lek albanez",
|
||||||
"ru": "албанский лек",
|
"ru": "албанский лек",
|
||||||
"sk": "Albánsky lek",
|
"sk": "Albánsky lek",
|
||||||
"sl": "albanski lek",
|
"sl": "albanski lek",
|
||||||
@@ -738,6 +739,7 @@
|
|||||||
"sr": "брунејски долар",
|
"sr": "брунејски долар",
|
||||||
"sv": "Bruneisk dollar",
|
"sv": "Bruneisk dollar",
|
||||||
"ta": "புரூணை டாலர்",
|
"ta": "புரூணை டாலர்",
|
||||||
|
"te": "బ్రూనై డాలర్",
|
||||||
"th": "ดอลลาร์บรูไน",
|
"th": "ดอลลาร์บรูไน",
|
||||||
"tr": "Brunei doları",
|
"tr": "Brunei doları",
|
||||||
"tt": "Бруней доллары",
|
"tt": "Бруней доллары",
|
||||||
@@ -911,6 +913,7 @@
|
|||||||
"sr": "бутански нгултрум",
|
"sr": "бутански нгултрум",
|
||||||
"sv": "Ngultrum",
|
"sv": "Ngultrum",
|
||||||
"ta": "பூட்டானின் இங்குல்ட்ரம்",
|
"ta": "பூட்டானின் இங்குல்ட்ரம்",
|
||||||
|
"te": "భూటానీస్ గుల్త్రమ్",
|
||||||
"th": "งุลตรัมภูฏาน",
|
"th": "งุลตรัมภูฏาน",
|
||||||
"tr": "Ngultrum",
|
"tr": "Ngultrum",
|
||||||
"tt": "ңгултрум",
|
"tt": "ңгултрум",
|
||||||
@@ -989,7 +992,7 @@
|
|||||||
"pa": "ਬੈਲਾਰੂਸੀ ਰੂਬਲ",
|
"pa": "ਬੈਲਾਰੂਸੀ ਰੂਬਲ",
|
||||||
"pl": "Rubel białoruski",
|
"pl": "Rubel białoruski",
|
||||||
"pt": "Rublo bielorrusso",
|
"pt": "Rublo bielorrusso",
|
||||||
"ro": "Rublă belarusă",
|
"ro": "rublă belarusă",
|
||||||
"ru": "Белорусский рубль",
|
"ru": "Белорусский рубль",
|
||||||
"sk": "Bieloruský rubeľ",
|
"sk": "Bieloruský rubeľ",
|
||||||
"sl": "beloruski rubelj",
|
"sl": "beloruski rubelj",
|
||||||
@@ -1083,6 +1086,7 @@
|
|||||||
"sr": "канадски долар",
|
"sr": "канадски долар",
|
||||||
"sv": "kanadensisk dollar",
|
"sv": "kanadensisk dollar",
|
||||||
"ta": "கனடா டொலர்",
|
"ta": "கனடா டொலர்",
|
||||||
|
"te": "కెనడియన్ డాలర్",
|
||||||
"th": "ดอลลาร์แคนาดา",
|
"th": "ดอลลาร์แคนาดา",
|
||||||
"tr": "Kanada doları",
|
"tr": "Kanada doları",
|
||||||
"tt": "Канада дуллыры",
|
"tt": "Канада дуллыры",
|
||||||
@@ -1231,6 +1235,7 @@
|
|||||||
"sl": "čilski peso",
|
"sl": "čilski peso",
|
||||||
"sr": "чилеански пезос",
|
"sr": "чилеански пезос",
|
||||||
"sv": "Chilensk peso",
|
"sv": "Chilensk peso",
|
||||||
|
"te": "చిలీ పెసో",
|
||||||
"th": "เปโซชิลี",
|
"th": "เปโซชิลี",
|
||||||
"tr": "Şili pesosu",
|
"tr": "Şili pesosu",
|
||||||
"tt": "Чили песосы",
|
"tt": "Чили песосы",
|
||||||
@@ -1284,6 +1289,7 @@
|
|||||||
"sr": "ренминби",
|
"sr": "ренминби",
|
||||||
"sv": "Renminbi",
|
"sv": "Renminbi",
|
||||||
"ta": "ரென்மின்பி",
|
"ta": "ரென்மின்பி",
|
||||||
|
"te": "రెన్మిన్బి",
|
||||||
"th": "เหรินหมินปี้",
|
"th": "เหรินหมินปี้",
|
||||||
"tr": "Renminbi",
|
"tr": "Renminbi",
|
||||||
"tt": "юән",
|
"tt": "юән",
|
||||||
@@ -1702,7 +1708,7 @@
|
|||||||
"vi": "Bảng Ai Cập"
|
"vi": "Bảng Ai Cập"
|
||||||
},
|
},
|
||||||
"ERN": {
|
"ERN": {
|
||||||
"ar": "ناكفا",
|
"ar": "نقفة",
|
||||||
"ca": "nakfa",
|
"ca": "nakfa",
|
||||||
"cs": "Eritrejská nakfa",
|
"cs": "Eritrejská nakfa",
|
||||||
"da": "Nakfa",
|
"da": "Nakfa",
|
||||||
@@ -1776,11 +1782,11 @@
|
|||||||
"bg": "евро",
|
"bg": "евро",
|
||||||
"bn": "ইউরো",
|
"bn": "ইউরো",
|
||||||
"ca": "euro",
|
"ca": "euro",
|
||||||
"cs": "euro",
|
"cs": "Euro",
|
||||||
"cy": "Ewro",
|
"cy": "Ewro",
|
||||||
"da": "Euro",
|
"da": "Euro",
|
||||||
"de": "Euro",
|
"de": "Euro",
|
||||||
"en": "euro",
|
"en": "Euro",
|
||||||
"eo": "eŭro",
|
"eo": "eŭro",
|
||||||
"es": "Euro",
|
"es": "Euro",
|
||||||
"et": "Euro",
|
"et": "Euro",
|
||||||
@@ -1794,7 +1800,7 @@
|
|||||||
"hu": "euró",
|
"hu": "euró",
|
||||||
"ia": "Euro",
|
"ia": "Euro",
|
||||||
"id": "Euro",
|
"id": "Euro",
|
||||||
"it": "euro",
|
"it": "Euro",
|
||||||
"ja": "ユーロ",
|
"ja": "ユーロ",
|
||||||
"ko": "유로",
|
"ko": "유로",
|
||||||
"lt": "Euras",
|
"lt": "Euras",
|
||||||
@@ -1805,11 +1811,11 @@
|
|||||||
"oc": "Èuro",
|
"oc": "Èuro",
|
||||||
"pa": "ਯੂਰੋ",
|
"pa": "ਯੂਰੋ",
|
||||||
"pap": "Euro",
|
"pap": "Euro",
|
||||||
"pl": "euro",
|
"pl": "Euro",
|
||||||
"pt": "Euro",
|
"pt": "Euro",
|
||||||
"ro": "euro",
|
"ro": "Euro",
|
||||||
"ru": "евро",
|
"ru": "евро",
|
||||||
"sk": "euro",
|
"sk": "Euro",
|
||||||
"sl": "evro",
|
"sl": "evro",
|
||||||
"sr": "евро",
|
"sr": "евро",
|
||||||
"sv": "Euro",
|
"sv": "Euro",
|
||||||
@@ -1935,7 +1941,7 @@
|
|||||||
"sk": "libra šterlingov",
|
"sk": "libra šterlingov",
|
||||||
"sl": "funt šterling",
|
"sl": "funt šterling",
|
||||||
"sr": "британска фунта",
|
"sr": "британска фунта",
|
||||||
"sv": "Brittiskt pund",
|
"sv": "brittiskt pund",
|
||||||
"ta": "பிரித்தானிய பவுண்டு",
|
"ta": "பிரித்தானிய பவுண்டு",
|
||||||
"th": "ปอนด์สเตอร์ลิง",
|
"th": "ปอนด์สเตอร์ลิง",
|
||||||
"tr": "İngiliz sterlini",
|
"tr": "İngiliz sterlini",
|
||||||
@@ -2410,6 +2416,7 @@
|
|||||||
"pl": "rupia indonezyjska",
|
"pl": "rupia indonezyjska",
|
||||||
"pt": "rupia indonésia",
|
"pt": "rupia indonésia",
|
||||||
"ru": "индонезийская рупия",
|
"ru": "индонезийская рупия",
|
||||||
|
"sk": "Indonézska rupia",
|
||||||
"sl": "indonezijska rupija",
|
"sl": "indonezijska rupija",
|
||||||
"sr": "индонежанска рупија",
|
"sr": "индонежанска рупија",
|
||||||
"sv": "Rupiah",
|
"sv": "Rupiah",
|
||||||
@@ -2463,6 +2470,7 @@
|
|||||||
"sr": "нови израелски шекел",
|
"sr": "нови израелски шекел",
|
||||||
"sv": "Shekel",
|
"sv": "Shekel",
|
||||||
"ta": "புது இசுரேலிய சேக்கல்",
|
"ta": "புது இசுரேலிய சேக்கல்",
|
||||||
|
"te": "ఇజ్రాయెల్ షెకెల్",
|
||||||
"tr": "Yeni İsrail Şekeli",
|
"tr": "Yeni İsrail Şekeli",
|
||||||
"tt": "Исраил шекеле",
|
"tt": "Исраил шекеле",
|
||||||
"uk": "ізраїльський новий шекель"
|
"uk": "ізраїльський новий шекель"
|
||||||
@@ -2758,6 +2766,7 @@
|
|||||||
"sr": "јапански јен",
|
"sr": "јапански јен",
|
||||||
"sv": "yen",
|
"sv": "yen",
|
||||||
"ta": "யென்",
|
"ta": "யென்",
|
||||||
|
"te": "జపనీస్ యెన్",
|
||||||
"th": "เยน",
|
"th": "เยน",
|
||||||
"tr": "Japon yeni",
|
"tr": "Japon yeni",
|
||||||
"tt": "япон иенасы",
|
"tt": "япон иенасы",
|
||||||
@@ -2823,6 +2832,7 @@
|
|||||||
"ja": "キルギス・ソム",
|
"ja": "キルギス・ソム",
|
||||||
"ko": "키르기스스탄 솜",
|
"ko": "키르기스스탄 솜",
|
||||||
"lt": "somas",
|
"lt": "somas",
|
||||||
|
"lv": "Kirgizstānas soms",
|
||||||
"nl": "Kirgizische som",
|
"nl": "Kirgizische som",
|
||||||
"pa": "ਕਿਰਗਿਜ਼ਸਤਾਨੀ ਸੋਮ",
|
"pa": "ਕਿਰਗਿਜ਼ਸਤਾਨੀ ਸੋਮ",
|
||||||
"pl": "som",
|
"pl": "som",
|
||||||
@@ -3236,6 +3246,7 @@
|
|||||||
"sr": "шриланчанска рупија",
|
"sr": "шриланчанска рупија",
|
||||||
"sv": "Lankesisk rupie",
|
"sv": "Lankesisk rupie",
|
||||||
"ta": "இலங்கை ரூபாய்",
|
"ta": "இலங்கை ரூபாய்",
|
||||||
|
"te": "శ్రీలంక రూపాయి",
|
||||||
"tr": "Sri Lanka rupisi",
|
"tr": "Sri Lanka rupisi",
|
||||||
"tt": "Шри-Ланка рупиясе",
|
"tt": "Шри-Ланка рупиясе",
|
||||||
"uk": "ланкійська рупія",
|
"uk": "ланкійська рупія",
|
||||||
@@ -3810,6 +3821,7 @@
|
|||||||
"MXV": {
|
"MXV": {
|
||||||
"de": "UNIDAD DE INVERSION",
|
"de": "UNIDAD DE INVERSION",
|
||||||
"en": "unidad de inversión",
|
"en": "unidad de inversión",
|
||||||
|
"eo": "Meksika unuo de investo",
|
||||||
"es": "Unidades de Inversión",
|
"es": "Unidades de Inversión",
|
||||||
"ja": "メキシコ投資単位"
|
"ja": "メキシコ投資単位"
|
||||||
},
|
},
|
||||||
@@ -3901,7 +3913,7 @@
|
|||||||
"de": "Namibia-Dollar",
|
"de": "Namibia-Dollar",
|
||||||
"en": "Namibian dollar",
|
"en": "Namibian dollar",
|
||||||
"eo": "namibia dolaro",
|
"eo": "namibia dolaro",
|
||||||
"es": "Dólar namibio",
|
"es": "dólar namibio",
|
||||||
"fi": "Namibian dollari",
|
"fi": "Namibian dollari",
|
||||||
"fr": "Dollar namibien",
|
"fr": "Dollar namibien",
|
||||||
"ga": "dollar na Namaibe",
|
"ga": "dollar na Namaibe",
|
||||||
@@ -4090,6 +4102,7 @@
|
|||||||
"sr": "непалска рупија",
|
"sr": "непалска рупија",
|
||||||
"sv": "Nepalesisk rupee",
|
"sv": "Nepalesisk rupee",
|
||||||
"ta": "நேபாள ரூபாய்",
|
"ta": "நேபாள ரூபாய்",
|
||||||
|
"te": "నేపాలీ రూపాయి",
|
||||||
"th": "รูปีเนปาล",
|
"th": "รูปีเนปาล",
|
||||||
"tr": "Nepal rupisi",
|
"tr": "Nepal rupisi",
|
||||||
"tt": "Непал рупиясе",
|
"tt": "Непал рупиясе",
|
||||||
@@ -4325,6 +4338,7 @@
|
|||||||
"sr": "филипински пезо",
|
"sr": "филипински пезо",
|
||||||
"sv": "Filippinsk peso",
|
"sv": "Filippinsk peso",
|
||||||
"ta": "பிலிப்பைன் பெசோ",
|
"ta": "பிலிப்பைன் பெசோ",
|
||||||
|
"te": "ఫిలిప్పీన్ పెసో",
|
||||||
"th": "เปโซฟิลิปปินส์",
|
"th": "เปโซฟิลิปปินส์",
|
||||||
"tr": "Filipinler pesosu",
|
"tr": "Filipinler pesosu",
|
||||||
"tt": "Филипин писысы",
|
"tt": "Филипин писысы",
|
||||||
@@ -4370,6 +4384,7 @@
|
|||||||
"sr": "пакистанска рупија",
|
"sr": "пакистанска рупија",
|
||||||
"sv": "Pakistansk rupee",
|
"sv": "Pakistansk rupee",
|
||||||
"ta": "பாக்கித்தானிய ரூபாய்",
|
"ta": "பாக்கித்தானிய ரூபாய்",
|
||||||
|
"te": "పాకిస్థానీ రూపాయి",
|
||||||
"tr": "Pakistan rupisi",
|
"tr": "Pakistan rupisi",
|
||||||
"tt": "Пакстан рупиясе",
|
"tt": "Пакстан рупиясе",
|
||||||
"uk": "пакистанська рупія",
|
"uk": "пакистанська рупія",
|
||||||
@@ -4549,7 +4564,7 @@
|
|||||||
"de": "rumänischer Leu",
|
"de": "rumänischer Leu",
|
||||||
"en": "Romanian Leu",
|
"en": "Romanian Leu",
|
||||||
"eo": "rumana leo",
|
"eo": "rumana leo",
|
||||||
"es": "Leu rumano",
|
"es": "leu rumano",
|
||||||
"et": "Rumeenia leu",
|
"et": "Rumeenia leu",
|
||||||
"fi": "Romanian leu",
|
"fi": "Romanian leu",
|
||||||
"fr": "leu roumain",
|
"fr": "leu roumain",
|
||||||
@@ -4750,6 +4765,7 @@
|
|||||||
"sr": "саудијски ријал",
|
"sr": "саудијски ријал",
|
||||||
"sv": "Saudiarabisk rial",
|
"sv": "Saudiarabisk rial",
|
||||||
"ta": "சவூதி ரியால்",
|
"ta": "சவூதி ரியால்",
|
||||||
|
"te": "సౌదీ రియాల్",
|
||||||
"tr": "Suudi Arabistan riyali",
|
"tr": "Suudi Arabistan riyali",
|
||||||
"tt": "Согуд риялы",
|
"tt": "Согуд риялы",
|
||||||
"uk": "саудівський ріал",
|
"uk": "саудівський ріал",
|
||||||
@@ -4948,6 +4964,7 @@
|
|||||||
"sr": "сингапурски долар",
|
"sr": "сингапурски долар",
|
||||||
"sv": "Singaporiansk dollar",
|
"sv": "Singaporiansk dollar",
|
||||||
"ta": "சிங்கப்பூர் வெள்ளி",
|
"ta": "சிங்கப்பூர் வெள்ளி",
|
||||||
|
"te": "సింగపూర్ డాలర్",
|
||||||
"th": "ดอลลาร์สิงคโปร์",
|
"th": "ดอลลาร์สิงคโปร์",
|
||||||
"tr": "Singapur doları",
|
"tr": "Singapur doları",
|
||||||
"tt": "Сингапур доллары",
|
"tt": "Сингапур доллары",
|
||||||
@@ -5175,7 +5192,7 @@
|
|||||||
"de": "syrische Lira",
|
"de": "syrische Lira",
|
||||||
"en": "Syrian pound",
|
"en": "Syrian pound",
|
||||||
"eo": "siria pundo",
|
"eo": "siria pundo",
|
||||||
"es": "Dolar sirio",
|
"es": "libra siria",
|
||||||
"fi": "Syyrian punta",
|
"fi": "Syyrian punta",
|
||||||
"fr": "livre syrienne",
|
"fr": "livre syrienne",
|
||||||
"ga": "punt na Siria",
|
"ga": "punt na Siria",
|
||||||
@@ -5977,7 +5994,7 @@
|
|||||||
"ms": "Franc CFA Afrika Tengah",
|
"ms": "Franc CFA Afrika Tengah",
|
||||||
"nl": "Central African CFA franc",
|
"nl": "Central African CFA franc",
|
||||||
"oc": "Franc CFA d'Africa Centrala",
|
"oc": "Franc CFA d'Africa Centrala",
|
||||||
"pl": "środkowoafrykański frank CFA",
|
"pl": "frank CFA",
|
||||||
"pt": "franco",
|
"pt": "franco",
|
||||||
"ro": "Franc CFA BEAC",
|
"ro": "Franc CFA BEAC",
|
||||||
"ru": "франк КФА BEAC",
|
"ru": "франк КФА BEAC",
|
||||||
@@ -5999,6 +6016,7 @@
|
|||||||
"fr": "argent d'investissement",
|
"fr": "argent d'investissement",
|
||||||
"ja": "投資対象としての銀",
|
"ja": "投資対象としての銀",
|
||||||
"ms": "Perak sebagai pelaburan",
|
"ms": "Perak sebagai pelaburan",
|
||||||
|
"pt": "Prata como investimento",
|
||||||
"ru": "серебро как инвестиция",
|
"ru": "серебро как инвестиция",
|
||||||
"sv": "Silver som investering",
|
"sv": "Silver som investering",
|
||||||
"tr": "Yatırım olarak gümüş",
|
"tr": "Yatırım olarak gümüş",
|
||||||
@@ -6017,6 +6035,7 @@
|
|||||||
"lv": "zelts kā investīcija",
|
"lv": "zelts kā investīcija",
|
||||||
"ml": "സ്വർണവും സാമ്പത്തിക ശാസ്ത്രവും",
|
"ml": "സ്വർണവും സാമ്പത്തിക ശാസ്ത്രവും",
|
||||||
"ms": "emas sebagai pelaburan",
|
"ms": "emas sebagai pelaburan",
|
||||||
|
"pt": "Ouro como investimento",
|
||||||
"ru": "золото как инвестиция",
|
"ru": "золото как инвестиция",
|
||||||
"sr": "investiciono zlato",
|
"sr": "investiciono zlato",
|
||||||
"sv": "investeringsguld",
|
"sv": "investeringsguld",
|
||||||
@@ -7905,7 +7924,6 @@
|
|||||||
"dolar de las islas caimán": "KYD",
|
"dolar de las islas caimán": "KYD",
|
||||||
"dolar de las islas salomon": "SBD",
|
"dolar de las islas salomon": "SBD",
|
||||||
"dolar de las islas salomón": "SBD",
|
"dolar de las islas salomón": "SBD",
|
||||||
"dolar de namibia": "NAD",
|
|
||||||
"dolar de nueva zelanda": "NZD",
|
"dolar de nueva zelanda": "NZD",
|
||||||
"dolar de singapor": "SGD",
|
"dolar de singapor": "SGD",
|
||||||
"dolar de singapur": "SGD",
|
"dolar de singapur": "SGD",
|
||||||
@@ -7963,7 +7981,6 @@
|
|||||||
"dolar namibia": "NAD",
|
"dolar namibia": "NAD",
|
||||||
"dolar namibian": "NAD",
|
"dolar namibian": "NAD",
|
||||||
"dolar namibijski": "NAD",
|
"dolar namibijski": "NAD",
|
||||||
"dolar namibio": "NAD",
|
|
||||||
"dolar neocelandes": "NZD",
|
"dolar neocelandes": "NZD",
|
||||||
"dolar neocelandés": "NZD",
|
"dolar neocelandés": "NZD",
|
||||||
"dolar neozeelandez": "NZD",
|
"dolar neozeelandez": "NZD",
|
||||||
@@ -7978,7 +7995,6 @@
|
|||||||
"dolar singapura": "SGD",
|
"dolar singapura": "SGD",
|
||||||
"dolar singapurski": "SGD",
|
"dolar singapurski": "SGD",
|
||||||
"dolar singapurtar": "SGD",
|
"dolar singapurtar": "SGD",
|
||||||
"dolar sirio": "SYP",
|
|
||||||
"dolar sua": "USD",
|
"dolar sua": "USD",
|
||||||
"dolar surinamdar": "SRD",
|
"dolar surinamdar": "SRD",
|
||||||
"dolar suriname": "SRD",
|
"dolar suriname": "SRD",
|
||||||
@@ -8179,6 +8195,7 @@
|
|||||||
"dominicaanse peso": "DOP",
|
"dominicaanse peso": "DOP",
|
||||||
"dominican peso": "DOP",
|
"dominican peso": "DOP",
|
||||||
"dominican peso oro": "DOP",
|
"dominican peso oro": "DOP",
|
||||||
|
"dominički pezo": "DOP",
|
||||||
"dominik pesosu": "DOP",
|
"dominik pesosu": "DOP",
|
||||||
"dominika peso": "DOP",
|
"dominika peso": "DOP",
|
||||||
"dominikaanisen tasavallan peso": "DOP",
|
"dominikaanisen tasavallan peso": "DOP",
|
||||||
@@ -8528,7 +8545,6 @@
|
|||||||
"etiópsky birr": "ETB",
|
"etiópsky birr": "ETB",
|
||||||
"eua 17": "XBD",
|
"eua 17": "XBD",
|
||||||
"eua 9": "XBC",
|
"eua 9": "XBC",
|
||||||
"eur": "EUR",
|
|
||||||
"euras": "EUR",
|
"euras": "EUR",
|
||||||
"eurco": "XBA",
|
"eurco": "XBA",
|
||||||
"euro": "EUR",
|
"euro": "EUR",
|
||||||
@@ -8859,7 +8875,6 @@
|
|||||||
"frank kongijski": "CDF",
|
"frank kongijski": "CDF",
|
||||||
"frank rwandyjski": "RWF",
|
"frank rwandyjski": "RWF",
|
||||||
"frank szwajcarski": "CHF",
|
"frank szwajcarski": "CHF",
|
||||||
"frank zachodnioafrykański": "XOF",
|
|
||||||
"franko suitzar": "CHF",
|
"franko suitzar": "CHF",
|
||||||
"frw": "RWF",
|
"frw": "RWF",
|
||||||
"ft": "HUF",
|
"ft": "HUF",
|
||||||
@@ -9180,6 +9195,7 @@
|
|||||||
"indonezijska rupija": "IDR",
|
"indonezijska rupija": "IDR",
|
||||||
"indonéská rupie": "IDR",
|
"indonéská rupie": "IDR",
|
||||||
"indonéz rúpia": "IDR",
|
"indonéz rúpia": "IDR",
|
||||||
|
"indonézska rupia": "IDR",
|
||||||
"indonēziešu rūpija": "IDR",
|
"indonēziešu rūpija": "IDR",
|
||||||
"indonēzijas rūpija": "IDR",
|
"indonēzijas rūpija": "IDR",
|
||||||
"inglise nael": "GBP",
|
"inglise nael": "GBP",
|
||||||
@@ -9494,6 +9510,8 @@
|
|||||||
"kirgizia somo": "KGS",
|
"kirgizia somo": "KGS",
|
||||||
"kirgizische som": "KGS",
|
"kirgizische som": "KGS",
|
||||||
"kirgizistansk som": "KGS",
|
"kirgizistansk som": "KGS",
|
||||||
|
"kirgizstānas soms": "KGS",
|
||||||
|
"kirgīzu soms": "KGS",
|
||||||
"kiwi dollar": "NZD",
|
"kiwi dollar": "NZD",
|
||||||
"kínai jüan": "CNY",
|
"kínai jüan": "CNY",
|
||||||
"kíp": "LAK",
|
"kíp": "LAK",
|
||||||
@@ -9810,7 +9828,6 @@
|
|||||||
"lek na halbáine": "ALL",
|
"lek na halbáine": "ALL",
|
||||||
"lek novo": "ALL",
|
"lek novo": "ALL",
|
||||||
"lekas": "ALL",
|
"lekas": "ALL",
|
||||||
"lekă albaneză": "ALL",
|
|
||||||
"lekë": "ALL",
|
"lekë": "ALL",
|
||||||
"leko": "ALL",
|
"leko": "ALL",
|
||||||
"lempira": "HNL",
|
"lempira": "HNL",
|
||||||
@@ -9981,6 +9998,7 @@
|
|||||||
"lira libanesa": "LBP",
|
"lira libanesa": "LBP",
|
||||||
"lira libanese": "LBP",
|
"lira libanese": "LBP",
|
||||||
"lira na tuirce": "TRY",
|
"lira na tuirce": "TRY",
|
||||||
|
"lira siria": "SYP",
|
||||||
"lira siriana": "SYP",
|
"lira siriana": "SYP",
|
||||||
"lira síria": "SYP",
|
"lira síria": "SYP",
|
||||||
"lira thổ nhĩ kỳ": "TRY",
|
"lira thổ nhĩ kỳ": "TRY",
|
||||||
@@ -10291,6 +10309,7 @@
|
|||||||
"meksički pezo": "MXN",
|
"meksički pezo": "MXN",
|
||||||
"meksika peso": "MXN",
|
"meksika peso": "MXN",
|
||||||
"meksika pesosu": "MXN",
|
"meksika pesosu": "MXN",
|
||||||
|
"meksika unuo de investo": "MXV",
|
||||||
"meksikaanse peso": "MXN",
|
"meksikaanse peso": "MXN",
|
||||||
"meksikas peso": "MXN",
|
"meksikas peso": "MXN",
|
||||||
"meksikon peso": "MXN",
|
"meksikon peso": "MXN",
|
||||||
@@ -10746,6 +10765,7 @@
|
|||||||
"ouguiya mauritanien": "MRU",
|
"ouguiya mauritanien": "MRU",
|
||||||
"ouguiya mawritania": "MRU",
|
"ouguiya mawritania": "MRU",
|
||||||
"ouguiya na máratáine": "MRU",
|
"ouguiya na máratáine": "MRU",
|
||||||
|
"ouro como investimento": "XAU",
|
||||||
"ouro do zimbábue": "ZWG",
|
"ouro do zimbábue": "ZWG",
|
||||||
"örmény dram": "AMD",
|
"örmény dram": "AMD",
|
||||||
"östkaribisk dollar": "XCD",
|
"östkaribisk dollar": "XCD",
|
||||||
@@ -11111,6 +11131,7 @@
|
|||||||
"põhja korea vonn": "KPW",
|
"põhja korea vonn": "KPW",
|
||||||
"põhja korea won": "KPW",
|
"põhja korea won": "KPW",
|
||||||
"põhja makedoonia denaar": "MKD",
|
"põhja makedoonia denaar": "MKD",
|
||||||
|
"prata como investimento": "XAG",
|
||||||
"pula": "BWP",
|
"pula": "BWP",
|
||||||
"pula botswana": "BWP",
|
"pula botswana": "BWP",
|
||||||
"pula botswanais": "BWP",
|
"pula botswanais": "BWP",
|
||||||
@@ -11901,6 +11922,7 @@
|
|||||||
"somoni taxico": "TJS",
|
"somoni taxico": "TJS",
|
||||||
"somoni tayiko": "TJS",
|
"somoni tayiko": "TJS",
|
||||||
"somonis": "TJS",
|
"somonis": "TJS",
|
||||||
|
"soms": "KGS",
|
||||||
"sonderziehungsrecht": "XDR",
|
"sonderziehungsrecht": "XDR",
|
||||||
"sos": "SOS",
|
"sos": "SOS",
|
||||||
"sosh": "SOS",
|
"sosh": "SOS",
|
||||||
@@ -12102,7 +12124,6 @@
|
|||||||
"szyling tanzański": "TZS",
|
"szyling tanzański": "TZS",
|
||||||
"szyling ugandyjski": "UGX",
|
"szyling ugandyjski": "UGX",
|
||||||
"sırp dinarı": "RSD",
|
"sırp dinarı": "RSD",
|
||||||
"środkowoafrykański frank cfa": "XAF",
|
|
||||||
"šalamounský dolar": "SBD",
|
"šalamounský dolar": "SBD",
|
||||||
"šalomounský dolar": "SBD",
|
"šalomounský dolar": "SBD",
|
||||||
"šekel chadaš": "ILS",
|
"šekel chadaš": "ILS",
|
||||||
@@ -14017,6 +14038,7 @@
|
|||||||
"самоанский доллар": "WST",
|
"самоанский доллар": "WST",
|
||||||
"самоанська тала": "WST",
|
"самоанська тала": "WST",
|
||||||
"сан томе һәм принсипи добрасы": "STN",
|
"сан томе һәм принсипи добрасы": "STN",
|
||||||
|
"саотомеанска добра": "STN",
|
||||||
"саотомска добра": "STN",
|
"саотомска добра": "STN",
|
||||||
"саудитски риал": "SAR",
|
"саудитски риал": "SAR",
|
||||||
"саудијски риал": "SAR",
|
"саудијски риал": "SAR",
|
||||||
@@ -15350,13 +15372,28 @@
|
|||||||
"ஹங்கேரிய போரிண்ட்": "HUF",
|
"ஹங்கேரிய போரிண்ட்": "HUF",
|
||||||
"ஹிருன்யா": "UAH",
|
"ஹிருன்யா": "UAH",
|
||||||
"ஹொங்கொங் டொலர்": "HKD",
|
"ஹொங்கொங் டொலர்": "HKD",
|
||||||
|
"అమెరికన్ డాలర్": "USD",
|
||||||
|
"ఇజ్రాయెల్ షెకెల్": "ILS",
|
||||||
|
"కెనడియన్ డాలర్": "CAD",
|
||||||
|
"చిలీ పెసో": "CLP",
|
||||||
|
"జపనీస్ యెన్": "JPY",
|
||||||
"డిజిటల్ రూపాయి": "INR",
|
"డిజిటల్ రూపాయి": "INR",
|
||||||
|
"నేపాలీ రూపాయి": "NPR",
|
||||||
|
"పాకిస్థానీ రూపాయి": "PKR",
|
||||||
|
"ఫిలిప్పీన్ పెసో": "PHP",
|
||||||
|
"బ్రూనై డాలర్": "BND",
|
||||||
"భారత రూపాయి": "INR",
|
"భారత రూపాయి": "INR",
|
||||||
"భారతదేశ రూపాయి": "INR",
|
"భారతదేశ రూపాయి": "INR",
|
||||||
"భారతీయ రూపాయి": "INR",
|
"భారతీయ రూపాయి": "INR",
|
||||||
|
"భూటానీస్ గుల్త్రమ్": "BTN",
|
||||||
"యునైటెడ్ స్టేట్స్ డాలర్": "USD",
|
"యునైటెడ్ స్టేట్స్ డాలర్": "USD",
|
||||||
|
"యూఏఈ దిర్హామ్": "AED",
|
||||||
"యూరో": "EUR",
|
"యూరో": "EUR",
|
||||||
"రూపాయి": "INR",
|
"రూపాయి": "INR",
|
||||||
|
"రెన్మిన్బి": "CNY",
|
||||||
|
"శ్రీలంక రూపాయి": "LKR",
|
||||||
|
"సింగపూర్ డాలర్": "SGD",
|
||||||
|
"సౌదీ రియాల్": "SAR",
|
||||||
"స్విస్ ఫ్రాంక్": "CHF",
|
"స్విస్ ఫ్రాంక్": "CHF",
|
||||||
"അൾജീരിയൻ ദിനാർ": "DZD",
|
"അൾജീരിയൻ ദിനാർ": "DZD",
|
||||||
"ഇന്തോനേഷ്യൻ റുപിയ": "IDR",
|
"ഇന്തോനേഷ്യൻ റുപിയ": "IDR",
|
||||||
@@ -15607,6 +15644,8 @@
|
|||||||
"부탄 뉘땀": "BTN",
|
"부탄 뉘땀": "BTN",
|
||||||
"부탄눌트럼": "BTN",
|
"부탄눌트럼": "BTN",
|
||||||
"북마케도니아 데나르": "MKD",
|
"북마케도니아 데나르": "MKD",
|
||||||
|
"북조선 원": "KPW",
|
||||||
|
"북한 원": "KPW",
|
||||||
"브라질 레알": "BRL",
|
"브라질 레알": "BRL",
|
||||||
"브라질 헤알": "BRL",
|
"브라질 헤알": "BRL",
|
||||||
"브루나이 달러": "BND",
|
"브루나이 달러": "BND",
|
||||||
@@ -16199,7 +16238,6 @@
|
|||||||
"香港・ドル": "HKD",
|
"香港・ドル": "HKD",
|
||||||
"香港元": "HKD",
|
"香港元": "HKD",
|
||||||
"﷼": "IRR",
|
"﷼": "IRR",
|
||||||
"﷼'": "YER",
|
"﷼'": "YER"
|
||||||
"💶": "EUR"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+364
-585
File diff suppressed because it is too large
Load Diff
+178
-187
@@ -4079,6 +4079,7 @@
|
|||||||
"bg-BG": "BG:bg",
|
"bg-BG": "BG:bg",
|
||||||
"bn-BD": "BD:bn",
|
"bn-BD": "BD:bn",
|
||||||
"bn-IN": "IN:bn",
|
"bn-IN": "IN:bn",
|
||||||
|
"ca-ES": "ES:ca",
|
||||||
"cs-CZ": "CZ:cs",
|
"cs-CZ": "CZ:cs",
|
||||||
"de-AT": "AT:de",
|
"de-AT": "AT:de",
|
||||||
"de-CH": "CH:de",
|
"de-CH": "CH:de",
|
||||||
@@ -4110,16 +4111,15 @@
|
|||||||
"es-CO": "CO:es-419",
|
"es-CO": "CO:es-419",
|
||||||
"es-CU": "CU:es-419",
|
"es-CU": "CU:es-419",
|
||||||
"es-ES": "ES:es",
|
"es-ES": "ES:es",
|
||||||
"es-MX": "MX:es-419",
|
"et-EE": "EE:et",
|
||||||
"es-PE": "PE:es-419",
|
"fi-FI": "FI:fi",
|
||||||
"es-US": "US:es-419",
|
|
||||||
"es-VE": "VE:es-419",
|
|
||||||
"fr-BE": "BE:fr",
|
"fr-BE": "BE:fr",
|
||||||
"fr-CA": "CA:fr",
|
"fr-CA": "CA:fr",
|
||||||
"fr-CH": "CH:fr",
|
"fr-CH": "CH:fr",
|
||||||
"fr-FR": "FR:fr",
|
"fr-FR": "FR:fr",
|
||||||
"fr-MA": "MA:fr",
|
"fr-MA": "MA:fr",
|
||||||
"fr-SN": "SN:fr",
|
"fr-SN": "SN:fr",
|
||||||
|
"gu-IN": "IN:gu",
|
||||||
"he-IL": "IL:he",
|
"he-IL": "IL:he",
|
||||||
"hi-IN": "IN:hi",
|
"hi-IN": "IN:hi",
|
||||||
"hu-HU": "HU:hu",
|
"hu-HU": "HU:hu",
|
||||||
@@ -4131,12 +4131,13 @@
|
|||||||
"lv-LV": "LV:lv",
|
"lv-LV": "LV:lv",
|
||||||
"ml-IN": "IN:ml",
|
"ml-IN": "IN:ml",
|
||||||
"mr-IN": "IN:mr",
|
"mr-IN": "IN:mr",
|
||||||
|
"ms-MY": "MY:ms",
|
||||||
"nb-NO": "NO:no",
|
"nb-NO": "NO:no",
|
||||||
"nl-BE": "BE:nl",
|
"nl-BE": "BE:nl",
|
||||||
"nl-NL": "NL:nl",
|
"nl-NL": "NL:nl",
|
||||||
|
"pa-IN": "IN:pa",
|
||||||
"pl-PL": "PL:pl",
|
"pl-PL": "PL:pl",
|
||||||
"pt-BR": "BR:pt-419",
|
"pt-BR": "BR:pt-419",
|
||||||
"pt-PT": "PT:pt-150",
|
|
||||||
"ro-RO": "RO:ro",
|
"ro-RO": "RO:ro",
|
||||||
"ru-RU": "RU:ru",
|
"ru-RU": "RU:ru",
|
||||||
"ru-UA": "UA:ru",
|
"ru-UA": "UA:ru",
|
||||||
@@ -4151,8 +4152,7 @@
|
|||||||
"uk-UA": "UA:uk",
|
"uk-UA": "UA:uk",
|
||||||
"vi-VN": "VN:vi",
|
"vi-VN": "VN:vi",
|
||||||
"zh-CN": "CN:zh-Hans",
|
"zh-CN": "CN:zh-Hans",
|
||||||
"zh-HK": "HK:zh-Hant",
|
"zh-HK": "HK:zh-Hant"
|
||||||
"zh-TW": "TW:zh-Hant"
|
|
||||||
},
|
},
|
||||||
"supported_domains": {}
|
"supported_domains": {}
|
||||||
},
|
},
|
||||||
@@ -5740,186 +5740,6 @@
|
|||||||
"zu-ZA": "ZA"
|
"zu-ZA": "ZA"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"karmasearch": {
|
|
||||||
"all_locale": null,
|
|
||||||
"custom": {},
|
|
||||||
"data_type": "traits_v1",
|
|
||||||
"languages": {},
|
|
||||||
"regions": {
|
|
||||||
"da-DK": "da-DK",
|
|
||||||
"de-AT": "de-AT",
|
|
||||||
"de-CH": "de-CH",
|
|
||||||
"de-DE": "de-DE",
|
|
||||||
"en-AU": "en-AU",
|
|
||||||
"en-CA": "en-CA",
|
|
||||||
"en-GB": "en-GB",
|
|
||||||
"en-ID": "en-ID",
|
|
||||||
"en-IN": "en-IN",
|
|
||||||
"en-MY": "en-MY",
|
|
||||||
"en-NZ": "en-NZ",
|
|
||||||
"en-PH": "en-PH",
|
|
||||||
"en-US": "en-US",
|
|
||||||
"en-ZA": "en-ZA",
|
|
||||||
"es-AR": "es-AR",
|
|
||||||
"es-CL": "es-CL",
|
|
||||||
"es-ES": "es-ES",
|
|
||||||
"es-MX": "es-MX",
|
|
||||||
"es-US": "es-US",
|
|
||||||
"fi-FI": "fi-FI",
|
|
||||||
"fr-BE": "fr-BE",
|
|
||||||
"fr-CA": "fr-CA",
|
|
||||||
"fr-CH": "fr-CH",
|
|
||||||
"fr-FR": "fr-FR",
|
|
||||||
"it-IT": "it-IT",
|
|
||||||
"ja-JP": "ja-JP",
|
|
||||||
"ko-KR": "ko-KR",
|
|
||||||
"nl-BE": "nl-BE",
|
|
||||||
"nl-NL": "nl-NL",
|
|
||||||
"pl-PL": "pl-PL",
|
|
||||||
"pt-BR": "pt-BR",
|
|
||||||
"ru-RU": "ru-RU",
|
|
||||||
"sv-SE": "sv-SE",
|
|
||||||
"tr-TR": "tr-TR",
|
|
||||||
"zh-CN": "zh-CN",
|
|
||||||
"zh-HK": "zh-HK",
|
|
||||||
"zh-TW": "zh-TW"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"karmasearch images": {
|
|
||||||
"all_locale": null,
|
|
||||||
"custom": {},
|
|
||||||
"data_type": "traits_v1",
|
|
||||||
"languages": {},
|
|
||||||
"regions": {
|
|
||||||
"da-DK": "da-DK",
|
|
||||||
"de-AT": "de-AT",
|
|
||||||
"de-CH": "de-CH",
|
|
||||||
"de-DE": "de-DE",
|
|
||||||
"en-AU": "en-AU",
|
|
||||||
"en-CA": "en-CA",
|
|
||||||
"en-GB": "en-GB",
|
|
||||||
"en-ID": "en-ID",
|
|
||||||
"en-IN": "en-IN",
|
|
||||||
"en-MY": "en-MY",
|
|
||||||
"en-NZ": "en-NZ",
|
|
||||||
"en-PH": "en-PH",
|
|
||||||
"en-US": "en-US",
|
|
||||||
"en-ZA": "en-ZA",
|
|
||||||
"es-AR": "es-AR",
|
|
||||||
"es-CL": "es-CL",
|
|
||||||
"es-ES": "es-ES",
|
|
||||||
"es-MX": "es-MX",
|
|
||||||
"es-US": "es-US",
|
|
||||||
"fi-FI": "fi-FI",
|
|
||||||
"fr-BE": "fr-BE",
|
|
||||||
"fr-CA": "fr-CA",
|
|
||||||
"fr-CH": "fr-CH",
|
|
||||||
"fr-FR": "fr-FR",
|
|
||||||
"it-IT": "it-IT",
|
|
||||||
"ja-JP": "ja-JP",
|
|
||||||
"ko-KR": "ko-KR",
|
|
||||||
"nl-BE": "nl-BE",
|
|
||||||
"nl-NL": "nl-NL",
|
|
||||||
"pl-PL": "pl-PL",
|
|
||||||
"pt-BR": "pt-BR",
|
|
||||||
"ru-RU": "ru-RU",
|
|
||||||
"sv-SE": "sv-SE",
|
|
||||||
"tr-TR": "tr-TR",
|
|
||||||
"zh-CN": "zh-CN",
|
|
||||||
"zh-HK": "zh-HK",
|
|
||||||
"zh-TW": "zh-TW"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"karmasearch news": {
|
|
||||||
"all_locale": null,
|
|
||||||
"custom": {},
|
|
||||||
"data_type": "traits_v1",
|
|
||||||
"languages": {},
|
|
||||||
"regions": {
|
|
||||||
"da-DK": "da-DK",
|
|
||||||
"de-AT": "de-AT",
|
|
||||||
"de-CH": "de-CH",
|
|
||||||
"de-DE": "de-DE",
|
|
||||||
"en-AU": "en-AU",
|
|
||||||
"en-CA": "en-CA",
|
|
||||||
"en-GB": "en-GB",
|
|
||||||
"en-ID": "en-ID",
|
|
||||||
"en-IN": "en-IN",
|
|
||||||
"en-MY": "en-MY",
|
|
||||||
"en-NZ": "en-NZ",
|
|
||||||
"en-PH": "en-PH",
|
|
||||||
"en-US": "en-US",
|
|
||||||
"en-ZA": "en-ZA",
|
|
||||||
"es-AR": "es-AR",
|
|
||||||
"es-CL": "es-CL",
|
|
||||||
"es-ES": "es-ES",
|
|
||||||
"es-MX": "es-MX",
|
|
||||||
"es-US": "es-US",
|
|
||||||
"fi-FI": "fi-FI",
|
|
||||||
"fr-BE": "fr-BE",
|
|
||||||
"fr-CA": "fr-CA",
|
|
||||||
"fr-CH": "fr-CH",
|
|
||||||
"fr-FR": "fr-FR",
|
|
||||||
"it-IT": "it-IT",
|
|
||||||
"ja-JP": "ja-JP",
|
|
||||||
"ko-KR": "ko-KR",
|
|
||||||
"nl-BE": "nl-BE",
|
|
||||||
"nl-NL": "nl-NL",
|
|
||||||
"pl-PL": "pl-PL",
|
|
||||||
"pt-BR": "pt-BR",
|
|
||||||
"ru-RU": "ru-RU",
|
|
||||||
"sv-SE": "sv-SE",
|
|
||||||
"tr-TR": "tr-TR",
|
|
||||||
"zh-CN": "zh-CN",
|
|
||||||
"zh-HK": "zh-HK",
|
|
||||||
"zh-TW": "zh-TW"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"karmasearch videos": {
|
|
||||||
"all_locale": null,
|
|
||||||
"custom": {},
|
|
||||||
"data_type": "traits_v1",
|
|
||||||
"languages": {},
|
|
||||||
"regions": {
|
|
||||||
"da-DK": "da-DK",
|
|
||||||
"de-AT": "de-AT",
|
|
||||||
"de-CH": "de-CH",
|
|
||||||
"de-DE": "de-DE",
|
|
||||||
"en-AU": "en-AU",
|
|
||||||
"en-CA": "en-CA",
|
|
||||||
"en-GB": "en-GB",
|
|
||||||
"en-ID": "en-ID",
|
|
||||||
"en-IN": "en-IN",
|
|
||||||
"en-MY": "en-MY",
|
|
||||||
"en-NZ": "en-NZ",
|
|
||||||
"en-PH": "en-PH",
|
|
||||||
"en-US": "en-US",
|
|
||||||
"en-ZA": "en-ZA",
|
|
||||||
"es-AR": "es-AR",
|
|
||||||
"es-CL": "es-CL",
|
|
||||||
"es-ES": "es-ES",
|
|
||||||
"es-MX": "es-MX",
|
|
||||||
"es-US": "es-US",
|
|
||||||
"fi-FI": "fi-FI",
|
|
||||||
"fr-BE": "fr-BE",
|
|
||||||
"fr-CA": "fr-CA",
|
|
||||||
"fr-CH": "fr-CH",
|
|
||||||
"fr-FR": "fr-FR",
|
|
||||||
"it-IT": "it-IT",
|
|
||||||
"ja-JP": "ja-JP",
|
|
||||||
"ko-KR": "ko-KR",
|
|
||||||
"nl-BE": "nl-BE",
|
|
||||||
"nl-NL": "nl-NL",
|
|
||||||
"pl-PL": "pl-PL",
|
|
||||||
"pt-BR": "pt-BR",
|
|
||||||
"ru-RU": "ru-RU",
|
|
||||||
"sv-SE": "sv-SE",
|
|
||||||
"tr-TR": "tr-TR",
|
|
||||||
"zh-CN": "zh-CN",
|
|
||||||
"zh-HK": "zh-HK",
|
|
||||||
"zh-TW": "zh-TW"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mojeek": {
|
"mojeek": {
|
||||||
"all_locale": null,
|
"all_locale": null,
|
||||||
"custom": {
|
"custom": {
|
||||||
@@ -8513,6 +8333,177 @@
|
|||||||
"zh-classical": "zh-classical"
|
"zh-classical": "zh-classical"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"yep": {
|
||||||
|
"all_locale": null,
|
||||||
|
"custom": {},
|
||||||
|
"data_type": "traits_v1",
|
||||||
|
"languages": {
|
||||||
|
"aa": "aa",
|
||||||
|
"ab": "ab",
|
||||||
|
"af": "af",
|
||||||
|
"ak": "ak",
|
||||||
|
"am": "am",
|
||||||
|
"an": "an",
|
||||||
|
"ar": "ar",
|
||||||
|
"as": "as",
|
||||||
|
"az": "az",
|
||||||
|
"ba": "ba",
|
||||||
|
"be": "be",
|
||||||
|
"bg": "bg",
|
||||||
|
"bho": "bh",
|
||||||
|
"bm": "bm",
|
||||||
|
"bn": "bn",
|
||||||
|
"bo": "bo",
|
||||||
|
"br": "br",
|
||||||
|
"bs": "bs",
|
||||||
|
"ca": "ca",
|
||||||
|
"ce": "ce",
|
||||||
|
"co": "co",
|
||||||
|
"cs": "cs",
|
||||||
|
"cu": "cu",
|
||||||
|
"cv": "cv",
|
||||||
|
"cy": "cy",
|
||||||
|
"da": "da",
|
||||||
|
"de": "de",
|
||||||
|
"dv": "dv",
|
||||||
|
"dz": "dz",
|
||||||
|
"ee": "ee",
|
||||||
|
"el": "el",
|
||||||
|
"en": "en",
|
||||||
|
"eo": "eo",
|
||||||
|
"es": "es",
|
||||||
|
"et": "et",
|
||||||
|
"eu": "eu",
|
||||||
|
"fa": "fa",
|
||||||
|
"ff": "ff",
|
||||||
|
"fi": "fi",
|
||||||
|
"fil": "tl",
|
||||||
|
"fo": "fo",
|
||||||
|
"fr": "fr",
|
||||||
|
"fy": "fy",
|
||||||
|
"ga": "ga",
|
||||||
|
"gd": "gd",
|
||||||
|
"gl": "gl",
|
||||||
|
"gn": "gn",
|
||||||
|
"gu": "gu",
|
||||||
|
"gv": "gv",
|
||||||
|
"ha": "ha",
|
||||||
|
"he": "he",
|
||||||
|
"hi": "hi",
|
||||||
|
"hr": "hr",
|
||||||
|
"ht": "ht",
|
||||||
|
"hu": "hu",
|
||||||
|
"hy": "hy",
|
||||||
|
"ia": "ia",
|
||||||
|
"id": "id",
|
||||||
|
"ie": "ie",
|
||||||
|
"ig": "ig",
|
||||||
|
"ii": "ii",
|
||||||
|
"io": "io",
|
||||||
|
"is": "is",
|
||||||
|
"it": "it",
|
||||||
|
"iu": "iu",
|
||||||
|
"ja": "ja",
|
||||||
|
"jv": "jv",
|
||||||
|
"ka": "ka",
|
||||||
|
"ki": "ki",
|
||||||
|
"kk": "kk",
|
||||||
|
"kl": "kl",
|
||||||
|
"km": "km",
|
||||||
|
"kn": "kn",
|
||||||
|
"ko": "ko",
|
||||||
|
"ks": "ks",
|
||||||
|
"ku": "ku",
|
||||||
|
"kw": "kw",
|
||||||
|
"ky": "ky",
|
||||||
|
"la": "la",
|
||||||
|
"lb": "lb",
|
||||||
|
"lg": "lg",
|
||||||
|
"ln": "ln",
|
||||||
|
"lo": "lo",
|
||||||
|
"lt": "lt",
|
||||||
|
"lu": "lu",
|
||||||
|
"lv": "lv",
|
||||||
|
"mg": "mg",
|
||||||
|
"mi": "mi",
|
||||||
|
"mk": "mk",
|
||||||
|
"ml": "ml",
|
||||||
|
"mn": "mn",
|
||||||
|
"mr": "mr",
|
||||||
|
"ms": "ms",
|
||||||
|
"mt": "mt",
|
||||||
|
"my": "my",
|
||||||
|
"nb": "nb",
|
||||||
|
"nd": "nd",
|
||||||
|
"ne": "ne",
|
||||||
|
"nl": "nl",
|
||||||
|
"nn": "nn",
|
||||||
|
"no": "no",
|
||||||
|
"nr": "nr",
|
||||||
|
"nv": "nv",
|
||||||
|
"ny": "ny",
|
||||||
|
"oc": "oc",
|
||||||
|
"om": "om",
|
||||||
|
"or": "or",
|
||||||
|
"os": "os",
|
||||||
|
"pa": "pa",
|
||||||
|
"pl": "pl",
|
||||||
|
"ps": "ps",
|
||||||
|
"pt": "pt",
|
||||||
|
"qu": "qu",
|
||||||
|
"rm": "rm",
|
||||||
|
"rn": "rn",
|
||||||
|
"ro": "ro",
|
||||||
|
"ru": "ru",
|
||||||
|
"rw": "rw",
|
||||||
|
"sa": "sa",
|
||||||
|
"sc": "sc",
|
||||||
|
"sd": "sd",
|
||||||
|
"se": "se",
|
||||||
|
"sg": "sg",
|
||||||
|
"si": "si",
|
||||||
|
"sk": "sk",
|
||||||
|
"sl": "sl",
|
||||||
|
"sn": "sn",
|
||||||
|
"so": "so",
|
||||||
|
"sq": "sq",
|
||||||
|
"sr": "sr",
|
||||||
|
"ss": "ss",
|
||||||
|
"st": "st",
|
||||||
|
"su": "su",
|
||||||
|
"sv": "sv",
|
||||||
|
"sw": "sw",
|
||||||
|
"ta": "ta",
|
||||||
|
"te": "te",
|
||||||
|
"tg": "tg",
|
||||||
|
"th": "th",
|
||||||
|
"ti": "ti",
|
||||||
|
"tk": "tk",
|
||||||
|
"tn": "tn",
|
||||||
|
"to": "to",
|
||||||
|
"tr": "tr",
|
||||||
|
"ts": "ts",
|
||||||
|
"tt": "tt",
|
||||||
|
"ug": "ug",
|
||||||
|
"uk": "uk",
|
||||||
|
"ur": "ur",
|
||||||
|
"uz": "uz",
|
||||||
|
"ve": "ve",
|
||||||
|
"vi": "vi",
|
||||||
|
"vo": "vo",
|
||||||
|
"wa": "wa",
|
||||||
|
"wo": "wo",
|
||||||
|
"xh": "xh",
|
||||||
|
"yi": "yi",
|
||||||
|
"yo": "yo",
|
||||||
|
"za": "za",
|
||||||
|
"zh": "zh",
|
||||||
|
"zh_Hans": "zh-cn",
|
||||||
|
"zh_Hant": "zh-tw",
|
||||||
|
"zu": "zu"
|
||||||
|
},
|
||||||
|
"regions": {}
|
||||||
|
},
|
||||||
"z-library": {
|
"z-library": {
|
||||||
"all_locale": "",
|
"all_locale": "",
|
||||||
"custom": {
|
"custom": {
|
||||||
|
|||||||
+4502
-2286
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
],
|
],
|
||||||
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
|
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
|
||||||
"versions": [
|
"versions": [
|
||||||
"150.0",
|
"151.0",
|
||||||
"149.0"
|
"150.0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+258
-143
@@ -69,11 +69,6 @@
|
|||||||
"symbol": "pk (US)",
|
"symbol": "pk (US)",
|
||||||
"to_si_factor": 0.008809768
|
"to_si_factor": 0.008809768
|
||||||
},
|
},
|
||||||
"Q101427917": {
|
|
||||||
"si_name": "Q25517",
|
|
||||||
"symbol": "pk (UK)",
|
|
||||||
"to_si_factor": 0.00909218
|
|
||||||
},
|
|
||||||
"Q101428098": {
|
"Q101428098": {
|
||||||
"si_name": "Q44395",
|
"si_name": "Q44395",
|
||||||
"symbol": "dbar",
|
"symbol": "dbar",
|
||||||
@@ -84,6 +79,11 @@
|
|||||||
"symbol": "μbar",
|
"symbol": "μbar",
|
||||||
"to_si_factor": 0.1
|
"to_si_factor": 0.1
|
||||||
},
|
},
|
||||||
|
"Q101435213": {
|
||||||
|
"si_name": "Q101435195",
|
||||||
|
"symbol": "Jy s",
|
||||||
|
"to_si_factor": 1e-26
|
||||||
|
},
|
||||||
"Q101435332": {
|
"Q101435332": {
|
||||||
"si_name": "Q44395",
|
"si_name": "Q44395",
|
||||||
"symbol": "cm Hg",
|
"symbol": "cm Hg",
|
||||||
@@ -124,11 +124,6 @@
|
|||||||
"symbol": "pm²",
|
"symbol": "pm²",
|
||||||
"to_si_factor": 1e-24
|
"to_si_factor": 1e-24
|
||||||
},
|
},
|
||||||
"Q101463679": {
|
|
||||||
"si_name": "Q25343",
|
|
||||||
"symbol": "hm²",
|
|
||||||
"to_si_factor": 10000.0
|
|
||||||
},
|
|
||||||
"Q101464050": {
|
"Q101464050": {
|
||||||
"si_name": "Q25343",
|
"si_name": "Q25343",
|
||||||
"symbol": "Mm²",
|
"symbol": "Mm²",
|
||||||
@@ -199,6 +194,11 @@
|
|||||||
"symbol": "d⁻¹",
|
"symbol": "d⁻¹",
|
||||||
"to_si_factor": 1.15741e-05
|
"to_si_factor": 1.15741e-05
|
||||||
},
|
},
|
||||||
|
"Q102129764": {
|
||||||
|
"si_name": "Q6137407",
|
||||||
|
"symbol": "a⁻¹",
|
||||||
|
"to_si_factor": 3.17098e-08
|
||||||
|
},
|
||||||
"Q102130673": {
|
"Q102130673": {
|
||||||
"si_name": "Q182429",
|
"si_name": "Q182429",
|
||||||
"symbol": "ym/s",
|
"symbol": "ym/s",
|
||||||
@@ -425,9 +425,9 @@
|
|||||||
"to_si_factor": 10.0
|
"to_si_factor": 10.0
|
||||||
},
|
},
|
||||||
"Q1042866": {
|
"Q1042866": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Zib",
|
"symbol": "Zib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.1805916207174113e+21
|
||||||
},
|
},
|
||||||
"Q104317437": {
|
"Q104317437": {
|
||||||
"si_name": "Q25381181",
|
"si_name": "Q25381181",
|
||||||
@@ -995,9 +995,9 @@
|
|||||||
"to_si_factor": 1000000000000.0
|
"to_si_factor": 1000000000000.0
|
||||||
},
|
},
|
||||||
"Q106247880": {
|
"Q106247880": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "digit/s",
|
"symbol": "digit/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q106247940": {
|
"Q106247940": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -1504,6 +1504,11 @@
|
|||||||
"symbol": "m²/(s² K)",
|
"symbol": "m²/(s² K)",
|
||||||
"to_si_factor": 1.0
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
|
"Q106707206": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "HU",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q106707404": {
|
"Q106707404": {
|
||||||
"si_name": "Q106707404",
|
"si_name": "Q106707404",
|
||||||
"symbol": "kg m²/(s² K)",
|
"symbol": "kg m²/(s² K)",
|
||||||
@@ -2314,79 +2319,124 @@
|
|||||||
"symbol": "cm²/(sr erg)",
|
"symbol": "cm²/(sr erg)",
|
||||||
"to_si_factor": 1000.0
|
"to_si_factor": 1000.0
|
||||||
},
|
},
|
||||||
|
"Q107710161": {
|
||||||
|
"si_name": "Q199",
|
||||||
|
"symbol": "J/bit",
|
||||||
|
"to_si_factor": 1.0
|
||||||
|
},
|
||||||
"Q107821494": {
|
"Q107821494": {
|
||||||
"si_name": null,
|
"si_name": "Q21401573",
|
||||||
"symbol": "bit/m³",
|
"symbol": "bit/m³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q107822428": {
|
"Q107822428": {
|
||||||
"si_name": null,
|
"si_name": "Q11547251",
|
||||||
"symbol": "bit/m",
|
"symbol": "bit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q107824325": {
|
"Q107824325": {
|
||||||
"si_name": null,
|
"si_name": "Q11547252",
|
||||||
"symbol": "bit/m²",
|
"symbol": "bit/m²",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
|
},
|
||||||
|
"Q107825143": {
|
||||||
|
"si_name": "Q11547251",
|
||||||
|
"symbol": "Eibit/m",
|
||||||
|
"to_si_factor": 1.152921504606847e+18
|
||||||
|
},
|
||||||
|
"Q107825584": {
|
||||||
|
"si_name": "Q11547252",
|
||||||
|
"symbol": "Eibit/m²",
|
||||||
|
"to_si_factor": 1.152921504606847e+18
|
||||||
|
},
|
||||||
|
"Q107825716": {
|
||||||
|
"si_name": "Q21401573",
|
||||||
|
"symbol": "Eibit/m³",
|
||||||
|
"to_si_factor": 1.152921504606847e+18
|
||||||
|
},
|
||||||
|
"Q107862736": {
|
||||||
|
"si_name": "Q11547251",
|
||||||
|
"symbol": "Gibit/m",
|
||||||
|
"to_si_factor": 1073741824.0
|
||||||
|
},
|
||||||
|
"Q107862746": {
|
||||||
|
"si_name": "Q11547252",
|
||||||
|
"symbol": "Gibit/m²",
|
||||||
|
"to_si_factor": 1073741824.0
|
||||||
|
},
|
||||||
|
"Q107862762": {
|
||||||
|
"si_name": "Q21401573",
|
||||||
|
"symbol": "Gibit/m³",
|
||||||
|
"to_si_factor": 1073741824.0
|
||||||
},
|
},
|
||||||
"Q107862770": {
|
"Q107862770": {
|
||||||
"si_name": null,
|
"si_name": "Q11547251",
|
||||||
"symbol": "Kibit/m",
|
"symbol": "Kibit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1024.0
|
||||||
},
|
},
|
||||||
"Q107862783": {
|
"Q107862783": {
|
||||||
"si_name": null,
|
"si_name": "Q11547252",
|
||||||
"symbol": "Kibit/m",
|
"symbol": "Kibit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1024.0
|
||||||
},
|
},
|
||||||
"Q107862850": {
|
"Q107862850": {
|
||||||
"si_name": null,
|
"si_name": "Q21401573",
|
||||||
"symbol": "Kibit/m³",
|
"symbol": "Kibit/m³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1024.0
|
||||||
},
|
},
|
||||||
"Q107862870": {
|
"Q107862870": {
|
||||||
"si_name": null,
|
"si_name": "Q11547251",
|
||||||
"symbol": "Mibit/m",
|
"symbol": "Mibit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1048576.0
|
||||||
},
|
},
|
||||||
"Q107862884": {
|
"Q107862884": {
|
||||||
"si_name": null,
|
"si_name": "Q11547252",
|
||||||
"symbol": "Mibit/m²",
|
"symbol": "Mibit/m²",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1048576.0
|
||||||
},
|
},
|
||||||
"Q107862898": {
|
"Q107862898": {
|
||||||
"si_name": null,
|
"si_name": "Q21401573",
|
||||||
"symbol": "Mibit/m³",
|
"symbol": "Mibit/m³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1048576.0
|
||||||
},
|
},
|
||||||
"Q107970215": {
|
"Q107970215": {
|
||||||
"si_name": null,
|
"si_name": "Q11547251",
|
||||||
"symbol": "Pibit/m",
|
"symbol": "Pibit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1125899906842624.0
|
||||||
},
|
},
|
||||||
"Q107970224": {
|
"Q107970224": {
|
||||||
"si_name": null,
|
"si_name": "Q11547252",
|
||||||
"symbol": "Pibit/m²",
|
"symbol": "Pibit/m²",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1125899906842624.0
|
||||||
},
|
},
|
||||||
"Q107970230": {
|
"Q107970230": {
|
||||||
"si_name": null,
|
"si_name": "Q21401573",
|
||||||
"symbol": "Pibit/m³",
|
"symbol": "Pibit/m³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1125899906842624.0
|
||||||
},
|
},
|
||||||
"Q107970235": {
|
"Q107970235": {
|
||||||
"si_name": null,
|
"si_name": "Q11547251",
|
||||||
"symbol": "Tibit/m",
|
"symbol": "Tibit/m",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1099511627776.0
|
||||||
},
|
},
|
||||||
"Q107970256": {
|
"Q107970256": {
|
||||||
"si_name": null,
|
"si_name": "Q21401573",
|
||||||
"symbol": "Tibit/m³",
|
"symbol": "Tibit/m³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1099511627776.0
|
||||||
},
|
},
|
||||||
"Q107970266": {
|
"Q107970266": {
|
||||||
"si_name": null,
|
"si_name": "Q11547252",
|
||||||
"symbol": "Tibit/m²",
|
"symbol": "Tibit/m²",
|
||||||
|
"to_si_factor": 1099511627776.0
|
||||||
|
},
|
||||||
|
"Q108112819": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "€/kWh",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
|
"Q108112891": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "€/(MW h)",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q108270163": {
|
"Q108270163": {
|
||||||
@@ -2395,9 +2445,9 @@
|
|||||||
"to_si_factor": 3.169e-05
|
"to_si_factor": 3.169e-05
|
||||||
},
|
},
|
||||||
"Q1084321": {
|
"Q1084321": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "Tb/s",
|
"symbol": "Tb/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000000.0
|
||||||
},
|
},
|
||||||
"Q108533173": {
|
"Q108533173": {
|
||||||
"si_name": "Q108533173",
|
"si_name": "Q108533173",
|
||||||
@@ -2434,6 +2484,16 @@
|
|||||||
"symbol": "GeV/c²",
|
"symbol": "GeV/c²",
|
||||||
"to_si_factor": 1.7826619216278976e-27
|
"to_si_factor": 1.7826619216278976e-27
|
||||||
},
|
},
|
||||||
|
"Q108913970": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "person/km²",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
|
"Q108914485": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "person/m²",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q108920356": {
|
"Q108920356": {
|
||||||
"si_name": "Q25406",
|
"si_name": "Q25406",
|
||||||
"symbol": "esu",
|
"symbol": "esu",
|
||||||
@@ -2464,6 +2524,11 @@
|
|||||||
"symbol": "e.u.",
|
"symbol": "e.u.",
|
||||||
"to_si_factor": 4.184
|
"to_si_factor": 4.184
|
||||||
},
|
},
|
||||||
|
"Q109337616": {
|
||||||
|
"si_name": "Q139710667",
|
||||||
|
"symbol": "1/(100 eV)",
|
||||||
|
"to_si_factor": 6.2415e+16
|
||||||
|
},
|
||||||
"Q109448508": {
|
"Q109448508": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
"symbol": "man-Sv",
|
"symbol": "man-Sv",
|
||||||
@@ -2499,6 +2564,11 @@
|
|||||||
"symbol": "nm²",
|
"symbol": "nm²",
|
||||||
"to_si_factor": 1e-18
|
"to_si_factor": 1e-18
|
||||||
},
|
},
|
||||||
|
"Q11123": {
|
||||||
|
"si_name": "Q25517",
|
||||||
|
"symbol": "pt (UK)",
|
||||||
|
"to_si_factor": 0.00056826125
|
||||||
|
},
|
||||||
"Q111494193": {
|
"Q111494193": {
|
||||||
"si_name": "Q111494193",
|
"si_name": "Q111494193",
|
||||||
"symbol": "J/(Hz mol)",
|
"symbol": "J/(Hz mol)",
|
||||||
@@ -2509,6 +2579,11 @@
|
|||||||
"symbol": "%",
|
"symbol": "%",
|
||||||
"to_si_factor": 0.01
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
|
"Q112659472": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "PVU",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q1131660": {
|
"Q1131660": {
|
||||||
"si_name": "Q11570",
|
"si_name": "Q11570",
|
||||||
"symbol": "st",
|
"symbol": "st",
|
||||||
@@ -2540,14 +2615,14 @@
|
|||||||
"to_si_factor": 31688087.81402895
|
"to_si_factor": 31688087.81402895
|
||||||
},
|
},
|
||||||
"Q1140444": {
|
"Q1140444": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Zb",
|
"symbol": "Zb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1e+21
|
||||||
},
|
},
|
||||||
"Q1140577": {
|
"Q1140577": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Yb",
|
"symbol": "Yb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.0
|
||||||
},
|
},
|
||||||
"Q114559346": {
|
"Q114559346": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -2560,14 +2635,14 @@
|
|||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q1152074": {
|
"Q1152074": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Pb",
|
"symbol": "Pb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000000000.0
|
||||||
},
|
},
|
||||||
"Q1152323": {
|
"Q1152323": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Tb",
|
"symbol": "Tb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000000.0
|
||||||
},
|
},
|
||||||
"Q115277430": {
|
"Q115277430": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -2810,14 +2885,14 @@
|
|||||||
"to_si_factor": 4.4482216152605
|
"to_si_factor": 4.4482216152605
|
||||||
},
|
},
|
||||||
"Q1194580": {
|
"Q1194580": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Mib",
|
"symbol": "Mib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1048576.0
|
||||||
},
|
},
|
||||||
"Q1195111": {
|
"Q1195111": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Eb",
|
"symbol": "Eb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1e+18
|
||||||
},
|
},
|
||||||
"Q1196837": {
|
"Q1196837": {
|
||||||
"si_name": "Q1063756",
|
"si_name": "Q1063756",
|
||||||
@@ -2865,15 +2940,20 @@
|
|||||||
"to_si_factor": 0.03110348
|
"to_si_factor": 0.03110348
|
||||||
},
|
},
|
||||||
"Q1204894": {
|
"Q1204894": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Gib",
|
"symbol": "Gib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1073741824.0
|
||||||
},
|
},
|
||||||
"Q12129": {
|
"Q12129": {
|
||||||
"si_name": "Q11573",
|
"si_name": "Q11573",
|
||||||
"symbol": "pc",
|
"symbol": "pc",
|
||||||
"to_si_factor": 3.085677581491367e+16
|
"to_si_factor": 3.085677581491367e+16
|
||||||
},
|
},
|
||||||
|
"Q12145303": {
|
||||||
|
"si_name": "Q11573",
|
||||||
|
"symbol": "rd",
|
||||||
|
"to_si_factor": 5.02921
|
||||||
|
},
|
||||||
"Q121965382": {
|
"Q121965382": {
|
||||||
"si_name": "Q121965382",
|
"si_name": "Q121965382",
|
||||||
"symbol": "mol/mol",
|
"symbol": "mol/mol",
|
||||||
@@ -3114,6 +3194,11 @@
|
|||||||
"symbol": "QF",
|
"symbol": "QF",
|
||||||
"to_si_factor": 1e+30
|
"to_si_factor": 1e+30
|
||||||
},
|
},
|
||||||
|
"Q12558489": {
|
||||||
|
"si_name": "Q25517",
|
||||||
|
"symbol": "pk (UK)",
|
||||||
|
"to_si_factor": 0.00909218
|
||||||
|
},
|
||||||
"Q125962250": {
|
"Q125962250": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
"symbol": "Ry",
|
"symbol": "Ry",
|
||||||
@@ -3240,14 +3325,14 @@
|
|||||||
"to_si_factor": 1e-30
|
"to_si_factor": 1e-30
|
||||||
},
|
},
|
||||||
"Q131395783": {
|
"Q131395783": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Rib",
|
"symbol": "Rib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.2379400392853803e+27
|
||||||
},
|
},
|
||||||
"Q131395793": {
|
"Q131395793": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Qib",
|
"symbol": "Qib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.2676506002282294e+30
|
||||||
},
|
},
|
||||||
"Q13147228": {
|
"Q13147228": {
|
||||||
"si_name": "Q844211",
|
"si_name": "Q844211",
|
||||||
@@ -3309,6 +3394,11 @@
|
|||||||
"symbol": "1/K",
|
"symbol": "1/K",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
|
"Q133796439": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "T/mm²",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q13400897": {
|
"Q13400897": {
|
||||||
"si_name": "Q1051665",
|
"si_name": "Q1051665",
|
||||||
"symbol": "g",
|
"symbol": "g",
|
||||||
@@ -3317,18 +3407,23 @@
|
|||||||
"Q13479685": {
|
"Q13479685": {
|
||||||
"si_name": "Q44395",
|
"si_name": "Q44395",
|
||||||
"symbol": "mm H2O",
|
"symbol": "mm H2O",
|
||||||
"to_si_factor": 9.80638
|
"to_si_factor": 9.80665
|
||||||
},
|
},
|
||||||
"Q1351253": {
|
"Q1351253": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Eib",
|
"symbol": "Eib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.152921504606847e+18
|
||||||
},
|
},
|
||||||
"Q1351334": {
|
"Q1351334": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
"symbol": "Pib",
|
"symbol": "Pib",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
|
"Q135373020": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "GTexel",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q135415097": {
|
"Q135415097": {
|
||||||
"si_name": "Q25236",
|
"si_name": "Q25236",
|
||||||
"symbol": "shp",
|
"symbol": "shp",
|
||||||
@@ -3380,9 +3475,9 @@
|
|||||||
"to_si_factor": 1e-06
|
"to_si_factor": 1e-06
|
||||||
},
|
},
|
||||||
"Q136039973": {
|
"Q136039973": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "FPS",
|
"symbol": "FPS",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q1361854": {
|
"Q1361854": {
|
||||||
"si_name": "Q11570",
|
"si_name": "Q11570",
|
||||||
@@ -3450,9 +3545,9 @@
|
|||||||
"to_si_factor": 1000000000000.0
|
"to_si_factor": 1000000000000.0
|
||||||
},
|
},
|
||||||
"Q139054848": {
|
"Q139054848": {
|
||||||
"si_name": null,
|
"si_name": "Q117899185",
|
||||||
"symbol": "A·h/m²",
|
"symbol": "A·h/m²",
|
||||||
"to_si_factor": null
|
"to_si_factor": 3600.0
|
||||||
},
|
},
|
||||||
"Q139086088": {
|
"Q139086088": {
|
||||||
"si_name": "Q69425409",
|
"si_name": "Q69425409",
|
||||||
@@ -3642,7 +3737,7 @@
|
|||||||
"Q1654435": {
|
"Q1654435": {
|
||||||
"si_name": "Q25250",
|
"si_name": "Q25250",
|
||||||
"symbol": "IRE",
|
"symbol": "IRE",
|
||||||
"to_si_factor": 0.007
|
"to_si_factor": 0.007143
|
||||||
},
|
},
|
||||||
"Q16673974": {
|
"Q16673974": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -3820,9 +3915,9 @@
|
|||||||
"to_si_factor": 0.01
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
"Q18434272": {
|
"Q18434272": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "°Balling",
|
"symbol": "°Balling",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
"Q185078": {
|
"Q185078": {
|
||||||
"si_name": "Q25343",
|
"si_name": "Q25343",
|
||||||
@@ -3850,9 +3945,9 @@
|
|||||||
"to_si_factor": 1e-21
|
"to_si_factor": 1e-21
|
||||||
},
|
},
|
||||||
"Q188768": {
|
"Q188768": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "FLOPS",
|
"symbol": "FLOPS",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q190095": {
|
"Q190095": {
|
||||||
"si_name": "Q190095",
|
"si_name": "Q190095",
|
||||||
@@ -3989,6 +4084,11 @@
|
|||||||
"symbol": "ppb",
|
"symbol": "ppb",
|
||||||
"to_si_factor": 1e-09
|
"to_si_factor": 1e-09
|
||||||
},
|
},
|
||||||
|
"Q206037": {
|
||||||
|
"si_name": "Q6137407",
|
||||||
|
"symbol": "r/min",
|
||||||
|
"to_si_factor": 0.0166667
|
||||||
|
},
|
||||||
"Q2064166": {
|
"Q2064166": {
|
||||||
"si_name": "Q179836",
|
"si_name": "Q179836",
|
||||||
"symbol": "fc",
|
"symbol": "fc",
|
||||||
@@ -4320,9 +4420,9 @@
|
|||||||
"to_si_factor": 10.0
|
"to_si_factor": 10.0
|
||||||
},
|
},
|
||||||
"Q2243141": {
|
"Q2243141": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "Gb/s",
|
"symbol": "Gb/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000.0
|
||||||
},
|
},
|
||||||
"Q2254856": {
|
"Q2254856": {
|
||||||
"si_name": "Q25343",
|
"si_name": "Q25343",
|
||||||
@@ -4335,9 +4435,9 @@
|
|||||||
"to_si_factor": 0.00508
|
"to_si_factor": 0.00508
|
||||||
},
|
},
|
||||||
"Q2269250": {
|
"Q2269250": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "kb/s",
|
"symbol": "kb/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000.0
|
||||||
},
|
},
|
||||||
"Q2278977": {
|
"Q2278977": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -4535,9 +4635,9 @@
|
|||||||
"to_si_factor": 898755178700.0
|
"to_si_factor": 898755178700.0
|
||||||
},
|
},
|
||||||
"Q25325238": {
|
"Q25325238": {
|
||||||
"si_name": null,
|
"si_name": "Q106919394",
|
||||||
"symbol": "bhp/cm³",
|
"symbol": "bhp/cm³",
|
||||||
"to_si_factor": null
|
"to_si_factor": 745700000.0
|
||||||
},
|
},
|
||||||
"Q253276": {
|
"Q253276": {
|
||||||
"si_name": "Q11573",
|
"si_name": "Q11573",
|
||||||
@@ -4545,9 +4645,9 @@
|
|||||||
"to_si_factor": 1609.344
|
"to_si_factor": 1609.344
|
||||||
},
|
},
|
||||||
"Q2533495": {
|
"Q2533495": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "°P",
|
"symbol": "°P",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
"Q25343": {
|
"Q25343": {
|
||||||
"si_name": "Q25343",
|
"si_name": "Q25343",
|
||||||
@@ -4885,9 +4985,9 @@
|
|||||||
"to_si_factor": 9.80665
|
"to_si_factor": 9.80665
|
||||||
},
|
},
|
||||||
"Q28657331": {
|
"Q28657331": {
|
||||||
"si_name": null,
|
"si_name": "Q106688958",
|
||||||
"symbol": "erg/(s cm²)",
|
"symbol": "erg/(s cm²)",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.001
|
||||||
},
|
},
|
||||||
"Q28683485": {
|
"Q28683485": {
|
||||||
"si_name": "Q28683485",
|
"si_name": "Q28683485",
|
||||||
@@ -4920,9 +5020,9 @@
|
|||||||
"to_si_factor": 0.001
|
"to_si_factor": 0.001
|
||||||
},
|
},
|
||||||
"Q29463526": {
|
"Q29463526": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "hr/yr",
|
"symbol": "hr/yr",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.14155e-06
|
||||||
},
|
},
|
||||||
"Q296936": {
|
"Q296936": {
|
||||||
"si_name": "Q25269",
|
"si_name": "Q25269",
|
||||||
@@ -5155,9 +5255,9 @@
|
|||||||
"to_si_factor": 1e-15
|
"to_si_factor": 1e-15
|
||||||
},
|
},
|
||||||
"Q3194304": {
|
"Q3194304": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "kb",
|
"symbol": "kb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000.0
|
||||||
},
|
},
|
||||||
"Q3196665": {
|
"Q3196665": {
|
||||||
"si_name": "Q215571",
|
"si_name": "Q215571",
|
||||||
@@ -5295,9 +5395,9 @@
|
|||||||
"to_si_factor": 3517.0
|
"to_si_factor": 3517.0
|
||||||
},
|
},
|
||||||
"Q3332814": {
|
"Q3332814": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Mb",
|
"symbol": "Mb",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000.0
|
||||||
},
|
},
|
||||||
"Q33680": {
|
"Q33680": {
|
||||||
"si_name": "Q33680",
|
"si_name": "Q33680",
|
||||||
@@ -5390,9 +5490,9 @@
|
|||||||
"to_si_factor": 3.085677581e+22
|
"to_si_factor": 3.085677581e+22
|
||||||
},
|
},
|
||||||
"Q3815076": {
|
"Q3815076": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Kib",
|
"symbol": "Kib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1024.0
|
||||||
},
|
},
|
||||||
"Q3858002": {
|
"Q3858002": {
|
||||||
"si_name": "Q25406",
|
"si_name": "Q25406",
|
||||||
@@ -5410,9 +5510,9 @@
|
|||||||
"to_si_factor": 0.3048
|
"to_si_factor": 0.3048
|
||||||
},
|
},
|
||||||
"Q389062": {
|
"Q389062": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Tib",
|
"symbol": "Tib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1099511627776.0
|
||||||
},
|
},
|
||||||
"Q3902688": {
|
"Q3902688": {
|
||||||
"si_name": "Q25517",
|
"si_name": "Q25517",
|
||||||
@@ -5534,6 +5634,11 @@
|
|||||||
"symbol": "D",
|
"symbol": "D",
|
||||||
"to_si_factor": 3.335640951981521e-30
|
"to_si_factor": 3.335640951981521e-30
|
||||||
},
|
},
|
||||||
|
"Q4063874": {
|
||||||
|
"si_name": "Q21401573",
|
||||||
|
"symbol": "amg",
|
||||||
|
"to_si_factor": 2.6868e+25
|
||||||
|
},
|
||||||
"Q4068266": {
|
"Q4068266": {
|
||||||
"si_name": "Q11570",
|
"si_name": "Q11570",
|
||||||
"symbol": "Ʒ",
|
"symbol": "Ʒ",
|
||||||
@@ -5640,9 +5745,9 @@
|
|||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q474533": {
|
"Q474533": {
|
||||||
"si_name": null,
|
"si_name": "Q25272",
|
||||||
"symbol": "At",
|
"symbol": "At",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q48013": {
|
"Q48013": {
|
||||||
"si_name": "Q11570",
|
"si_name": "Q11570",
|
||||||
@@ -5705,14 +5810,14 @@
|
|||||||
"to_si_factor": 0.01
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
"Q50094": {
|
"Q50094": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Np",
|
"symbol": "Np",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q50098": {
|
"Q50098": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "B",
|
"symbol": "B",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q50190518": {
|
"Q50190518": {
|
||||||
"si_name": "Q25377184",
|
"si_name": "Q25377184",
|
||||||
@@ -5755,9 +5860,9 @@
|
|||||||
"to_si_factor": 1000.0
|
"to_si_factor": 1000.0
|
||||||
},
|
},
|
||||||
"Q524410": {
|
"Q524410": {
|
||||||
"si_name": null,
|
"si_name": "Q11574",
|
||||||
"symbol": "Ga",
|
"symbol": "Ga",
|
||||||
"to_si_factor": null
|
"to_si_factor": 3.15576e+16
|
||||||
},
|
},
|
||||||
"Q531": {
|
"Q531": {
|
||||||
"si_name": "Q11573",
|
"si_name": "Q11573",
|
||||||
@@ -5765,9 +5870,9 @@
|
|||||||
"to_si_factor": 9460730472580800.0
|
"to_si_factor": 9460730472580800.0
|
||||||
},
|
},
|
||||||
"Q5329": {
|
"Q5329": {
|
||||||
"si_name": null,
|
"si_name": "Q50098",
|
||||||
"symbol": "dB",
|
"symbol": "dB",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.1
|
||||||
},
|
},
|
||||||
"Q53393488": {
|
"Q53393488": {
|
||||||
"si_name": "Q39369",
|
"si_name": "Q39369",
|
||||||
@@ -6410,9 +6515,9 @@
|
|||||||
"to_si_factor": 3.4262590996353905
|
"to_si_factor": 3.4262590996353905
|
||||||
},
|
},
|
||||||
"Q549389": {
|
"Q549389": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "b/s",
|
"symbol": "b/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q550341": {
|
"Q550341": {
|
||||||
"si_name": "Q550341",
|
"si_name": "Q550341",
|
||||||
@@ -6440,9 +6545,9 @@
|
|||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q5558595": {
|
"Q5558595": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "GFLOPS",
|
"symbol": "GFLOPS",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000.0
|
||||||
},
|
},
|
||||||
"Q55663153": {
|
"Q55663153": {
|
||||||
"si_name": "Q55663153",
|
"si_name": "Q55663153",
|
||||||
@@ -6519,20 +6624,25 @@
|
|||||||
"symbol": "GtCO2eq",
|
"symbol": "GtCO2eq",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
|
"Q57084839": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "gCDE/km",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q57084901": {
|
"Q57084901": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
"symbol": "KgCO2eq",
|
"symbol": "KgCO2eq",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q57084921": {
|
"Q57084921": {
|
||||||
"si_name": null,
|
"si_name": "Q11570",
|
||||||
"symbol": "gCO2eq",
|
"symbol": "gCO2eq",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.001
|
||||||
},
|
},
|
||||||
"Q5711255": {
|
"Q5711255": {
|
||||||
"si_name": null,
|
"si_name": "Q25517",
|
||||||
"symbol": "aL",
|
"symbol": "aL",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1e-21
|
||||||
},
|
},
|
||||||
"Q5711261": {
|
"Q5711261": {
|
||||||
"si_name": "Q25517",
|
"si_name": "Q25517",
|
||||||
@@ -6674,11 +6784,6 @@
|
|||||||
"symbol": "in",
|
"symbol": "in",
|
||||||
"to_si_factor": 0.0254
|
"to_si_factor": 0.0254
|
||||||
},
|
},
|
||||||
"Q61793198": {
|
|
||||||
"si_name": "Q11573",
|
|
||||||
"symbol": "rd",
|
|
||||||
"to_si_factor": 5.02921
|
|
||||||
},
|
|
||||||
"Q61794766": {
|
"Q61794766": {
|
||||||
"si_name": "Q11573",
|
"si_name": "Q11573",
|
||||||
"symbol": "ch (US survey)",
|
"symbol": "ch (US survey)",
|
||||||
@@ -6710,9 +6815,9 @@
|
|||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q61995006": {
|
"Q61995006": {
|
||||||
"si_name": null,
|
"si_name": "Q106682512",
|
||||||
"symbol": "KWth",
|
"symbol": "KWth",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000.0
|
||||||
},
|
},
|
||||||
"Q61996348": {
|
"Q61996348": {
|
||||||
"si_name": "Q794261",
|
"si_name": "Q794261",
|
||||||
@@ -6755,14 +6860,14 @@
|
|||||||
"to_si_factor": 1.1574074e-05
|
"to_si_factor": 1.1574074e-05
|
||||||
},
|
},
|
||||||
"Q64740041": {
|
"Q64740041": {
|
||||||
"si_name": null,
|
"si_name": "Q106688958",
|
||||||
"symbol": "kWh/(m² yr)",
|
"symbol": "kWh/(m² yr)",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.000114
|
||||||
},
|
},
|
||||||
"Q64740314": {
|
"Q64740314": {
|
||||||
"si_name": null,
|
"si_name": "Q106688958",
|
||||||
"symbol": "kWh/(m² day)",
|
"symbol": "kWh/(m² day)",
|
||||||
"to_si_factor": null
|
"to_si_factor": 41.67
|
||||||
},
|
},
|
||||||
"Q64748817": {
|
"Q64748817": {
|
||||||
"si_name": "Q80374519",
|
"si_name": "Q80374519",
|
||||||
@@ -6825,9 +6930,9 @@
|
|||||||
"to_si_factor": 1016.0469088
|
"to_si_factor": 1016.0469088
|
||||||
},
|
},
|
||||||
"Q66778234": {
|
"Q66778234": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "TFLOPS",
|
"symbol": "TFLOPS",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000000000.0
|
||||||
},
|
},
|
||||||
"Q66778809": {
|
"Q66778809": {
|
||||||
"si_name": null,
|
"si_name": null,
|
||||||
@@ -6839,6 +6944,11 @@
|
|||||||
"symbol": "PFLOPS",
|
"symbol": "PFLOPS",
|
||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
|
"Q66778951": {
|
||||||
|
"si_name": null,
|
||||||
|
"symbol": "ZFLOPS",
|
||||||
|
"to_si_factor": null
|
||||||
|
},
|
||||||
"Q67060736": {
|
"Q67060736": {
|
||||||
"si_name": "Q67060736",
|
"si_name": "Q67060736",
|
||||||
"symbol": "W/kg",
|
"symbol": "W/kg",
|
||||||
@@ -7225,9 +7335,9 @@
|
|||||||
"to_si_factor": 1.0
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q7350781": {
|
"Q7350781": {
|
||||||
"si_name": null,
|
"si_name": "Q6137407",
|
||||||
"symbol": "Mb/s",
|
"symbol": "Mb/s",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1000000.0
|
||||||
},
|
},
|
||||||
"Q743895": {
|
"Q743895": {
|
||||||
"si_name": "Q39369",
|
"si_name": "Q39369",
|
||||||
@@ -7545,9 +7655,9 @@
|
|||||||
"to_si_factor": 1.0
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q836941": {
|
"Q836941": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "°Bx",
|
"symbol": "°Bx",
|
||||||
"to_si_factor": null
|
"to_si_factor": 0.01
|
||||||
},
|
},
|
||||||
"Q83853845": {
|
"Q83853845": {
|
||||||
"si_name": "Q83853845",
|
"si_name": "Q83853845",
|
||||||
@@ -7599,6 +7709,11 @@
|
|||||||
"symbol": "hm",
|
"symbol": "hm",
|
||||||
"to_si_factor": 100.0
|
"to_si_factor": 100.0
|
||||||
},
|
},
|
||||||
|
"Q84451486": {
|
||||||
|
"si_name": "Q84451486",
|
||||||
|
"symbol": "K m/W",
|
||||||
|
"to_si_factor": 1.0
|
||||||
|
},
|
||||||
"Q844976": {
|
"Q844976": {
|
||||||
"si_name": "Q2844478",
|
"si_name": "Q2844478",
|
||||||
"symbol": "Oe",
|
"symbol": "Oe",
|
||||||
@@ -7630,9 +7745,9 @@
|
|||||||
"to_si_factor": 1000000000.0
|
"to_si_factor": 1000000000.0
|
||||||
},
|
},
|
||||||
"Q855161": {
|
"Q855161": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "Yib",
|
"symbol": "Yib",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.2089258196146292e+24
|
||||||
},
|
},
|
||||||
"Q856240": {
|
"Q856240": {
|
||||||
"si_name": "Q794261",
|
"si_name": "Q794261",
|
||||||
@@ -7659,6 +7774,11 @@
|
|||||||
"symbol": "abA",
|
"symbol": "abA",
|
||||||
"to_si_factor": 10.0
|
"to_si_factor": 10.0
|
||||||
},
|
},
|
||||||
|
"Q86897783": {
|
||||||
|
"si_name": "Q86897783",
|
||||||
|
"symbol": "Pa² s",
|
||||||
|
"to_si_factor": 1.0
|
||||||
|
},
|
||||||
"Q87047886": {
|
"Q87047886": {
|
||||||
"si_name": "Q87047886",
|
"si_name": "Q87047886",
|
||||||
"symbol": "Pa s/m³",
|
"symbol": "Pa s/m³",
|
||||||
@@ -7680,14 +7800,14 @@
|
|||||||
"to_si_factor": 1e-12
|
"to_si_factor": 1e-12
|
||||||
},
|
},
|
||||||
"Q8799": {
|
"Q8799": {
|
||||||
"si_name": null,
|
"si_name": "Q199",
|
||||||
"symbol": "B",
|
"symbol": "B",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q8805": {
|
"Q8805": {
|
||||||
"si_name": null,
|
"si_name": "Q8805",
|
||||||
"symbol": "bit",
|
"symbol": "bit",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1.0
|
||||||
},
|
},
|
||||||
"Q88296091": {
|
"Q88296091": {
|
||||||
"si_name": "Q25517",
|
"si_name": "Q25517",
|
||||||
@@ -7714,11 +7834,6 @@
|
|||||||
"symbol": "bu (UK)",
|
"symbol": "bu (UK)",
|
||||||
"to_si_factor": 0.03636872
|
"to_si_factor": 0.03636872
|
||||||
},
|
},
|
||||||
"Q89662131": {
|
|
||||||
"si_name": "Q25517",
|
|
||||||
"symbol": "pt (UK)",
|
|
||||||
"to_si_factor": 0.00056826125
|
|
||||||
},
|
|
||||||
"Q89992008": {
|
"Q89992008": {
|
||||||
"si_name": "Q89992008",
|
"si_name": "Q89992008",
|
||||||
"symbol": "F⁻¹",
|
"symbol": "F⁻¹",
|
||||||
@@ -7745,9 +7860,9 @@
|
|||||||
"to_si_factor": null
|
"to_si_factor": null
|
||||||
},
|
},
|
||||||
"Q9048643": {
|
"Q9048643": {
|
||||||
"si_name": null,
|
"si_name": "Q25517",
|
||||||
"symbol": "nl",
|
"symbol": "nl",
|
||||||
"to_si_factor": null
|
"to_si_factor": 1e-12
|
||||||
},
|
},
|
||||||
"Q905912": {
|
"Q905912": {
|
||||||
"si_name": "Q281096",
|
"si_name": "Q281096",
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
There is a command line for developer purposes and for deeper analysis. Here is
|
There is a command line for developer purposes and for deeper analysis. Here is
|
||||||
an example in which the command line is called in the development environment::
|
an example in which the command line is called in the development environment::
|
||||||
|
|
||||||
$ ./manage pyenv.cmd bash --norc --noprofile
|
$ ./manage dev.env
|
||||||
(py3) python -m searx.enginelib --help
|
(dev.env)$ python -m searx.enginelib --help
|
||||||
|
|
||||||
.. hint::
|
.. hint::
|
||||||
|
|
||||||
@@ -46,6 +46,7 @@ ENGINES_CACHE: ExpireCacheSQLite = ExpireCacheSQLite.build_cache(
|
|||||||
name="ENGINES_CACHE",
|
name="ENGINES_CACHE",
|
||||||
MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days
|
MAXHOLD_TIME=60 * 60 * 24 * 7, # 7 days
|
||||||
MAINTENANCE_PERIOD=60 * 60, # 2h
|
MAINTENANCE_PERIOD=60 * 60, # 2h
|
||||||
|
MAX_VALUE_LEN=1024 * 1024 * 1024, # 1MB
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"""Global :py:obj:`searx.cache.ExpireCacheSQLite` instance where the cached
|
"""Global :py:obj:`searx.cache.ExpireCacheSQLite` instance where the cached
|
||||||
@@ -71,9 +72,9 @@ def state():
|
|||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def maintenance(force: bool = True):
|
def maintenance(force: bool = True, truncate: bool = False):
|
||||||
"""Carry out maintenance on cache of the engines."""
|
"""Carry out maintenance on cache of the engines."""
|
||||||
ENGINES_CACHE.maintenance(force=force)
|
ENGINES_CACHE.maintenance(force=force, truncate=truncate)
|
||||||
|
|
||||||
|
|
||||||
class EngineCache:
|
class EngineCache:
|
||||||
@@ -111,8 +112,8 @@ class EngineCache:
|
|||||||
For introspection of the DB, jump into developer environment and run command to
|
For introspection of the DB, jump into developer environment and run command to
|
||||||
show cache state::
|
show cache state::
|
||||||
|
|
||||||
$ ./manage pyenv.cmd bash --norc --noprofile
|
$ ./manage dev.env
|
||||||
(py3) python -m searx.enginelib cache state
|
(dev.env)$ python -m searx.enginelib cache state
|
||||||
|
|
||||||
cache tables and key/values
|
cache tables and key/values
|
||||||
===========================
|
===========================
|
||||||
@@ -159,7 +160,8 @@ class EngineCache:
|
|||||||
def __init__(self, engine_name: str, expire: int | None = None):
|
def __init__(self, engine_name: str, expire: int | None = None):
|
||||||
self.expire: int = expire or ENGINES_CACHE.cfg.MAXHOLD_TIME
|
self.expire: int = expire or ENGINES_CACHE.cfg.MAXHOLD_TIME
|
||||||
_valid = "-_." + string.ascii_letters + string.digits
|
_valid = "-_." + string.ascii_letters + string.digits
|
||||||
self.table_name: str = "".join([c if c in _valid else "_" for c in engine_name])
|
# engine_name is a table and SQL table names must start with a letter
|
||||||
|
self.table_name: str = "eng_" + "".join([c if c in _valid else "_" for c in engine_name])
|
||||||
|
|
||||||
def set(self, key: str, value: t.Any, expire: int | None = None) -> bool:
|
def set(self, key: str, value: t.Any, expire: int | None = None) -> bool:
|
||||||
return ENGINES_CACHE.set(
|
return ENGINES_CACHE.set(
|
||||||
|
|||||||
@@ -116,19 +116,6 @@ class EngineTraits:
|
|||||||
return self.all_locale
|
return self.all_locale
|
||||||
return locales.get_engine_locale(searxng_locale, self.regions, default=default)
|
return locales.get_engine_locale(searxng_locale, self.regions, default=default)
|
||||||
|
|
||||||
def is_locale_supported(self, searxng_locale: str) -> bool:
|
|
||||||
"""A *locale* (SearXNG's internal representation) is considered to be
|
|
||||||
supported by the engine if the *region* or the *language* is supported
|
|
||||||
by the engine.
|
|
||||||
|
|
||||||
For verification the functions :py:func:`EngineTraits.get_region` and
|
|
||||||
:py:func:`EngineTraits.get_language` are used.
|
|
||||||
"""
|
|
||||||
if self.data_type == "traits_v1":
|
|
||||||
return bool(self.get_region(searxng_locale) or self.get_language(searxng_locale))
|
|
||||||
|
|
||||||
raise TypeError("engine traits of type %s is unknown" % self.data_type)
|
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
"""Create a copy of the dataclass object."""
|
"""Create a copy of the dataclass object."""
|
||||||
return EngineTraits(**dataclasses.asdict(self))
|
return EngineTraits(**dataclasses.asdict(self))
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ def request(query, params):
|
|||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
# sometimes 360search returns empty response when called from non-chinese ips
|
||||||
|
if not resp.text or not resp.text.strip():
|
||||||
|
return []
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
"""500px_ is a online network for photographers with millions of members
|
||||||
|
worldwide. Photographers come to 500px to discover and share incredible photos,
|
||||||
|
gain meaningful exposure, compete in photo contests, and license their photos
|
||||||
|
through our exclusive distribution partners.
|
||||||
|
|
||||||
|
.. _500px: https://500px.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
|
||||||
|
# about
|
||||||
|
about = {
|
||||||
|
"website": "https://500px.com",
|
||||||
|
"wikidata_id": "Q354894",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
base_url = "https://500px.com"
|
||||||
|
api_url = "https://api.500px.com"
|
||||||
|
|
||||||
|
categories = ["images"]
|
||||||
|
|
||||||
|
paging = True
|
||||||
|
results_per_page = 30
|
||||||
|
"""Number of results to return in the request.
|
||||||
|
|
||||||
|
The default was taken from the WEB UI, where the GraphQL query sets the value to
|
||||||
|
*static*: ``first: 30``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
SXNG_query = """query PhotoSearchPaginationContainerQuery(
|
||||||
|
$first: Int, $cursor: String, $search: String!, $sort: PhotoSort, $filters: [PhotoSearchFilter!], $nlp: Boolean
|
||||||
|
) {
|
||||||
|
...SXNG_query
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment SXNG_query on Query {
|
||||||
|
photoSearch(sort: $sort, first: $first, after: $cursor, search: $search, filters: $filters, nlp: $nlp) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
canonicalPath
|
||||||
|
name
|
||||||
|
description
|
||||||
|
width
|
||||||
|
height
|
||||||
|
photographer: uploader {
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
images(sizes: [35, 33]) {
|
||||||
|
size
|
||||||
|
url
|
||||||
|
jpegUrl
|
||||||
|
webpUrl
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def setup(_) -> bool:
|
||||||
|
global SXNG_query # pylint: disable=global-statement
|
||||||
|
rand_str: str = "".join(random.choice(string.ascii_letters) for _ in range(5))
|
||||||
|
SXNG_query = SXNG_query.replace("SXNG_query", "PhotoSearchPaginationContainer_query_1" + rand_str)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
# cursor is the base64 hash of the string "pos-<offset-1>", e.g. "pos-29" -> "cG9zLTI5"
|
||||||
|
offset = ((params["pageno"] - 1) * results_per_page) - 1
|
||||||
|
cursor = codecs.encode(f"pos-{offset}".encode("utf-8"), "base64").decode("utf-8")
|
||||||
|
|
||||||
|
params["url"] = f"{api_url}/graphql"
|
||||||
|
params["method"] = "POST"
|
||||||
|
params["json"] = {
|
||||||
|
"operationName": "PhotoSearchPaginationContainerQuery",
|
||||||
|
"variables": {
|
||||||
|
"first": results_per_page,
|
||||||
|
"cursor": cursor,
|
||||||
|
"search": query,
|
||||||
|
"sort": "RELEVANCE",
|
||||||
|
"filters": [],
|
||||||
|
"nlp": False,
|
||||||
|
},
|
||||||
|
"query": SXNG_query,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
json_data = resp.json()["data"]["photoSearch"]
|
||||||
|
|
||||||
|
for edge in json_data["edges"]:
|
||||||
|
node = edge["node"] # pyright: ignore[reportAny]
|
||||||
|
if not node["images"]:
|
||||||
|
continue
|
||||||
|
images: list[dict[str, str]] = sorted(node["images"], key=lambda i: i["size"])
|
||||||
|
thumbnail_src = images[0]["url"]
|
||||||
|
img_src = images[-1]["url"]
|
||||||
|
res.add(
|
||||||
|
res.types.LegacyResult(
|
||||||
|
{
|
||||||
|
"template": "images.html",
|
||||||
|
"url": base_url + node["canonicalPath"],
|
||||||
|
"thumbnail_src": thumbnail_src,
|
||||||
|
"img_src": img_src,
|
||||||
|
"title": node["name"],
|
||||||
|
"content": node["description"],
|
||||||
|
"author": node["photographer"]["displayName"],
|
||||||
|
"resolution": f"{node['width']}x{node['height']}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -12,6 +12,7 @@ import typing as t
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
|
import os
|
||||||
from os.path import realpath, dirname
|
from os.path import realpath, dirname
|
||||||
|
|
||||||
import types
|
import types
|
||||||
@@ -278,6 +279,8 @@ def load_engines(engine_list: list[dict[str, t.Any]]):
|
|||||||
else:
|
else:
|
||||||
# if an engine can't be loaded (if for example the engine is missing
|
# if an engine can't be loaded (if for example the engine is missing
|
||||||
# tor or some other requirements) its set to inactive!
|
# tor or some other requirements) its set to inactive!
|
||||||
logger.error("loading engine %s failed: set engine to inactive!", engine_data.get("name", "???"))
|
logger.error(
|
||||||
|
f"(PID {os.getpid()}) loading engine %s failed: set engine to inactive!", engine_data.get("name", "???")
|
||||||
|
)
|
||||||
engine_data["inactive"] = True
|
engine_data["inactive"] = True
|
||||||
return engines
|
return engines
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ url_xpath = './h4/a/@href'
|
|||||||
title_xpath = './h4/a[1]'
|
title_xpath = './h4/a[1]'
|
||||||
content_xpath = './/p[1]'
|
content_xpath = './/p[1]'
|
||||||
correction_xpath = '//*[@id="didYouMean"]//a'
|
correction_xpath = '//*[@id="didYouMean"]//a'
|
||||||
number_of_results_xpath = '//*[@id="totalResults"]'
|
|
||||||
name_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@name'
|
name_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@name'
|
||||||
value_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@value'
|
value_token_xpath = '//form[@id="searchForm"]/input[@type="hidden"]/@value'
|
||||||
|
|
||||||
@@ -107,14 +106,6 @@ def response(resp):
|
|||||||
for correction in eval_xpath_list(dom, correction_xpath):
|
for correction in eval_xpath_list(dom, correction_xpath):
|
||||||
results.append({'correction': extract_text(correction)})
|
results.append({'correction': extract_text(correction)})
|
||||||
|
|
||||||
# get number of results
|
|
||||||
number_of_results = eval_xpath(dom, number_of_results_xpath)
|
|
||||||
if number_of_results:
|
|
||||||
try:
|
|
||||||
results.append({'number_of_results': int(extract_text(number_of_results))})
|
|
||||||
except: # pylint: disable=bare-except
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Update the tokens to the newest ones
|
# Update the tokens to the newest ones
|
||||||
token_str = _get_tokens(dom)
|
token_str = _get_tokens(dom)
|
||||||
CACHE.set('ahmia-tokens', token_str, expire=60 * 60)
|
CACHE.set('ahmia-tokens', token_str, expire=60 * 60)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ if t.TYPE_CHECKING:
|
|||||||
|
|
||||||
about = {
|
about = {
|
||||||
"website": "https://www.aol.com",
|
"website": "https://www.aol.com",
|
||||||
"wikidata_id": "Q2407",
|
"wikidata_id": "Q27585",
|
||||||
"official_api_documentation": None,
|
"official_api_documentation": None,
|
||||||
"use_official_api": False,
|
"use_official_api": False,
|
||||||
"require_api_key": False,
|
"require_api_key": False,
|
||||||
@@ -60,6 +60,8 @@ base_url = "https://search.aol.com"
|
|||||||
time_range_map = {"day": "1d", "week": "1w", "month": "1m", "year": "1y"}
|
time_range_map = {"day": "1d", "week": "1w", "month": "1m", "year": "1y"}
|
||||||
safesearch_map = {0: "p", 1: "r", 2: "i"}
|
safesearch_map = {0: "p", 1: "r", 2: "i"}
|
||||||
|
|
||||||
|
enable_http2 = False
|
||||||
|
|
||||||
|
|
||||||
def init(_):
|
def init(_):
|
||||||
if search_type not in ("search", "image", "video"):
|
if search_type not in ("search", "image", "video"):
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ about = {
|
|||||||
|
|
||||||
categories = ['images']
|
categories = ['images']
|
||||||
paging = True
|
paging = True
|
||||||
nb_per_page = 20
|
page_size = 20
|
||||||
|
|
||||||
search_api = 'https://api.artic.edu/api/v1/artworks/search?'
|
search_api = 'https://api.artic.edu/api/v1/artworks/search?'
|
||||||
image_api = 'https://www.artic.edu/iiif/2/'
|
image_api = 'https://www.artic.edu/iiif/2/'
|
||||||
@@ -34,7 +34,7 @@ def request(query, params):
|
|||||||
'q': query,
|
'q': query,
|
||||||
'page': params['pageno'],
|
'page': params['pageno'],
|
||||||
'fields': 'id,title,artist_display,medium_display,image_id,date_display,dimensions,artist_titles',
|
'fields': 'id,title,artist_display,medium_display,image_id,date_display,dimensions,artist_titles',
|
||||||
'limit': nb_per_page,
|
'limit': page_size,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
params['url'] = search_api + args
|
params['url'] = search_api + args
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
"""Ask.com"""
|
|
||||||
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
import dateutil
|
|
||||||
from lxml import html
|
|
||||||
from searx import utils
|
|
||||||
|
|
||||||
# Metadata
|
|
||||||
about = {
|
|
||||||
"website": "https://www.ask.com/",
|
|
||||||
"wikidata_id": 'Q847564',
|
|
||||||
"official_api_documentation": None,
|
|
||||||
"use_official_api": False,
|
|
||||||
"require_api_key": False,
|
|
||||||
"results": "HTML",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Engine Configuration
|
|
||||||
categories = ['general']
|
|
||||||
paging = True
|
|
||||||
max_page = 5
|
|
||||||
"""Ask.com has at max 5 pages."""
|
|
||||||
|
|
||||||
# Base URL
|
|
||||||
base_url = "https://www.ask.com/web"
|
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
|
||||||
|
|
||||||
query_params = {
|
|
||||||
"q": query,
|
|
||||||
"page": params["pageno"],
|
|
||||||
}
|
|
||||||
|
|
||||||
params["url"] = f"{base_url}?{urlencode(query_params)}"
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
|
||||||
|
|
||||||
start_tag = 'window.MESON.initialState = {'
|
|
||||||
end_tag = '}};'
|
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
|
||||||
script = utils.eval_xpath_getindex(dom, '//script', 0, default=None).text
|
|
||||||
|
|
||||||
pos = script.index(start_tag) + len(start_tag) - 1
|
|
||||||
script = script[pos:]
|
|
||||||
pos = script.index(end_tag) + len(end_tag) - 1
|
|
||||||
script = script[:pos]
|
|
||||||
|
|
||||||
json_resp = utils.js_obj_str_to_python(script)
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
for item in json_resp['search']['webResults']['results']:
|
|
||||||
|
|
||||||
pubdate_original = item.get('pubdate_original')
|
|
||||||
if pubdate_original:
|
|
||||||
pubdate_original = dateutil.parser.parse(pubdate_original)
|
|
||||||
metadata = [item.get(field) for field in ['category_l1', 'catsy'] if item.get(field)]
|
|
||||||
|
|
||||||
results.append(
|
|
||||||
{
|
|
||||||
"url": item['url'].split('&ueid')[0],
|
|
||||||
"title": item['title'],
|
|
||||||
"content": item['abstract'],
|
|
||||||
"publishedDate": pubdate_original,
|
|
||||||
# "thumbnail": item.get('image_url') or None, # these are not thumbs / to large
|
|
||||||
"metadata": ' | '.join(metadata),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return results
|
|
||||||
@@ -26,7 +26,7 @@ base_url = (
|
|||||||
|
|
||||||
# engine dependent config
|
# engine dependent config
|
||||||
paging = True
|
paging = True
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
|
|
||||||
# shortcuts for advanced search
|
# shortcuts for advanced search
|
||||||
shortcut_dict = {
|
shortcut_dict = {
|
||||||
@@ -57,12 +57,12 @@ def request(query, params):
|
|||||||
query = re.sub(key, val, query)
|
query = re.sub(key, val, query)
|
||||||
|
|
||||||
# basic search
|
# basic search
|
||||||
offset = (params['pageno'] - 1) * number_of_results
|
offset = (params['pageno'] - 1) * page_size
|
||||||
|
|
||||||
string_args = {
|
string_args = {
|
||||||
'query': urlencode({'query': query}),
|
'query': urlencode({'query': query}),
|
||||||
'offset': offset,
|
'offset': offset,
|
||||||
'hits': number_of_results,
|
'hits': page_size,
|
||||||
}
|
}
|
||||||
|
|
||||||
params['url'] = base_url.format(**string_args)
|
params['url'] = base_url.format(**string_args)
|
||||||
|
|||||||
@@ -51,11 +51,10 @@ def request(query, params):
|
|||||||
}
|
}
|
||||||
|
|
||||||
params["url"] = f"{base_url}?{urlencode(query_params)}"
|
params["url"] = f"{base_url}?{urlencode(query_params)}"
|
||||||
params["headers"]["Referer"] = "https://www.bilibili.com"
|
params["headers"]["Referer"] = "https://www.bilibili.com/"
|
||||||
|
params["headers"]["Accept"] = "application/json, text/javascript, */*; q=0.01"
|
||||||
params["cookies"] = cookie
|
params["cookies"] = cookie
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
search_res = resp.json()
|
search_res = resp.json()
|
||||||
|
|||||||
+3
-16
@@ -13,7 +13,6 @@ implementations are shared by other engines:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import re
|
|
||||||
import typing as t
|
import typing as t
|
||||||
from urllib.parse import parse_qs, urlencode, urlparse
|
from urllib.parse import parse_qs, urlencode, urlparse
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ _safesearch_map: dict[int, str] = {
|
|||||||
}
|
}
|
||||||
"""Filter results. 0: None, 1: Moderate, 2: Strict"""
|
"""Filter results. 0: None, 1: Moderate, 2: Strict"""
|
||||||
|
|
||||||
base_url = "https://www.bing.com/search"
|
base_url = "https://www.bing.com"
|
||||||
"""Bing-Web search URL"""
|
"""Bing-Web search URL"""
|
||||||
|
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ def override_accept_language(params: "OnlineParams", engine_region: str | None)
|
|||||||
params["headers"]["Accept-Language"] = f"{engine_region},{lang};q=0.9"
|
params["headers"]["Accept-Language"] = f"{engine_region},{lang};q=0.9"
|
||||||
|
|
||||||
|
|
||||||
def request(query: str, params: "OnlineParams") -> "OnlineParams":
|
def request(query: str, params: "OnlineParams"):
|
||||||
"""Assemble a Bing-Web request."""
|
"""Assemble a Bing-Web request."""
|
||||||
|
|
||||||
engine_region = traits.get_region(params["searxng_locale"], traits.all_locale)
|
engine_region = traits.get_region(params["searxng_locale"], traits.all_locale)
|
||||||
@@ -110,13 +109,7 @@ def request(query: str, params: "OnlineParams") -> "OnlineParams":
|
|||||||
if locale_params:
|
if locale_params:
|
||||||
query_params.update(locale_params)
|
query_params.update(locale_params)
|
||||||
|
|
||||||
params["url"] = f"{base_url}?{urlencode(query_params)}"
|
params["url"] = f"{base_url}/search?{urlencode(query_params)}"
|
||||||
|
|
||||||
# in some regions where geoblocking is employed (e.g. China),
|
|
||||||
# www.bing.com redirects to the regional version of Bing
|
|
||||||
params["allow_redirects"] = True
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
|
def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
|
||||||
@@ -159,12 +152,6 @@ def response(resp: "SXNG_Response") -> list[dict[str, t.Any]]:
|
|||||||
|
|
||||||
results.append({"url": href, "title": title, "content": content})
|
results.append({"url": href, "title": title, "content": content})
|
||||||
|
|
||||||
if results:
|
|
||||||
result_len_container = "".join(eval_xpath(dom, '//span[@class="sb_count"]//text()'))
|
|
||||||
result_len_container = re.sub(r"[^0-9]", "", result_len_container)
|
|
||||||
if result_len_container:
|
|
||||||
results.append({"number_of_results": int(result_len_container)})
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ time_map = {
|
|||||||
"year": 60 * 24 * 365,
|
"year": 60 * 24 * 365,
|
||||||
}
|
}
|
||||||
|
|
||||||
base_url = "https://www.bing.com/images/async"
|
base_url = "https://www.bing.com"
|
||||||
"""Bing-Image search URL"""
|
"""Bing-Image search URL"""
|
||||||
|
|
||||||
|
|
||||||
@@ -64,9 +64,7 @@ def request(query, params):
|
|||||||
if params["time_range"]:
|
if params["time_range"]:
|
||||||
query_params["qft"] = "filterui:age-lt%s" % time_map[params["time_range"]]
|
query_params["qft"] = "filterui:age-lt%s" % time_map[params["time_range"]]
|
||||||
|
|
||||||
params["url"] = base_url + "?" + urlencode(query_params)
|
params["url"] = base_url + "/images/async?" + urlencode(query_params)
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ time_map = {
|
|||||||
difference of *last day* and *last week* in the result list is just marginally.
|
difference of *last day* and *last week* in the result list is just marginally.
|
||||||
Bing does not have news range ``year`` / we use ``month`` instead."""
|
Bing does not have news range ``year`` / we use ``month`` instead."""
|
||||||
|
|
||||||
base_url = "https://www.bing.com/news/infinitescrollajax"
|
base_url = "https://www.bing.com"
|
||||||
"""Bing (News) search URL"""
|
"""Bing (News) search URL"""
|
||||||
|
|
||||||
|
|
||||||
@@ -74,9 +74,7 @@ def request(query, params):
|
|||||||
if params["time_range"]:
|
if params["time_range"]:
|
||||||
query_params["qft"] = time_map.get(params["time_range"], 'interval="9"')
|
query_params["qft"] = time_map.get(params["time_range"], 'interval="9"')
|
||||||
|
|
||||||
params["url"] = base_url + "?" + urlencode(query_params)
|
params["url"] = base_url + "/news/infinitescrollajax?" + urlencode(query_params)
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ paging = True
|
|||||||
safesearch = True
|
safesearch = True
|
||||||
time_range_support = True
|
time_range_support = True
|
||||||
|
|
||||||
base_url = "https://www.bing.com/videos/asyncv2"
|
base_url = "https://www.bing.com"
|
||||||
"""Bing-Video search URL"""
|
"""Bing-Video search URL"""
|
||||||
|
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ def request(query, params):
|
|||||||
query_params["form"] = "VRFLTR"
|
query_params["form"] = "VRFLTR"
|
||||||
query_params["qft"] = " filterui:videoage-lt%s" % time_map[params["time_range"]]
|
query_params["qft"] = " filterui:videoage-lt%s" % time_map[params["time_range"]]
|
||||||
|
|
||||||
params["url"] = base_url + "?" + urlencode(query_params)
|
params["url"] = base_url + "/videos/asyncv2?" + urlencode(query_params)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
"""Cara_ is a social media and portfolio-sharing platform for artists and art
|
||||||
|
enthusiasts.
|
||||||
|
|
||||||
|
With the widespread use of generative AI, Cara_ decided to build a place that
|
||||||
|
filters out gen AI images so that people searching for authentic creatives and
|
||||||
|
images can do so easily.
|
||||||
|
|
||||||
|
.. _Cara: https://cara.app/about
|
||||||
|
"""
|
||||||
|
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://cara.app",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
base_url = "https://cara.app"
|
||||||
|
images_url = "https://images.cara.app"
|
||||||
|
|
||||||
|
categories = ["images"]
|
||||||
|
paging = True
|
||||||
|
results_per_page = 24
|
||||||
|
|
||||||
|
# if using HTTP2, we get blocked immediately
|
||||||
|
enable_http2 = False
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
args = {
|
||||||
|
"q": query,
|
||||||
|
"sortBy": "Top",
|
||||||
|
"take": results_per_page,
|
||||||
|
"skip": (params["pageno"] - 1) * results_per_page,
|
||||||
|
}
|
||||||
|
params["url"] = f"{base_url}/api/search/portfolio-posts?{urlencode(args)}"
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
json_data: list[dict[str, t.Any]] = resp.json()
|
||||||
|
|
||||||
|
for result in json_data:
|
||||||
|
thumbnail, img = None, None
|
||||||
|
|
||||||
|
i: dict[str, str]
|
||||||
|
for i in result["images"]:
|
||||||
|
if thumbnail is None or i["isCoverImg"]:
|
||||||
|
thumbnail = i
|
||||||
|
|
||||||
|
if img is None or not i["isCoverImg"]:
|
||||||
|
img = i
|
||||||
|
|
||||||
|
if not thumbnail or not img:
|
||||||
|
continue
|
||||||
|
|
||||||
|
res.add(
|
||||||
|
res.types.LegacyResult(
|
||||||
|
{
|
||||||
|
"template": "images.html",
|
||||||
|
"url": f"{base_url}/post/{result['id']}",
|
||||||
|
"thumbnail_src": f"{images_url}/{thumbnail['src']}?height=256",
|
||||||
|
"img_src": f"{images_url}/{img['src']}",
|
||||||
|
"title": result["title"],
|
||||||
|
"content": result["content"],
|
||||||
|
"author": result["name"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -16,7 +16,7 @@ about = {
|
|||||||
paging = True
|
paging = True
|
||||||
categories = []
|
categories = []
|
||||||
|
|
||||||
number_of_results = 20
|
page_size = 20
|
||||||
skip_premium = True
|
skip_premium = True
|
||||||
|
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ thumbnail_format = "crop-240x300"
|
|||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
args = {'query': query, 'limit': number_of_results, 'offset': (params['pageno'] - 1) * number_of_results}
|
args = {'query': query, 'limit': page_size, 'offset': (params['pageno'] - 1) * page_size}
|
||||||
params['url'] = f"{base_url}/v2/search-gateway/recipes?{urlencode(args)}"
|
params['url'] = f"{base_url}/v2/search-gateway/recipes?{urlencode(args)}"
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ the API key in the engine :ref:`core engine config`."""
|
|||||||
|
|
||||||
categories = ["science", "scientific publications"]
|
categories = ["science", "scientific publications"]
|
||||||
paging = True
|
paging = True
|
||||||
nb_per_page = 10
|
page_size = 10
|
||||||
base_url = "https://api.core.ac.uk/v3/search/works/"
|
base_url = "https://api.core.ac.uk/v3/search/works/"
|
||||||
|
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ def request(query: str, params: "OnlineParams") -> None:
|
|||||||
# API v3 uses different parameters
|
# API v3 uses different parameters
|
||||||
search_params = {
|
search_params = {
|
||||||
"q": query,
|
"q": query,
|
||||||
"offset": (params["pageno"] - 1) * nb_per_page,
|
"offset": (params["pageno"] - 1) * page_size,
|
||||||
"limit": nb_per_page,
|
"limit": page_size,
|
||||||
"sort": "relevance",
|
"sort": "relevance",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ about = {
|
|||||||
# engine dependent config
|
# engine dependent config
|
||||||
categories = ["videos"]
|
categories = ["videos"]
|
||||||
paging = True
|
paging = True
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
|
|
||||||
time_range_support = True
|
time_range_support = True
|
||||||
time_delta_dict = {
|
time_delta_dict = {
|
||||||
@@ -113,7 +113,7 @@ def request(query, params):
|
|||||||
"password_protected": "false",
|
"password_protected": "false",
|
||||||
"private": "false",
|
"private": "false",
|
||||||
"sort": "relevance",
|
"sort": "relevance",
|
||||||
"limit": number_of_results,
|
"limit": page_size,
|
||||||
"fields": ",".join(result_fields),
|
"fields": ",".join(result_fields),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ def search(query: str, params: "RequestParams") -> EngineResults:
|
|||||||
kvmap=kvmap,
|
kvmap=kvmap,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
res.add(res.types.LegacyResult(number_of_results=count))
|
|
||||||
|
|
||||||
# cache counter value for 20sec
|
# cache counter value for 20sec
|
||||||
CACHE.set("count", count, expire=20)
|
CACHE.set("count", count, expire=20)
|
||||||
|
|||||||
@@ -176,6 +176,4 @@ def response(resp):
|
|||||||
|
|
||||||
results.append(result)
|
results.append(result)
|
||||||
|
|
||||||
results.append({'number_of_results': len(json_data['topics'])})
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Dogpile is a metasearch engine by the American advertising company `System1`_.
|
||||||
|
|
||||||
|
.. _System1: https://system1.com/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import html
|
||||||
|
|
||||||
|
from searx.utils import format_duration, html_to_text, humanize_number
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://www.dogpile.com",
|
||||||
|
"wikidata_id": "Q3595363",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
paging = True
|
||||||
|
safesearch = True
|
||||||
|
|
||||||
|
categories = ["general"]
|
||||||
|
dogpile_categ = "search"
|
||||||
|
"""Category to search in. Can be either "search", "images", "videos" or "news"."""
|
||||||
|
|
||||||
|
|
||||||
|
base_url = "https://www.dogpile.com"
|
||||||
|
safe_search_map = {0: "none", 1: "moderate", 2: "heavy"}
|
||||||
|
|
||||||
|
|
||||||
|
def init(_):
|
||||||
|
if dogpile_categ not in ("search", "images", "videos", "news"):
|
||||||
|
raise ValueError("invalid search type: %s" % dogpile_categ)
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams"):
|
||||||
|
params["url"] = f"{base_url}/api/{dogpile_categ}"
|
||||||
|
params["method"] = "POST"
|
||||||
|
params["json"] = {"q": query, "qadf": safe_search_map[params["safesearch"]], "page": params["pageno"]}
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
json_resp = resp.json()
|
||||||
|
|
||||||
|
for result in json_resp["results"]:
|
||||||
|
if dogpile_categ == "search":
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["clickUrl"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif dogpile_categ == "news":
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["clickUrl"],
|
||||||
|
title=html_to_text(html.unescape(result["title"])),
|
||||||
|
content=html_to_text(html.unescape(result["description"])),
|
||||||
|
thumbnail=result["thumbnailUrl"],
|
||||||
|
publishedDate=datetime.fromtimestamp(result["date"], tz=timezone.utc),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif dogpile_categ == "videos":
|
||||||
|
res.add(
|
||||||
|
res.types.LegacyResult(
|
||||||
|
template="videos.html",
|
||||||
|
url=result["clickUrl"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
thumbnail=result["thumbnailUrl"],
|
||||||
|
publishedDate=datetime.fromisoformat(result["publishDate"]),
|
||||||
|
length=format_duration(result["duration"]),
|
||||||
|
views=humanize_number(result["viewCount"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif dogpile_categ == "images":
|
||||||
|
res.add(
|
||||||
|
res.types.Image(
|
||||||
|
url=result["altClickUrl"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
img_src=result["clickUrl"],
|
||||||
|
thumbnail_src=result["thumbnailUrl"],
|
||||||
|
resolution=f"{result['width']}x{result['height']}",
|
||||||
|
img_format=result["format"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -21,7 +21,6 @@ about = {
|
|||||||
# engine dependent config
|
# engine dependent config
|
||||||
categories = ['general'] # 'images', 'music', 'videos', 'files'
|
categories = ['general'] # 'images', 'music', 'videos', 'files'
|
||||||
paging = False
|
paging = False
|
||||||
number_of_results = 5
|
|
||||||
|
|
||||||
# search-url
|
# search-url
|
||||||
# Doku is OpenSearch compatible
|
# Doku is OpenSearch compatible
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ safesearch_cookies = {0: "-2", 1: None, 2: "1"}
|
|||||||
safesearch_args = {0: "1", 1: None, 2: "1"}
|
safesearch_args = {0: "1", 1: None, 2: "1"}
|
||||||
|
|
||||||
search_path_map = {"images": "i", "videos": "v", "news": "news"}
|
search_path_map = {"images": "i", "videos": "v", "news": "news"}
|
||||||
|
|
||||||
_HTTP_User_Agent: str = gen_useragent()
|
_HTTP_User_Agent: str = gen_useragent()
|
||||||
|
send_accept_language_header = False
|
||||||
|
|
||||||
|
|
||||||
def init(engine_settings: dict[str, t.Any]):
|
def init(engine_settings: dict[str, t.Any]):
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""DuckDuckGo Web (general)
|
||||||
|
|
||||||
|
This implementation fetches the link to the first API page
|
||||||
|
(i.e. ``links.duckduckgo.com/d.js?...``) from duckduckgo.com and uses the ``n``
|
||||||
|
parameter of the API to fetch all subsequent pages.
|
||||||
|
|
||||||
|
This also means that it's not possible to immediately search for the third
|
||||||
|
page - the first and the second page would need to be loaded first.
|
||||||
|
|
||||||
|
The reason why we can't just normally use the `vqd` value is that the API URLs
|
||||||
|
require an additional parameter `dp` which seems generated at server-side, so we
|
||||||
|
can't build it ourselves and must scrape it from the HTML pages.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
from searx.utils import html_to_text, gen_useragent, extract_text, eval_xpath
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
from searx.enginelib import EngineCache
|
||||||
|
from searx.network import get
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://duckduckgo.com/",
|
||||||
|
"wikidata_id": "Q12805",
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
# engine dependent config
|
||||||
|
categories = ["general"]
|
||||||
|
paging = True
|
||||||
|
_HTTP_User_Agent: str = gen_useragent()
|
||||||
|
|
||||||
|
base_url = "https://duckduckgo.com"
|
||||||
|
|
||||||
|
CACHE: EngineCache
|
||||||
|
"""Cache to store the API URLs for combinations of (query, page)."""
|
||||||
|
|
||||||
|
|
||||||
|
def setup(engine_settings: dict[str, str]):
|
||||||
|
global CACHE # pylint:disable=global-statement
|
||||||
|
CACHE = EngineCache(engine_settings["name"])
|
||||||
|
return CACHE
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_first_page_link(
|
||||||
|
query: str,
|
||||||
|
headers: dict[str, str],
|
||||||
|
):
|
||||||
|
"""Search for a::
|
||||||
|
|
||||||
|
<link id="deep_preload_link" rel="preload" as="script"
|
||||||
|
href="https://links.duckduckgo.com/d.js?q=rust&t=D&l=us-en&s=0&a=h_&ct=DE&vqd=VQD_VALUE&bing_market=en-US&p_ent=&ex=-1&dp=LONG_TOKEN
|
||||||
|
>
|
||||||
|
|
||||||
|
This points to the first page
|
||||||
|
""" # pylint:disable=line-too-long
|
||||||
|
|
||||||
|
cache_key = _cache_key(query, 1)
|
||||||
|
cached: str | None = CACHE.get(cache_key)
|
||||||
|
if cached:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
resp = get(
|
||||||
|
url=f"{base_url}/?q={quote_plus(query)}&t=h_&ia=web",
|
||||||
|
headers=headers,
|
||||||
|
timeout=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if resp.status_code != 200:
|
||||||
|
logger.error("vqd: got HTTP %s from duckduckgo.com", resp.status_code)
|
||||||
|
|
||||||
|
dom = html.fromstring(resp.text)
|
||||||
|
first_page_link = extract_text(eval_xpath(dom, "//link[@id='deep_preload_link']/@href"))
|
||||||
|
|
||||||
|
if not first_page_link:
|
||||||
|
logger.error("vqd: failed to load first page JS url from ddg response (return empty string)")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
logger.debug("got link to first page from duckduckgo.com request: '%s'", first_page_link)
|
||||||
|
CACHE.set(cache_key, first_page_link, expire=7200)
|
||||||
|
|
||||||
|
return first_page_link
|
||||||
|
|
||||||
|
|
||||||
|
def _cache_key(query: str, pageno: int) -> str:
|
||||||
|
return f"nextpage_url|{query}|{pageno}"
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
|
||||||
|
if len(query) >= 500:
|
||||||
|
# DDG does not accept queries with more than 499 chars
|
||||||
|
params["url"] = None
|
||||||
|
return
|
||||||
|
|
||||||
|
headers = params["headers"]
|
||||||
|
|
||||||
|
# The vqd value is generated from the query and the UA header. To be able
|
||||||
|
# to reuse the vqd value, the UA header must be static.
|
||||||
|
headers["User-Agent"] = _HTTP_User_Agent
|
||||||
|
headers["Accept"] = "*/*"
|
||||||
|
headers["Referer"] = f"{base_url}/"
|
||||||
|
headers["Host"] = "duckduckgo.com"
|
||||||
|
|
||||||
|
# Sec-Fetch headers are required to not get blocked when sending a Firefox user agent
|
||||||
|
headers["Sec-Fetch-Dest"] = "script"
|
||||||
|
headers["Sec-Fetch-Mode"] = "no-cors"
|
||||||
|
headers["Sec-Fetch-Site"] = "same-site"
|
||||||
|
|
||||||
|
api_url = ""
|
||||||
|
if params["pageno"] > 1:
|
||||||
|
api_url = CACHE.get(_cache_key(query, params["pageno"]))
|
||||||
|
else:
|
||||||
|
api_url = _fetch_first_page_link(query, headers)
|
||||||
|
|
||||||
|
if not api_url:
|
||||||
|
params["url"] = None
|
||||||
|
return
|
||||||
|
|
||||||
|
params["url"] = api_url.replace("/d.js?", "/d.js?o=json&")
|
||||||
|
|
||||||
|
# TODO: support safesearch, timerange and engine traits # pylint:disable=fixme
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
res_json = resp.json()
|
||||||
|
|
||||||
|
for result in res_json["results"]:
|
||||||
|
if "u" not in result:
|
||||||
|
continue
|
||||||
|
|
||||||
|
res.add(res.types.MainResult(url=result["u"], title=result["t"], content=html_to_text(result["a"])))
|
||||||
|
|
||||||
|
# link to next page
|
||||||
|
next_page_path = res_json["results"][-1].get("n")
|
||||||
|
if next_page_path:
|
||||||
|
CACHE.set(
|
||||||
|
_cache_key(resp.search_params["query"], resp.search_params["pageno"] + 1),
|
||||||
|
base_url + next_page_path,
|
||||||
|
expire=60 * 60,
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""Duden"""
|
"""Duden"""
|
||||||
|
|
||||||
import re
|
|
||||||
from urllib.parse import quote, urljoin
|
from urllib.parse import quote, urljoin
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
|
from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
|
||||||
@@ -51,13 +50,6 @@ def response(resp):
|
|||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
number_of_results_element = eval_xpath_getindex(
|
|
||||||
dom, '//a[@class="active" and contains(@href,"/suchen/dudenonline")]/span/text()', 0, default=None
|
|
||||||
)
|
|
||||||
if number_of_results_element is not None:
|
|
||||||
number_of_results_string = re.sub('[^0-9]', '', number_of_results_element)
|
|
||||||
results.append({'number_of_results': int(number_of_results_string)})
|
|
||||||
|
|
||||||
for result in eval_xpath_list(dom, '//section[not(contains(@class, "essay"))]'):
|
for result in eval_xpath_list(dom, '//section[not(contains(@class, "essay"))]'):
|
||||||
url = eval_xpath_getindex(result, './/h2/a', 0).get('href')
|
url = eval_xpath_getindex(result, './/h2/a', 0).get('href')
|
||||||
url = urljoin(base_url, url)
|
url = urljoin(base_url, url)
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Fireball_ is a Germany-based, privacy-focused search engine.
|
||||||
|
|
||||||
|
It likely doesn't have its own index, but it's unclear where its results come
|
||||||
|
from.
|
||||||
|
|
||||||
|
.. _Fireball: https://fireball.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from searx.enginelib import EngineCache
|
||||||
|
from searx.exceptions import SearxEngineAPIException
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
from searx.network import post
|
||||||
|
from searx.utils import html_to_text
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://fireball.com",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
base_url = "https://fireball.com"
|
||||||
|
categories = ["general"]
|
||||||
|
fireball_category = "web" # values: "web", "news", "videos"
|
||||||
|
|
||||||
|
paging = False
|
||||||
|
safesearch = True
|
||||||
|
|
||||||
|
safe_search_map = {0: "off", 1: "moderate", 2: "strict"}
|
||||||
|
|
||||||
|
CACHE: EngineCache
|
||||||
|
"""Cache to store the settings cookie (contains e.g. language, safesearch, ...)."""
|
||||||
|
|
||||||
|
CACHE_VALID_DURATION = 30 * 24 * 3600 # one month, same as website
|
||||||
|
"""Duration how long settings cookies are valid."""
|
||||||
|
|
||||||
|
|
||||||
|
def init(engine_settings: dict[str, t.Any]):
|
||||||
|
global CACHE # pylint: disable=global-statement
|
||||||
|
CACHE = EngineCache(engine_settings["name"])
|
||||||
|
|
||||||
|
if fireball_category not in ("web", "news", "videos"):
|
||||||
|
raise ValueError(f"Unsupported category: {fireball_category}")
|
||||||
|
|
||||||
|
|
||||||
|
def _cache_key(fireball_settings: dict[str, str]) -> str:
|
||||||
|
return f"fireball_settings_{fireball_settings['safesearch']}_{fireball_settings['market']}"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_search_settings_cookie(params: 'OnlineParams') -> str:
|
||||||
|
"""Get a 'fireball' cookie for the given locale and safesearch setting set
|
||||||
|
in params."""
|
||||||
|
|
||||||
|
# the language is set by only specifying the search country on their
|
||||||
|
# website, they only list DE and US, but in fact it supports much more
|
||||||
|
# countries
|
||||||
|
country = "US"
|
||||||
|
if params["searxng_locale"] != "all":
|
||||||
|
language_parts = params["searxng_locale"].split("-")
|
||||||
|
country = language_parts[-1].upper()
|
||||||
|
|
||||||
|
fireball_settings = {
|
||||||
|
"action": "save",
|
||||||
|
"language": "en", # language is irrelevant, only changes UI language
|
||||||
|
"market": country,
|
||||||
|
"adprovider": "automatic",
|
||||||
|
"target": "_blank",
|
||||||
|
"tiles": "on",
|
||||||
|
"safesearch": safe_search_map[params["safesearch"]],
|
||||||
|
}
|
||||||
|
cache_key = _cache_key(fireball_settings)
|
||||||
|
|
||||||
|
cached_cookie = CACHE.get(cache_key)
|
||||||
|
if cached_cookie:
|
||||||
|
return cached_cookie
|
||||||
|
|
||||||
|
resp = post("https://fireball.com/settings", data=fireball_settings)
|
||||||
|
if not resp.ok:
|
||||||
|
raise SearxEngineAPIException("failed to obtain cookie for settings")
|
||||||
|
|
||||||
|
cookie = resp.cookies.get("fireball")
|
||||||
|
if not cookie:
|
||||||
|
raise SearxEngineAPIException("failed to obtain cookie for settings")
|
||||||
|
|
||||||
|
CACHE.set(cache_key, cookie, expire=CACHE_VALID_DURATION)
|
||||||
|
return cookie
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams"):
|
||||||
|
# no matter the category, the request is always the same, i.e. we get all
|
||||||
|
# different categories with one HTTP request
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"f": "web",
|
||||||
|
"q": query,
|
||||||
|
}
|
||||||
|
|
||||||
|
params["url"] = f"{base_url}/getResults/?{urlencode(args)}"
|
||||||
|
params["cookies"]["fireball"] = _get_search_settings_cookie(params)
|
||||||
|
|
||||||
|
# referer header has to be set, otherwise the requests get blocked
|
||||||
|
params["headers"]["Referer"] = f"{base_url}/search?{urlencode(args)}"
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
json_data = resp.json()
|
||||||
|
|
||||||
|
for result in json_data.get(fireball_category, {}).get("results", []):
|
||||||
|
published_date = None
|
||||||
|
if result.get("page_age"):
|
||||||
|
published_date = datetime.fromisoformat(result["page_age"])
|
||||||
|
|
||||||
|
if fireball_category == "web":
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["url"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
publishedDate=published_date,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif fireball_category == "news":
|
||||||
|
thumbnail: str | None = None
|
||||||
|
if result.get("thumbnail"):
|
||||||
|
thumbnail = result["thumbnail"]["src"]
|
||||||
|
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["url"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
thumbnail=thumbnail or "",
|
||||||
|
publishedDate=published_date,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif fireball_category == "videos":
|
||||||
|
length = None
|
||||||
|
if result.get("video"):
|
||||||
|
length = result["video"].get("duration")
|
||||||
|
|
||||||
|
res.add(
|
||||||
|
res.types.LegacyResult(
|
||||||
|
{
|
||||||
|
"template": "videos.html",
|
||||||
|
"url": result["url"],
|
||||||
|
"title": html_to_text(result["title"]),
|
||||||
|
"content": html_to_text(result["description"]),
|
||||||
|
"thumbnail": result.get("thumbnail", {}).get("original"),
|
||||||
|
"length": length,
|
||||||
|
"publishedDate": published_date,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Flaticon_ is a database for icons.
|
||||||
|
|
||||||
|
.. _Flaticon: https://www.flaticon.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://www.flaticon.com",
|
||||||
|
"wikidata_id": "Q105283791",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
base_url = "https://www.flaticon.com"
|
||||||
|
|
||||||
|
categories = ["images", "icons"]
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
args = {
|
||||||
|
"word": query,
|
||||||
|
}
|
||||||
|
params["headers"].update(
|
||||||
|
{
|
||||||
|
# important: query term is not URL encoded in the referer string
|
||||||
|
"Referer": f"{base_url}/search?word={query}",
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
params["url"] = f"{base_url}/ajax/search/{params['pageno']}?{urlencode(args)}"
|
||||||
|
|
||||||
|
|
||||||
|
def _fix_url(url: str) -> str:
|
||||||
|
return url.replace(r"\/", "/")
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
result: dict[str, str] # TBH: dict[str, t.Any]
|
||||||
|
for result in resp.json()["items"]:
|
||||||
|
tags = [
|
||||||
|
tag_info["tag"] for tag_info in result["tags"] if tag_info["tag"] # pyright: ignore[reportArgumentType]
|
||||||
|
]
|
||||||
|
res.add(
|
||||||
|
res.types.Image(
|
||||||
|
title=result["name"],
|
||||||
|
content=", ".join(tags),
|
||||||
|
url=_fix_url(result["slug"]),
|
||||||
|
thumbnail_src=_fix_url(result["png"]),
|
||||||
|
img_src=_fix_url(result["png512"]),
|
||||||
|
author=result["team_name"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -20,7 +20,7 @@ about = {
|
|||||||
|
|
||||||
categories = ['images']
|
categories = ['images']
|
||||||
|
|
||||||
nb_per_page = 15
|
page_size = 15
|
||||||
paging = True
|
paging = True
|
||||||
api_key = None
|
api_key = None
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ url = (
|
|||||||
'https://api.flickr.com/services/rest/?method=flickr.photos.search'
|
'https://api.flickr.com/services/rest/?method=flickr.photos.search'
|
||||||
+ '&api_key={api_key}&{text}&sort=relevance'
|
+ '&api_key={api_key}&{text}&sort=relevance'
|
||||||
+ '&extras=description%2C+owner_name%2C+url_o%2C+url_n%2C+url_z'
|
+ '&extras=description%2C+owner_name%2C+url_o%2C+url_n%2C+url_z'
|
||||||
+ '&per_page={nb_per_page}&format=json&nojsoncallback=1&page={page}'
|
+ '&per_page={page_size}&format=json&nojsoncallback=1&page={page}'
|
||||||
)
|
)
|
||||||
photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
|
photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ def build_flickr_url(user_id, photo_id):
|
|||||||
|
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
params['url'] = url.format(
|
params['url'] = url.format(
|
||||||
text=urlencode({'text': query}), api_key=api_key, nb_per_page=nb_per_page, page=params['pageno']
|
text=urlencode({'text': query}), api_key=api_key, page_size=page_size, page=params['pageno']
|
||||||
)
|
)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|||||||
+31
-24
@@ -1,15 +1,23 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""Fyyd (podcasts)"""
|
"""Fyyd (podcasts)"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
about = {
|
about = {
|
||||||
'website': 'https://fyyd.de',
|
"website": "https://fyyd.de",
|
||||||
'official_api_documentation': 'https://github.com/eazyliving/fyyd-api',
|
"official_api_documentation": "https://github.com/eazyliving/fyyd-api",
|
||||||
'use_official_api': True,
|
"use_official_api": True,
|
||||||
'require_api_key': False,
|
"require_api_key": False,
|
||||||
'results': 'JSON',
|
"results": "JSON",
|
||||||
}
|
}
|
||||||
categories = []
|
categories = []
|
||||||
paging = True
|
paging = True
|
||||||
@@ -18,31 +26,30 @@ base_url = "https://api.fyyd.de"
|
|||||||
page_size = 10
|
page_size = 10
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
args = {
|
args = {
|
||||||
'term': query,
|
"term": query,
|
||||||
'count': page_size,
|
"count": page_size,
|
||||||
'page': params['pageno'] - 1,
|
"page": params["pageno"] - 1,
|
||||||
}
|
}
|
||||||
params['url'] = f"{base_url}/0.2/search/podcast?{urlencode(args)}"
|
params["url"] = f"{base_url}/0.2/search/podcast?{urlencode(args)}"
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp: "SXNG_Response"):
|
||||||
results = []
|
res = EngineResults()
|
||||||
|
|
||||||
json_results = resp.json()['data']
|
json_results: list[dict[str, str]] = resp.json()["data"] # pyright: ignore[reportAny]
|
||||||
|
|
||||||
for result in json_results:
|
for result in json_results:
|
||||||
results.append(
|
res.add(
|
||||||
{
|
res.types.MainResult(
|
||||||
'url': result['htmlURL'],
|
url=result["htmlURL"],
|
||||||
'title': result['title'],
|
title=result["title"],
|
||||||
'content': result['description'],
|
content=result["description"],
|
||||||
'thumbnail': result['smallImageURL'],
|
thumbnail=result["smallImageURL"],
|
||||||
'publishedDate': datetime.strptime(result['status_since'], '%Y-%m-%d %H:%M:%S'),
|
publishedDate=datetime.strptime(result["status_since"], "%Y-%m-%d %H:%M:%S"),
|
||||||
'metadata': f"Rank: {result['rank']} || {result['episode_count']} episodes",
|
metadata=f"Rank: {result['rank']} || {result['episode_count']} episodes",
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return results
|
return res
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import time
|
|||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
from searx.result_types import EngineResults
|
from searx.result_types import EngineResults
|
||||||
|
from searx.exceptions import SearxEngineCaptchaException
|
||||||
from searx.extended_types import SXNG_Response
|
from searx.extended_types import SXNG_Response
|
||||||
from searx.utils import extr, gen_useragent, html_to_text
|
from searx.utils import extr, gen_useragent, html_to_text, eval_xpath
|
||||||
from searx.network import get
|
from searx.network import get
|
||||||
|
|
||||||
if t.TYPE_CHECKING:
|
if t.TYPE_CHECKING:
|
||||||
@@ -40,6 +42,11 @@ time_range_map = {"day": "d", "week": "w", "month": "m", "year": "y"}
|
|||||||
def _get_page_hash(query: str, page: int, headers: dict[str, str]) -> str:
|
def _get_page_hash(query: str, page: int, headers: dict[str, str]) -> str:
|
||||||
resp = get(f"{base_url}/web/result?q={query}&page={page}", headers=headers)
|
resp = get(f"{base_url}/web/result?q={query}&page={page}", headers=headers)
|
||||||
|
|
||||||
|
# detect captcha (if any)
|
||||||
|
doc = html.fromstring(resp.text)
|
||||||
|
if eval_xpath(doc, "//*[@id='spam-messages']"):
|
||||||
|
raise SearxEngineCaptchaException()
|
||||||
|
|
||||||
# the text we search for looks like:
|
# the text we search for looks like:
|
||||||
# load("/desk?lang="+eV.p.param['hl']+"&q="+eV['p']['q_encode']+"&page=5&h=aa45603&t=177582576&origin=web&comp=web_serp_pag&p=gmx-com&sp=&lr="+eV.p.param['lr0']+"&mkt="+eV.p.param['mkt0']+"&family="+eV.p.param['familyFilter']+"&fcons="+eV.p.perm.fCons,"google", "eMMO", "eMH","eMP"); # pylint: disable=line-too-long
|
# load("/desk?lang="+eV.p.param['hl']+"&q="+eV['p']['q_encode']+"&page=5&h=aa45603&t=177582576&origin=web&comp=web_serp_pag&p=gmx-com&sp=&lr="+eV.p.param['lr0']+"&mkt="+eV.p.param['mkt0']+"&family="+eV.p.param['familyFilter']+"&fcons="+eV.p.perm.fCons,"google", "eMMO", "eMH","eMP"); # pylint: disable=line-too-long
|
||||||
return extr(resp.text, "&h=", "&t=")
|
return extr(resp.text, "&h=", "&t=")
|
||||||
|
|||||||
+19
-1
@@ -278,10 +278,28 @@ def get_google_info(params: "OnlineParams", eng_traits: EngineTraits) -> dict[st
|
|||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
def detect_google_sorry(resp):
|
def detect_google_sorry(resp: "SXNG_Response"):
|
||||||
|
"""Detect Google's bot-protection responses (CAPTCHA / sorry pages).
|
||||||
|
|
||||||
|
Google may block requests in several ways:
|
||||||
|
|
||||||
|
1. Redirect to sorry.google.com (standard CAPTCHA).
|
||||||
|
2. HTTP 302 redirect to ``/sorry/index?...`` on the same host -- when the
|
||||||
|
HTTP client doesn't follow the redirect, the response body is a short
|
||||||
|
HTML stub with a link to the sorry page.
|
||||||
|
3. Short HTML response (<2000 bytes) containing "/sorry/" -- a meta-refresh
|
||||||
|
or JS redirect variant.
|
||||||
|
"""
|
||||||
|
|
||||||
if resp.url.host == "sorry.google.com" or resp.url.path.startswith("/sorry"):
|
if resp.url.host == "sorry.google.com" or resp.url.path.startswith("/sorry"):
|
||||||
raise SearxEngineCaptchaException()
|
raise SearxEngineCaptchaException()
|
||||||
|
|
||||||
|
if resp.status_code == 302:
|
||||||
|
raise SearxEngineCaptchaException()
|
||||||
|
|
||||||
|
if len(resp.text) < 2000 and "/sorry/" in resp.text:
|
||||||
|
raise SearxEngineCaptchaException()
|
||||||
|
|
||||||
|
|
||||||
def request(query: str, params: "OnlineParams") -> None:
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
"""Google search request"""
|
"""Google search request"""
|
||||||
|
|||||||
+188
-159
@@ -23,9 +23,11 @@ The google news API ignores some parameters from the common :ref:`google API`:
|
|||||||
.. _num: https://developers.google.com/custom-search/docs/xml_results#numsp
|
.. _num: https://developers.google.com/custom-search/docs/xml_results#numsp
|
||||||
.. _save: https://developers.google.com/custom-search/docs/xml_results#safesp
|
.. _save: https://developers.google.com/custom-search/docs/xml_results#safesp
|
||||||
"""
|
"""
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from urllib.parse import urlencode
|
import json
|
||||||
import base64
|
import base64
|
||||||
|
from urllib.parse import urlencode
|
||||||
from lxml import html
|
from lxml import html
|
||||||
import babel
|
import babel
|
||||||
|
|
||||||
@@ -44,18 +46,24 @@ from searx.engines.google import (
|
|||||||
)
|
)
|
||||||
from searx.enginelib.traits import EngineTraits
|
from searx.enginelib.traits import EngineTraits
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
# about
|
# about
|
||||||
about = {
|
about = {
|
||||||
"website": 'https://news.google.com',
|
"website": "https://news.google.com",
|
||||||
"wikidata_id": 'Q12020',
|
"wikidata_id": "Q12020",
|
||||||
"official_api_documentation": 'https://developers.google.com/custom-search',
|
"official_api_documentation": "https://developers.google.com/custom-search",
|
||||||
"use_official_api": False,
|
"use_official_api": False,
|
||||||
"require_api_key": False,
|
"require_api_key": False,
|
||||||
"results": 'HTML',
|
"results": "HTML",
|
||||||
}
|
}
|
||||||
|
|
||||||
# engine dependent config
|
# engine dependent config
|
||||||
categories = ['news']
|
categories = ["news"]
|
||||||
paging = False
|
paging = False
|
||||||
time_range_support = False
|
time_range_support = False
|
||||||
|
|
||||||
@@ -64,231 +72,252 @@ time_range_support = False
|
|||||||
#
|
#
|
||||||
# safesearch : results are identical for safesearch=0 and safesearch=2
|
# safesearch : results are identical for safesearch=0 and safesearch=2
|
||||||
safesearch = True
|
safesearch = True
|
||||||
|
base_url: str = "https://news.google.com"
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
"""Google-News search request"""
|
"""Google-News search request"""
|
||||||
|
|
||||||
sxng_locale = params.get('searxng_locale', 'en-US')
|
sxng_locale = params.get("searxng_locale", "en-US")
|
||||||
ceid = locales.get_engine_locale(sxng_locale, traits.custom['ceid'], default='US:en')
|
ceid: str = locales.get_engine_locale(
|
||||||
|
sxng_locale, traits.custom["ceid"], default="US:en"
|
||||||
|
) # pyright: ignore[reportAssignmentType]
|
||||||
google_info = get_google_info(params, traits)
|
google_info = get_google_info(params, traits)
|
||||||
google_info['subdomain'] = 'news.google.com' # google news has only one domain
|
google_info["subdomain"] = "news.google.com" # google news has only one domain
|
||||||
|
|
||||||
ceid_region, ceid_lang = ceid.split(':')
|
ceid_region, ceid_lang = ceid.split(":")
|
||||||
ceid_lang, ceid_suffix = (
|
ceid_lang, ceid_suffix = (
|
||||||
ceid_lang.split('-')
|
ceid_lang.split(":")
|
||||||
+ [
|
+ [
|
||||||
None,
|
"",
|
||||||
]
|
]
|
||||||
)[:2]
|
)[:2]
|
||||||
|
|
||||||
google_info['params']['hl'] = ceid_lang
|
google_info["params"]["hl"] = ceid_lang
|
||||||
|
|
||||||
if ceid_suffix and ceid_suffix not in ['Hans', 'Hant']:
|
if ceid_suffix and ceid_suffix not in ["Hans", "Hant"]:
|
||||||
|
|
||||||
if ceid_region.lower() == ceid_lang:
|
if ceid_region.lower() == ceid_lang:
|
||||||
google_info['params']['hl'] = ceid_lang + '-' + ceid_region
|
google_info["params"]["hl"] = ceid_lang + "-" + ceid_region
|
||||||
else:
|
else:
|
||||||
google_info['params']['hl'] = ceid_lang + '-' + ceid_suffix
|
google_info["params"]["hl"] = ceid_lang + "-" + ceid_suffix
|
||||||
|
|
||||||
elif ceid_region.lower() != ceid_lang:
|
elif ceid_region.lower() != ceid_lang:
|
||||||
|
|
||||||
if ceid_region in ['AT', 'BE', 'CH', 'IL', 'SA', 'IN', 'BD', 'PT']:
|
if ceid_region in ["AT", "BE", "CH", "IL", "SA", "IN", "BD", "PT"]:
|
||||||
google_info['params']['hl'] = ceid_lang
|
google_info["params"]["hl"] = ceid_lang
|
||||||
else:
|
else:
|
||||||
google_info['params']['hl'] = ceid_lang + '-' + ceid_region
|
google_info["params"]["hl"] = ceid_lang + "-" + ceid_region
|
||||||
|
|
||||||
google_info['params']['lr'] = 'lang_' + ceid_lang.split('-')[0]
|
google_info["params"]["lr"] = "lang_" + ceid_lang.split("-")[0]
|
||||||
google_info['params']['gl'] = ceid_region
|
google_info["params"]["gl"] = ceid_region
|
||||||
|
|
||||||
query_url = (
|
query_url = (
|
||||||
'https://'
|
"https://"
|
||||||
+ google_info['subdomain']
|
+ google_info["subdomain"]
|
||||||
+ "/search?"
|
+ "/search?"
|
||||||
+ urlencode(
|
+ urlencode(
|
||||||
{
|
{"q": query, **google_info["params"]},
|
||||||
'q': query,
|
|
||||||
**google_info['params'],
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
# ceid includes a ':' character which must not be urlencoded
|
# ceid includes a ':' character which must not be urlencoded
|
||||||
+ ('&ceid=%s' % ceid)
|
+ ("&ceid=%s" % ceid)
|
||||||
)
|
)
|
||||||
|
|
||||||
params['url'] = query_url
|
params["url"] = query_url
|
||||||
params['cookies'] = google_info['cookies']
|
params["cookies"] = google_info["cookies"]
|
||||||
params['headers'].update(google_info['headers'])
|
params["headers"].update(google_info["headers"])
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
"""Get response from google's search request"""
|
"""Get response from google's search request"""
|
||||||
results = []
|
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
detect_google_sorry(resp)
|
detect_google_sorry(resp)
|
||||||
|
|
||||||
# convert the text to dom
|
# convert the text to dom
|
||||||
dom = html.fromstring(resp.text)
|
dom = html.fromstring(resp.text)
|
||||||
|
|
||||||
for result in eval_xpath_list(dom, '//div[@class="xrnccd"]'):
|
for result in eval_xpath_list(dom, "//div[@jslog and @data-n-tid and @jsdata]"):
|
||||||
|
|
||||||
# The first <a> tag in the <article> contains the link to the article
|
url: str = eval_xpath_getindex(result, "./a[@target='_blank']/@href", 0, default=0)
|
||||||
# The href attribute of the <a> tag is a google internal link, we have
|
if not url:
|
||||||
# to decode
|
continue
|
||||||
|
if url.startswith("./"):
|
||||||
|
url = base_url + url[1:]
|
||||||
|
|
||||||
href = eval_xpath_getindex(result, './article/a/@href', 0)
|
# The real URL is often encoded in the "jslog" attribute
|
||||||
href = href.split('?')[0]
|
jslog: str | None = eval_xpath_getindex(result, "./a[@target='_blank']/@jslog", 0, default=None)
|
||||||
href = href.split('/')[-1]
|
|
||||||
href = base64.urlsafe_b64decode(href + '====')
|
|
||||||
href = href[href.index(b'http') :].split(b'\xd2')[0]
|
|
||||||
href = href.decode()
|
|
||||||
|
|
||||||
title = extract_text(eval_xpath(result, './article/h3[1]'))
|
# Try to extract the real URL from jslog
|
||||||
|
real_url: str | None = None
|
||||||
|
if jslog:
|
||||||
|
# jslog format is usually: "95014; 5:<base64>; track:click,vis". We
|
||||||
|
# want the second part (index 1) after splitting by ";"
|
||||||
|
parts: list[str] = jslog.split(";")
|
||||||
|
if len(parts) > 1:
|
||||||
|
b64_data: str = parts[1].split(":")[-1].strip()
|
||||||
|
# Pad base64 if necessary
|
||||||
|
b64_data += "=" * (-len(b64_data) % 4)
|
||||||
|
decoded_data: list[str | None] = json.loads(base64.b64decode(b64_data).decode("utf-8"))
|
||||||
|
# The URL is typically the last element in the decoded array
|
||||||
|
if (
|
||||||
|
isinstance(decoded_data, list)
|
||||||
|
and isinstance(decoded_data[-1], str)
|
||||||
|
and decoded_data[-1].startswith("http")
|
||||||
|
):
|
||||||
|
real_url = decoded_data[-1]
|
||||||
|
if real_url:
|
||||||
|
url = real_url
|
||||||
|
else:
|
||||||
|
logger.error(f"no real-url found: {url}")
|
||||||
|
continue
|
||||||
|
|
||||||
# The pub_date is mostly a string like 'yesterday', not a real
|
title = extract_text(eval_xpath(result, "./h4")) or ""
|
||||||
# timezone date or time. Therefore we can't use publishedDate.
|
|
||||||
pub_date = extract_text(eval_xpath(result, './article//time'))
|
|
||||||
pub_origin = extract_text(eval_xpath(result, './article//a[@data-n-tid]'))
|
|
||||||
|
|
||||||
content = ' / '.join([x for x in [pub_origin, pub_date] if x])
|
# The pub_date is mostly a string like 'yesterday', not a real timezone
|
||||||
|
# date or time. Therefore we can't use publishedDate and place the
|
||||||
|
# *pub* sting into the content.
|
||||||
|
|
||||||
# The image URL is located in a preceding sibling <img> tag, e.g.:
|
pub_date = extract_text(eval_xpath(result, ".//time"))
|
||||||
# "https://lh3.googleusercontent.com/DjhQh7DMszk.....z=-p-h100-w100"
|
pub_origin = extract_text(eval_xpath(result, ".//div[contains(@class, 'vr1PYe')]"))
|
||||||
# These URL are long but not personalized (double checked via tor).
|
content = " / ".join([x for x in [pub_origin, pub_date] if x])
|
||||||
|
|
||||||
thumbnail = extract_text(result.xpath('preceding-sibling::a/figure/img/@src'))
|
thumbnail: str = eval_xpath_getindex(result, ".//figure/img/@src", 0, default="")
|
||||||
|
if thumbnail and thumbnail.startswith("/"):
|
||||||
|
thumbnail = base_url + thumbnail
|
||||||
|
|
||||||
results.append(
|
res.add(
|
||||||
{
|
res.types.MainResult(
|
||||||
'url': href,
|
url=url,
|
||||||
'title': title,
|
title=title,
|
||||||
'content': content,
|
content=content,
|
||||||
'thumbnail': thumbnail,
|
thumbnail=thumbnail,
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# return results
|
return res
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
ceid_list = [
|
ceid_list = [
|
||||||
'AE:ar',
|
"AE:ar",
|
||||||
'AR:es-419',
|
"AR:es-419",
|
||||||
'AT:de',
|
"AT:de",
|
||||||
'AU:en',
|
"AU:en",
|
||||||
'BD:bn',
|
"BD:bn",
|
||||||
'BE:fr',
|
"BE:fr",
|
||||||
'BE:nl',
|
"BE:nl",
|
||||||
'BG:bg',
|
"BG:bg",
|
||||||
'BR:pt-419',
|
"BR:pt-419",
|
||||||
'BW:en',
|
"BW:en",
|
||||||
'CA:en',
|
"CA:en",
|
||||||
'CA:fr',
|
"CA:fr",
|
||||||
'CH:de',
|
"CH:de",
|
||||||
'CH:fr',
|
"CH:fr",
|
||||||
'CL:es-419',
|
"CL:es-419",
|
||||||
'CN:zh-Hans',
|
"CN:zh-Hans",
|
||||||
'CO:es-419',
|
"CO:es-419",
|
||||||
'CU:es-419',
|
"CU:es-419",
|
||||||
'CZ:cs',
|
"CZ:cs",
|
||||||
'DE:de',
|
"DE:de",
|
||||||
'EG:ar',
|
"EE:et",
|
||||||
'ES:es',
|
"EG:ar",
|
||||||
'ET:en',
|
"ES:ca",
|
||||||
'FR:fr',
|
"ES:es",
|
||||||
'GB:en',
|
"ET:en",
|
||||||
'GH:en',
|
"FI:fi",
|
||||||
'GR:el',
|
"FR:fr",
|
||||||
'HK:zh-Hant',
|
"GB:en",
|
||||||
'HU:hu',
|
"GH:en",
|
||||||
'ID:en',
|
"GR:el",
|
||||||
'ID:id',
|
"HK:zh-Hant",
|
||||||
'IE:en',
|
"HU:hu",
|
||||||
'IL:en',
|
"ID:en",
|
||||||
'IL:he',
|
"ID:id",
|
||||||
'IN:bn',
|
"IE:en",
|
||||||
'IN:en',
|
"IL:en",
|
||||||
'IN:hi',
|
"IL:he",
|
||||||
'IN:ml',
|
"IN:bn",
|
||||||
'IN:mr',
|
"IN:en",
|
||||||
'IN:ta',
|
"IN:gu",
|
||||||
'IN:te',
|
"IN:hi",
|
||||||
'IT:it',
|
"IN:ml",
|
||||||
'JP:ja',
|
"IN:mr",
|
||||||
'KE:en',
|
"IN:pa",
|
||||||
'KR:ko',
|
"IN:ta",
|
||||||
'LB:ar',
|
"IN:te",
|
||||||
'LT:lt',
|
"IT:it",
|
||||||
'LV:en',
|
"JP:ja",
|
||||||
'LV:lv',
|
"KE:en",
|
||||||
'MA:fr',
|
"KR:ko",
|
||||||
'MX:es-419',
|
"LB:ar",
|
||||||
'MY:en',
|
"LT:lt",
|
||||||
'NA:en',
|
"LV:en",
|
||||||
'NG:en',
|
"LV:lv",
|
||||||
'NL:nl',
|
"MA:fr",
|
||||||
'NO:no',
|
"MY:en",
|
||||||
'NZ:en',
|
"MY:ms",
|
||||||
'PE:es-419',
|
"NA:en",
|
||||||
'PH:en',
|
"NG:en",
|
||||||
'PK:en',
|
"NL:nl",
|
||||||
'PL:pl',
|
"NO:no",
|
||||||
'PT:pt-150',
|
"NZ:en",
|
||||||
'RO:ro',
|
"PH:en",
|
||||||
'RS:sr',
|
"PK:en",
|
||||||
'RU:ru',
|
"PL:pl",
|
||||||
'SA:ar',
|
"RO:ro",
|
||||||
'SE:sv',
|
"RS:sr",
|
||||||
'SG:en',
|
"RU:ru",
|
||||||
'SI:sl',
|
"SA:ar",
|
||||||
'SK:sk',
|
"SE:sv",
|
||||||
'SN:fr',
|
"SG:en",
|
||||||
'TH:th',
|
"SI:sl",
|
||||||
'TR:tr',
|
"SK:sk",
|
||||||
'TW:zh-Hant',
|
"SN:fr",
|
||||||
'TZ:en',
|
"TH:th",
|
||||||
'UA:ru',
|
"TR:tr",
|
||||||
'UA:uk',
|
"TZ:en",
|
||||||
'UG:en',
|
"UA:ru",
|
||||||
'US:en',
|
"UA:uk",
|
||||||
'US:es-419',
|
"UG:en",
|
||||||
'VE:es-419',
|
"US:en",
|
||||||
'VN:vi',
|
"VN:vi",
|
||||||
'ZA:en',
|
"ZA:en",
|
||||||
'ZW:en',
|
"ZW:en",
|
||||||
]
|
]
|
||||||
"""List of region/language combinations supported by Google News. Values of the
|
"""List of region/language combinations supported by Google News. Values of the
|
||||||
``ceid`` argument of the Google News REST API."""
|
``ceid`` argument of the Google News REST API."""
|
||||||
|
|
||||||
|
|
||||||
_skip_values = [
|
_skip_values = [
|
||||||
'ET:en', # english (ethiopia)
|
"ET:en", # english (ethiopia)
|
||||||
'ID:en', # english (indonesia)
|
"ID:en", # english (indonesia)
|
||||||
'LV:en', # english (latvia)
|
"LV:en", # english (latvia)
|
||||||
]
|
]
|
||||||
|
|
||||||
_ceid_locale_map = {'NO:no': 'nb-NO'}
|
_ceid_locale_map = {"NO:no": "nb-NO"}
|
||||||
|
|
||||||
|
|
||||||
def fetch_traits(engine_traits: EngineTraits):
|
def fetch_traits(engine_traits: EngineTraits):
|
||||||
_fetch_traits(engine_traits, add_domains=False)
|
_fetch_traits(engine_traits, add_domains=False)
|
||||||
|
|
||||||
engine_traits.custom['ceid'] = {}
|
engine_traits.custom["ceid"] = {}
|
||||||
|
|
||||||
for ceid in ceid_list:
|
for ceid in ceid_list:
|
||||||
if ceid in _skip_values:
|
if ceid in _skip_values:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
region, lang = ceid.split(':')
|
region, lang = ceid.split(":")
|
||||||
x = lang.split('-')
|
x = lang.split("-")
|
||||||
if len(x) > 1:
|
if len(x) > 1:
|
||||||
if x[1] not in ['Hant', 'Hans']:
|
if x[1] not in ["Hant", "Hans"]:
|
||||||
lang = x[0]
|
lang = x[0]
|
||||||
|
|
||||||
sxng_locale = _ceid_locale_map.get(ceid, lang + '-' + region)
|
sxng_locale = _ceid_locale_map.get(ceid, lang + "-" + region)
|
||||||
try:
|
try:
|
||||||
locale = babel.Locale.parse(sxng_locale, sep='-')
|
locale = babel.Locale.parse(sxng_locale, sep="-")
|
||||||
except babel.UnknownLocaleError:
|
except babel.UnknownLocaleError:
|
||||||
print("ERROR: %s -> %s is unknown by babel" % (ceid, sxng_locale))
|
print("ERROR: %s -> %s is unknown by babel" % (ceid, sxng_locale))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
engine_traits.custom['ceid'][locales.region_tag(locale)] = ceid
|
engine_traits.custom["ceid"][locales.region_tag(locale)] = ceid
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Heexy_ is a minimalist search engine that focuses on privacy.
|
||||||
|
|
||||||
|
Although it also supports news and videos, these are not implemented here
|
||||||
|
because they usually return no result to very few irrelevant ones.
|
||||||
|
|
||||||
|
It seems to use Bing internally, as the image thumbnails are loaded from Bing.
|
||||||
|
|
||||||
|
.. _Heexy: https://docs.heexy.org/introduction
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from searx.exceptions import SearxEngineAccessDeniedException
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://heexy.org",
|
||||||
|
"wikidata_id": None,
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
paging = True
|
||||||
|
safesearch = True
|
||||||
|
|
||||||
|
categories = ["general"]
|
||||||
|
heexy_categ = "web"
|
||||||
|
"""Category to search in. Can be either "web" or "image"."""
|
||||||
|
|
||||||
|
|
||||||
|
base_url = "https://seapi.heexy.org"
|
||||||
|
safe_search_map = {0: "off", 1: "on", 2: "on"}
|
||||||
|
|
||||||
|
|
||||||
|
def init(_):
|
||||||
|
if heexy_categ not in ("web", "image"):
|
||||||
|
raise ValueError("invalid search category: %s" % heexy_categ)
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
args = {
|
||||||
|
"q": query,
|
||||||
|
"page": params["pageno"],
|
||||||
|
"safe": safe_search_map[params["safesearch"]],
|
||||||
|
}
|
||||||
|
if params["searxng_locale"] != "all":
|
||||||
|
args["lang"] = params["searxng_locale"].split("-")[0]
|
||||||
|
|
||||||
|
params["url"] = f"{base_url}/search/{heexy_categ}?{urlencode(args)}"
|
||||||
|
params["headers"]["Origin"] = base_url
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response"):
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
json_resp = resp.json()
|
||||||
|
if not json_resp["success"]:
|
||||||
|
raise SearxEngineAccessDeniedException()
|
||||||
|
|
||||||
|
result: dict[str, str]
|
||||||
|
for result in json_resp["results"]:
|
||||||
|
if heexy_categ == "web":
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["url"],
|
||||||
|
title=result["title"],
|
||||||
|
content=result["description"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif heexy_categ == "image":
|
||||||
|
res.add(
|
||||||
|
res.types.Image(
|
||||||
|
title=result["description"],
|
||||||
|
url=result["url"],
|
||||||
|
thumbnail_src=result["image"],
|
||||||
|
img_src=result["rawImage"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -20,6 +20,7 @@ Paging:
|
|||||||
- :py:obj:`paging`
|
- :py:obj:`paging`
|
||||||
- :py:obj:`page_size`
|
- :py:obj:`page_size`
|
||||||
- :py:obj:`first_page_num`
|
- :py:obj:`first_page_num`
|
||||||
|
- :py:obj:`send_page_num_on_first_page`
|
||||||
|
|
||||||
Time Range:
|
Time Range:
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ Time Range:
|
|||||||
|
|
||||||
Safe-Search:
|
Safe-Search:
|
||||||
|
|
||||||
- :py:obj:`safe_search_support`
|
- :py:obj:`safesearch`
|
||||||
- :py:obj:`safe_search_map`
|
- :py:obj:`safe_search_map`
|
||||||
|
|
||||||
Response:
|
Response:
|
||||||
@@ -103,7 +104,7 @@ Replacements are:
|
|||||||
|
|
||||||
``{safe_search}``:
|
``{safe_search}``:
|
||||||
Safe-search :py:obj:`URL parameter <safe_search_map>` if engine
|
Safe-search :py:obj:`URL parameter <safe_search_map>` if engine
|
||||||
:py:obj:`supports safe-search <safe_search_support>`. The ``{safe_search}``
|
:py:obj:`supports safe-search <safesearch>`. The ``{safe_search}``
|
||||||
replacement is taken from the :py:obj:`safes_search_map`. Filter results::
|
replacement is taken from the :py:obj:`safes_search_map`. Filter results::
|
||||||
|
|
||||||
0: none, 1: moderate, 2:strict
|
0: none, 1: moderate, 2:strict
|
||||||
@@ -169,6 +170,10 @@ number, but an offset.'''
|
|||||||
first_page_num = 1
|
first_page_num = 1
|
||||||
'''Number of the first page (usually 0 or 1).'''
|
'''Number of the first page (usually 0 or 1).'''
|
||||||
|
|
||||||
|
send_page_num_on_first_page = True
|
||||||
|
'''Whether to include the page number in the request for the first page.
|
||||||
|
This can help if an engine blocks request that send a page number for the first page.'''
|
||||||
|
|
||||||
results_query = ''
|
results_query = ''
|
||||||
'''JSON query for the list of result items.
|
'''JSON query for the list of result items.
|
||||||
|
|
||||||
@@ -236,7 +241,7 @@ time_range_map = {
|
|||||||
year: 365
|
year: 365
|
||||||
'''
|
'''
|
||||||
|
|
||||||
safe_search_support = False
|
safesearch = False
|
||||||
'''Engine supports safe-search.'''
|
'''Engine supports safe-search.'''
|
||||||
|
|
||||||
safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'}
|
safe_search_map = {0: '&filter=none', 1: '&filter=moderate', 2: '&filter=strict'}
|
||||||
@@ -286,7 +291,6 @@ def do_query(data, q): # pylint: disable=invalid-name
|
|||||||
qkey = q[0]
|
qkey = q[0]
|
||||||
|
|
||||||
for key, value in iterate(data):
|
for key, value in iterate(data):
|
||||||
|
|
||||||
if len(q) == 1:
|
if len(q) == 1:
|
||||||
if key == qkey:
|
if key == qkey:
|
||||||
ret.append(value)
|
ret.append(value)
|
||||||
@@ -323,10 +327,13 @@ def request(query, params): # pylint: disable=redefined-outer-name
|
|||||||
if params['safesearch']:
|
if params['safesearch']:
|
||||||
safe_search = safe_search_map[params['safesearch']]
|
safe_search = safe_search_map[params['safesearch']]
|
||||||
|
|
||||||
|
pageno = ""
|
||||||
|
if send_page_num_on_first_page or params["pageno"] != 1:
|
||||||
|
pageno = (params['pageno'] - 1) * page_size + first_page_num
|
||||||
fp = { # pylint: disable=invalid-name
|
fp = { # pylint: disable=invalid-name
|
||||||
'query': urlencode({'q': query})[2:],
|
'query': urlencode({'q': query})[2:],
|
||||||
'lang': lang,
|
'lang': lang,
|
||||||
'pageno': (params['pageno'] - 1) * page_size + first_page_num,
|
'pageno': pageno,
|
||||||
'time_range': time_range,
|
'time_range': time_range,
|
||||||
'safe_search': safe_search,
|
'safe_search': safe_search,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,205 +0,0 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
"""Karmasearch uses Brave's index, so the results should be the same as Brave's.
|
|
||||||
|
|
||||||
However, the advantages of this engine are:
|
|
||||||
|
|
||||||
- it has less strict rate-limits
|
|
||||||
- it has a JSON API, so it's less likely to break
|
|
||||||
"""
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
import typing as t
|
|
||||||
|
|
||||||
from dateutil import parser
|
|
||||||
|
|
||||||
from searx.enginelib.traits import EngineTraits
|
|
||||||
|
|
||||||
from searx.utils import html_to_text
|
|
||||||
from searx.result_types import EngineResults, MainResult
|
|
||||||
from searx.result_types._base import LegacyResult
|
|
||||||
|
|
||||||
|
|
||||||
if t.TYPE_CHECKING:
|
|
||||||
from searx.extended_types import SXNG_Response
|
|
||||||
from searx.search.processors import OnlineParams
|
|
||||||
|
|
||||||
about = {
|
|
||||||
"website": "https://karmasearch.org",
|
|
||||||
"official_api_documentation": None,
|
|
||||||
"use_official_api": False,
|
|
||||||
"require_api_key": False,
|
|
||||||
"results": "JSON",
|
|
||||||
}
|
|
||||||
|
|
||||||
base_url = "https://api.karmasearch.org"
|
|
||||||
categories = ["web", "general"]
|
|
||||||
search_type = "web" # supported: web, images, videos, news
|
|
||||||
|
|
||||||
# all types except "images" support pagination
|
|
||||||
paging = True
|
|
||||||
safesearch = True
|
|
||||||
time_range_support = True
|
|
||||||
|
|
||||||
safe_search_map = {0: "off", 1: "moderate", 2: "strict"}
|
|
||||||
time_range_map = {"day": "Day", "week": "Week", "month": "Month", "year": "Year"}
|
|
||||||
|
|
||||||
|
|
||||||
def init(_):
|
|
||||||
if search_type not in ("web", "images", "videos", "news"):
|
|
||||||
raise ValueError(f"invalid search type: {search_type}")
|
|
||||||
|
|
||||||
|
|
||||||
def request(query: str, params: "OnlineParams") -> None:
|
|
||||||
engine_region: str = traits.get_region(params["searxng_locale"]) or "en-US"
|
|
||||||
|
|
||||||
args: dict[str, str | int] = {
|
|
||||||
"searchTerm": query,
|
|
||||||
"adultFilter": safe_search_map[params["safesearch"]],
|
|
||||||
"pageNumber": params["pageno"],
|
|
||||||
"country": engine_region.split("-")[-1],
|
|
||||||
"userLanguage": "en", # UI language: en, es or fr / no effect on search results
|
|
||||||
"market": engine_region,
|
|
||||||
}
|
|
||||||
if params["time_range"]:
|
|
||||||
args["freshness"] = time_range_map[params["time_range"]]
|
|
||||||
|
|
||||||
# Needed to circumvent Cloudflare bot protection
|
|
||||||
params['headers']['Referer'] = "https://karmasearch.org"
|
|
||||||
|
|
||||||
params["url"] = f"{base_url}/search/{search_type}?{urlencode(args)}"
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_date(date_string: str) -> datetime | None:
|
|
||||||
try:
|
|
||||||
return parser.parse(date_string)
|
|
||||||
except parser.ParserError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_general(result: dict[str, str]):
|
|
||||||
return MainResult(
|
|
||||||
url=result["url"],
|
|
||||||
title=result["title"],
|
|
||||||
content=html_to_text(result["description"]),
|
|
||||||
thumbnail=result.get("thumbnail", ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_news(result: dict[str, str]) -> LegacyResult:
|
|
||||||
return LegacyResult(
|
|
||||||
{
|
|
||||||
"url": result["url"],
|
|
||||||
"title": result["title"],
|
|
||||||
"content": html_to_text(result["description"]),
|
|
||||||
"thumbnail": result.get("thumbnail"),
|
|
||||||
"publishedDate": _parse_date(result.get("age", "")),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_videos(result: dict[str, t.Any]) -> LegacyResult:
|
|
||||||
return LegacyResult(
|
|
||||||
{
|
|
||||||
"template": "videos.html",
|
|
||||||
"url": result["url"],
|
|
||||||
"title": result["title"],
|
|
||||||
"content": html_to_text(result["description"]),
|
|
||||||
"thumbnail": result.get("thumbnail"),
|
|
||||||
"publishedDate": _parse_date(result.get("age", "")),
|
|
||||||
"length": result.get("video", {}).get("duration"),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_images(result: dict[str, t.Any]) -> LegacyResult:
|
|
||||||
return LegacyResult(
|
|
||||||
{
|
|
||||||
"template": "images.html",
|
|
||||||
"url": result["url"],
|
|
||||||
"title": result["title"],
|
|
||||||
"content": "",
|
|
||||||
"img_src": result.get("properties", {}).get("url"),
|
|
||||||
"thumbnail_src": result.get("thumbnail", {}).get("src"),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp: "SXNG_Response") -> EngineResults:
|
|
||||||
res = EngineResults()
|
|
||||||
|
|
||||||
json_resp: dict[str, t.Any] = resp.json()
|
|
||||||
if not isinstance(json_resp, dict):
|
|
||||||
return res # pyright: ignore[reportUnreachable]
|
|
||||||
|
|
||||||
for result in json_resp["results"]:
|
|
||||||
# hide sponsored results
|
|
||||||
if result.get("sponsored", False):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if "videos" in result:
|
|
||||||
for videos_result in result["videos"]:
|
|
||||||
res.add(_parse_videos(videos_result))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if "news" in result:
|
|
||||||
for news_result in result["news"]:
|
|
||||||
res.add(_parse_news(news_result))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if search_type == "news":
|
|
||||||
res.add(_parse_news(result))
|
|
||||||
elif search_type == "videos":
|
|
||||||
res.add(_parse_videos(result))
|
|
||||||
elif search_type == "images":
|
|
||||||
res.add(_parse_images(result))
|
|
||||||
else:
|
|
||||||
res.add(_parse_general(result))
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_traits(engine_traits: EngineTraits):
|
|
||||||
"""Fetch :ref:`languages <brave languages>` and :ref:`regions <brave
|
|
||||||
regions>` from Brave."""
|
|
||||||
|
|
||||||
# pylint: disable=import-outside-toplevel, too-many-branches
|
|
||||||
|
|
||||||
from lxml import html
|
|
||||||
import babel
|
|
||||||
|
|
||||||
from searx.locales import region_tag
|
|
||||||
from searx.network import get # see https://github.com/searxng/searxng/issues/762
|
|
||||||
|
|
||||||
# from searx.engines.xpath import extract_text
|
|
||||||
from searx.utils import gen_useragent
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
"Accept-Encoding": "gzip, deflate",
|
|
||||||
"Cache-Control": "no-cache",
|
|
||||||
"DNT": "1",
|
|
||||||
"Connection": "keep-alive",
|
|
||||||
"Accept-Language": "en,en-US;q=0.7,en;q=0.3",
|
|
||||||
"User-Agent": gen_useragent(),
|
|
||||||
}
|
|
||||||
|
|
||||||
resp = get("https://karmasearch.org/settings", headers=headers, timeout=5)
|
|
||||||
if not resp.ok:
|
|
||||||
raise RuntimeError("Response from Brave languages is not OK.")
|
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
|
||||||
for option in dom.xpath("//select[@name='country']/option"):
|
|
||||||
country_tag: str = option.get("value", "")
|
|
||||||
try:
|
|
||||||
sxng_tag = region_tag(babel.Locale.parse(country_tag, sep="-"))
|
|
||||||
except babel.UnknownLocaleError:
|
|
||||||
# silently ignore unknown languages
|
|
||||||
continue
|
|
||||||
# print("%-20s: %s <-- %s" % (extract_text(option), country_tag, sxng_tag))
|
|
||||||
|
|
||||||
conflict = engine_traits.regions.get(sxng_tag)
|
|
||||||
if conflict:
|
|
||||||
if conflict != country_tag:
|
|
||||||
print("CONFLICT: babel %s --> %s, %s" % (sxng_tag, conflict, country_tag))
|
|
||||||
continue
|
|
||||||
engine_traits.regions[sxng_tag] = country_tag
|
|
||||||
@@ -45,7 +45,7 @@ about = {
|
|||||||
base_url = "https://api2.marginalia-search.com"
|
base_url = "https://api2.marginalia-search.com"
|
||||||
safesearch = True
|
safesearch = True
|
||||||
categories = ["general"]
|
categories = ["general"]
|
||||||
paging = False
|
paging = True
|
||||||
results_per_page = 20
|
results_per_page = 20
|
||||||
api_key = None
|
api_key = None
|
||||||
"""To get an API key, please follow the instructions from `Key and license`_
|
"""To get an API key, please follow the instructions from `Key and license`_
|
||||||
@@ -85,7 +85,12 @@ class ApiSearchResults(t.TypedDict):
|
|||||||
|
|
||||||
def request(query: str, params: dict[str, t.Any]):
|
def request(query: str, params: dict[str, t.Any]):
|
||||||
|
|
||||||
query_params = {"count": results_per_page, "nsfw": min(params["safesearch"], 1), "query": query}
|
query_params = {
|
||||||
|
"page": params["pageno"],
|
||||||
|
"count": results_per_page,
|
||||||
|
"nsfw": min(params["safesearch"], 1),
|
||||||
|
"query": query,
|
||||||
|
}
|
||||||
|
|
||||||
params["url"] = f"{base_url}/search?{urlencode(query_params)}"
|
params["url"] = f"{base_url}/search?{urlencode(query_params)}"
|
||||||
params["headers"]["User-Agent"] = searxng_useragent()
|
params["headers"]["User-Agent"] = searxng_useragent()
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ about = {
|
|||||||
# engine dependent config
|
# engine dependent config
|
||||||
categories = ['general']
|
categories = ['general']
|
||||||
paging = True
|
paging = True
|
||||||
number_of_results = 5
|
page_size = 5
|
||||||
|
|
||||||
search_type: str = 'nearmatch'
|
search_type: str = 'nearmatch'
|
||||||
"""Which type of search to perform. One of the following values: ``nearmatch``,
|
"""Which type of search to perform. One of the following values: ``nearmatch``,
|
||||||
@@ -110,7 +110,7 @@ def request(query, params):
|
|||||||
params['language'] = params['language'].split('-')[0]
|
params['language'] = params['language'].split('-')[0]
|
||||||
|
|
||||||
api_url = f"{base_url.rstrip('/')}/{api_path}?".format(language=params['language'])
|
api_url = f"{base_url.rstrip('/')}/{api_path}?".format(language=params['language'])
|
||||||
offset = (params['pageno'] - 1) * number_of_results
|
offset = (params['pageno'] - 1) * page_size
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
'action': 'query',
|
'action': 'query',
|
||||||
@@ -118,7 +118,7 @@ def request(query, params):
|
|||||||
'format': 'json',
|
'format': 'json',
|
||||||
'srsearch': query,
|
'srsearch': query,
|
||||||
'sroffset': offset,
|
'sroffset': offset,
|
||||||
'srlimit': number_of_results,
|
'srlimit': page_size,
|
||||||
'srwhat': search_type,
|
'srwhat': search_type,
|
||||||
'srprop': srprop,
|
'srprop': srprop,
|
||||||
'srsort': srsort,
|
'srsort': srsort,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ about = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# engine dependent config
|
# engine dependent config
|
||||||
number_of_results = 20 # Don't put this over 5000
|
page_size = 20 # Don't put this over 5000
|
||||||
categories = ["it", "packages"]
|
categories = ["it", "packages"]
|
||||||
disabled = True
|
disabled = True
|
||||||
shortcut = "cpan"
|
shortcut = "cpan"
|
||||||
@@ -43,7 +43,7 @@ query_data_template = {
|
|||||||
{"date": {"order": "desc"}},
|
{"date": {"order": "desc"}},
|
||||||
],
|
],
|
||||||
'_source': ['documentation', "abstract"],
|
'_source': ['documentation', "abstract"],
|
||||||
'size': number_of_results,
|
'size': page_size,
|
||||||
}
|
}
|
||||||
search_url = urlunparse(["https", "fastapi.metacpan.org", "/v1/file/_search", "", "", ""])
|
search_url = urlunparse(["https", "fastapi.metacpan.org", "/v1/file/_search", "", "", ""])
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ def request(query, params):
|
|||||||
params["method"] = "POST"
|
params["method"] = "POST"
|
||||||
query_data = query_data_template
|
query_data = query_data_template
|
||||||
query_data["query"]["multi_match"]["query"] = query
|
query_data["query"]["multi_match"]["query"] = query
|
||||||
query_data["from"] = (params["pageno"] - 1) * number_of_results
|
query_data["from"] = (params["pageno"] - 1) * page_size
|
||||||
params["json"] = query_data
|
params["json"] = query_data
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,6 @@ def request(query, params):
|
|||||||
args = {
|
args = {
|
||||||
"q": query,
|
"q": query,
|
||||||
"safe": min(params["safesearch"], 1),
|
"safe": min(params["safesearch"], 1),
|
||||||
language_param: traits.get_language(params["searxng_locale"], traits.custom["language_all"]),
|
|
||||||
region_param: traits.get_region(params["searxng_locale"], traits.custom["region_all"]),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if search_type:
|
if search_type:
|
||||||
@@ -76,6 +74,10 @@ def request(query, params):
|
|||||||
logger.debug(args["since"])
|
logger.debug(args["since"])
|
||||||
|
|
||||||
params["url"] = f"{base_url}/search?{urlencode(args)}"
|
params["url"] = f"{base_url}/search?{urlencode(args)}"
|
||||||
|
params["cookies"] = {
|
||||||
|
language_param: traits.get_language(params["searxng_locale"], traits.custom["language_all"]),
|
||||||
|
region_param: traits.get_region(params["searxng_locale"], traits.custom["region_all"]),
|
||||||
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ def search(query, params) -> EngineResults:
|
|||||||
|
|
||||||
query = _client.find({key: q}).skip((params['pageno'] - 1) * results_per_page).limit(results_per_page)
|
query = _client.find({key: q}).skip((params['pageno'] - 1) * results_per_page).limit(results_per_page)
|
||||||
|
|
||||||
res.add(res.types.LegacyResult(number_of_results=query.count()))
|
|
||||||
for row in query:
|
for row in query:
|
||||||
del row['_id']
|
del row['_id']
|
||||||
kvmap = {str(k): str(v) for k, v in row.items()}
|
kvmap = {str(k): str(v) for k, v in row.items()}
|
||||||
|
|||||||
+1
-10
@@ -4,7 +4,6 @@
|
|||||||
.. _Odysee: https://github.com/OdyseeTeam/odysee-frontend
|
.. _Odysee: https://github.com/OdyseeTeam/odysee-frontend
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
@@ -12,6 +11,7 @@ import babel
|
|||||||
|
|
||||||
from searx.enginelib.traits import EngineTraits
|
from searx.enginelib.traits import EngineTraits
|
||||||
from searx.locales import language_tag
|
from searx.locales import language_tag
|
||||||
|
from searx.utils import format_duration
|
||||||
|
|
||||||
# Engine metadata
|
# Engine metadata
|
||||||
about = {
|
about = {
|
||||||
@@ -61,15 +61,6 @@ def request(query, params):
|
|||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
# Format the video duration
|
|
||||||
def format_duration(duration):
|
|
||||||
seconds = int(duration)
|
|
||||||
length = time.gmtime(seconds)
|
|
||||||
if length.tm_hour:
|
|
||||||
return time.strftime("%H:%M:%S", length)
|
|
||||||
return time.strftime("%M:%S", length)
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp):
|
||||||
data = resp.json()
|
data = resp.json()
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ about = {
|
|||||||
categories = ['images']
|
categories = ['images']
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
nb_per_page = 20
|
page_size = 20
|
||||||
|
|
||||||
base_url = 'https://api.openverse.org/v1/images/'
|
base_url = 'https://api.openverse.org/v1/images/'
|
||||||
search_string = '?page={page}&page_size={nb_per_page}&format=json&{query}'
|
search_string = '?page={page}&page_size={page_size}&format=json&{query}'
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
|
|
||||||
search_path = search_string.format(query=urlencode({'q': query}), nb_per_page=nb_per_page, page=params['pageno'])
|
search_path = search_string.format(query=urlencode({'q': query}), page_size=page_size, page=params['pageno'])
|
||||||
|
|
||||||
params['url'] = base_url + search_path
|
params['url'] = base_url + search_path
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from lxml import html
|
|||||||
from searx.result_types import EngineResults
|
from searx.result_types import EngineResults
|
||||||
from searx.utils import eval_xpath_list, gen_useragent
|
from searx.utils import eval_xpath_list, gen_useragent
|
||||||
from searx.enginelib import EngineCache
|
from searx.enginelib import EngineCache
|
||||||
from searx.exceptions import SearxEngineAPIException
|
from searx.exceptions import SearxEngineAPIException, SearxEngineAccessDeniedException
|
||||||
from searx.network import get
|
from searx.network import get
|
||||||
|
|
||||||
|
|
||||||
@@ -58,6 +58,8 @@ def _get_secret_key():
|
|||||||
# circumvents Cloudflare bot protections
|
# circumvents Cloudflare bot protections
|
||||||
"User-Agent": gen_useragent(),
|
"User-Agent": gen_useragent(),
|
||||||
"Referer": base_url,
|
"Referer": base_url,
|
||||||
|
"Sec-GPC": "1",
|
||||||
|
"Connection": "keep-alive",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ def request(query, params):
|
|||||||
try:
|
try:
|
||||||
secret_key = _get_secret_key()
|
secret_key = _get_secret_key()
|
||||||
CACHE.set(SECRET_KEY_DB_KEY, secret_key)
|
CACHE.set(SECRET_KEY_DB_KEY, secret_key)
|
||||||
except SearxEngineAPIException as e:
|
except (SearxEngineAPIException, SearxEngineAccessDeniedException) as e:
|
||||||
logger.debug("failed to extract API key %s" % e)
|
logger.debug("failed to extract API key %s" % e)
|
||||||
secret_key = api_key
|
secret_key = api_key
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ about = {
|
|||||||
# engine dependent config
|
# engine dependent config
|
||||||
categories = ['map']
|
categories = ['map']
|
||||||
paging = False
|
paging = False
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
|
|
||||||
# search-url
|
# search-url
|
||||||
base_url = 'https://photon.komoot.io/'
|
base_url = 'https://photon.komoot.io/'
|
||||||
@@ -33,7 +33,7 @@ supported_languages = ['de', 'en', 'fr', 'it']
|
|||||||
|
|
||||||
# do search-request
|
# do search-request
|
||||||
def request(query, params):
|
def request(query, params):
|
||||||
params['url'] = base_url + search_string.format(query=urlencode({'q': query}), limit=number_of_results)
|
params['url'] = base_url + search_string.format(query=urlencode({'q': query}), limit=page_size)
|
||||||
|
|
||||||
if params['language'] != 'all':
|
if params['language'] != 'all':
|
||||||
language = params['language'].split('_')[0]
|
language = params['language'].split('_')[0]
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
"""Podcast Index"""
|
|
||||||
|
|
||||||
from urllib.parse import quote_plus
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
about = {
|
|
||||||
'website': 'https://podcastindex.org',
|
|
||||||
'official_api_documentation': None, # requires an account
|
|
||||||
'use_official_api': False,
|
|
||||||
'require_api_key': False,
|
|
||||||
'results': 'JSON',
|
|
||||||
}
|
|
||||||
categories = []
|
|
||||||
|
|
||||||
base_url = "https://podcastindex.org"
|
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
|
||||||
params['url'] = f"{base_url}/api/search/byterm?q={quote_plus(query)}"
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
|
||||||
results = []
|
|
||||||
|
|
||||||
json = resp.json()
|
|
||||||
|
|
||||||
for result in json['feeds']:
|
|
||||||
results.append(
|
|
||||||
{
|
|
||||||
'url': result['link'],
|
|
||||||
'title': result['title'],
|
|
||||||
'content': result['description'],
|
|
||||||
'thumbnail': result['image'],
|
|
||||||
'publishedDate': datetime.fromtimestamp(result['newestItemPubdate']),
|
|
||||||
'metadata': f"{result['author']}, {result['episodeCount']} episodes",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return results
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""Public domain image archive"""
|
"""Public domain image archive"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl
|
from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl
|
||||||
from json import dumps
|
from json import dumps
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ paging = True
|
|||||||
|
|
||||||
__CACHED_API_URL = None
|
__CACHED_API_URL = None
|
||||||
|
|
||||||
|
_API_URL_RE = re.compile(r"\"(https://.*?/search-proxy)\"")
|
||||||
|
|
||||||
|
|
||||||
def _clean_url(url):
|
def _clean_url(url):
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
@@ -74,11 +78,12 @@ def _get_algolia_api_url():
|
|||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
raise LookupError("Failed to obtain AWS api url for PDImageArchive")
|
raise LookupError("Failed to obtain AWS api url for PDImageArchive")
|
||||||
|
|
||||||
api_url = extr(resp.text, 'const r="', '"', default=None)
|
api_url_match = _API_URL_RE.search(resp.text)
|
||||||
|
if api_url_match is None:
|
||||||
if api_url is None:
|
|
||||||
raise LookupError("Couldn't obtain AWS api url for PDImageArchive")
|
raise LookupError("Couldn't obtain AWS api url for PDImageArchive")
|
||||||
|
|
||||||
|
api_url = api_url_match.group(1)
|
||||||
|
|
||||||
__CACHED_API_URL = api_url
|
__CACHED_API_URL = api_url
|
||||||
return api_url
|
return api_url
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ categories = ["science", "scientific publications"]
|
|||||||
eutils_api = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
|
eutils_api = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils"
|
||||||
|
|
||||||
# engine dependent config
|
# engine dependent config
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
pubmed_url = "https://www.ncbi.nlm.nih.gov/pubmed/"
|
pubmed_url = "https://www.ncbi.nlm.nih.gov/pubmed/"
|
||||||
|
|
||||||
|
|
||||||
@@ -67,8 +67,8 @@ def request(query: str, params: "OnlineParams") -> None:
|
|||||||
{
|
{
|
||||||
"db": "pubmed",
|
"db": "pubmed",
|
||||||
"term": query,
|
"term": query,
|
||||||
"retstart": (params["pageno"] - 1) * number_of_results,
|
"retstart": (params["pageno"] - 1) * page_size,
|
||||||
"hits": number_of_results,
|
"hits": page_size,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
esearch_url = f"{eutils_api}/esearch.fcgi?{args}"
|
esearch_url = f"{eutils_api}/esearch.fcgi?{args}"
|
||||||
|
|||||||
+112
-148
@@ -6,7 +6,6 @@ engineered by reading the network log of https://www.qwant.com/ queries.
|
|||||||
For Qwant's *web-search* two alternatives are implemented:
|
For Qwant's *web-search* two alternatives are implemented:
|
||||||
|
|
||||||
- ``web``: uses the :py:obj:`api_url` which returns a JSON structure
|
- ``web``: uses the :py:obj:`api_url` which returns a JSON structure
|
||||||
- ``web-lite``: uses the :py:obj:`web_lite_url` which returns a HTML page
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
@@ -22,7 +21,7 @@ This implementation is used by different qwant engines in the :ref:`settings.yml
|
|||||||
.. code:: yaml
|
.. code:: yaml
|
||||||
|
|
||||||
- name: qwant
|
- name: qwant
|
||||||
qwant_categ: web-lite # alternatively use 'web'
|
qwant_categ: web
|
||||||
...
|
...
|
||||||
- name: qwant news
|
- name: qwant news
|
||||||
qwant_categ: news
|
qwant_categ: news
|
||||||
@@ -39,6 +38,8 @@ Implementations
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from datetime import (
|
from datetime import (
|
||||||
datetime,
|
datetime,
|
||||||
timedelta,
|
timedelta,
|
||||||
@@ -47,8 +48,7 @@ from json import loads
|
|||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
import babel
|
import babel
|
||||||
import lxml
|
from flask_babel import gettext # pyright: ignore[reportUnknownVariableType]
|
||||||
from flask_babel import gettext
|
|
||||||
|
|
||||||
from searx.enginelib.traits import EngineTraits
|
from searx.enginelib.traits import EngineTraits
|
||||||
from searx.exceptions import (
|
from searx.exceptions import (
|
||||||
@@ -59,11 +59,13 @@ from searx.exceptions import (
|
|||||||
)
|
)
|
||||||
from searx.network import raise_for_httperror
|
from searx.network import raise_for_httperror
|
||||||
from searx.utils import (
|
from searx.utils import (
|
||||||
eval_xpath,
|
|
||||||
eval_xpath_list,
|
|
||||||
extract_text,
|
|
||||||
get_embeded_stream_url,
|
get_embeded_stream_url,
|
||||||
)
|
)
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
|
||||||
# about
|
# about
|
||||||
about = {
|
about = {
|
||||||
@@ -82,113 +84,66 @@ max_page = 5
|
|||||||
"""5 pages maximum (``&p=5``): Trying to do more just results in an improper
|
"""5 pages maximum (``&p=5``): Trying to do more just results in an improper
|
||||||
redirect"""
|
redirect"""
|
||||||
|
|
||||||
qwant_categ = None
|
qwant_categ: str = None # pyright: ignore[reportAssignmentType]
|
||||||
"""One of ``web-lite`` (or ``web``), ``news``, ``images`` or ``videos``"""
|
"""One of ``web``, ``news``, ``images`` or ``videos``"""
|
||||||
|
|
||||||
safesearch = True
|
safesearch = True
|
||||||
# safe_search_map = {0: '&safesearch=0', 1: '&safesearch=1', 2: '&safesearch=2'}
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
qwant_news_locales = [
|
qwant_news_locales = [
|
||||||
'ca_ad', 'ca_es', 'ca_fr', 'co_fr', 'de_at', 'de_ch', 'de_de', 'en_au',
|
"ca_ad", "ca_es", "ca_fr", "co_fr", "de_at", "de_ch", "de_de", "en_au",
|
||||||
'en_ca', 'en_gb', 'en_ie', 'en_my', 'en_nz', 'en_us', 'es_ad', 'es_ar',
|
"en_ca", "en_gb", "en_ie", "en_my", "en_nz", "en_us", "es_ad", "es_ar",
|
||||||
'es_cl', 'es_co', 'es_es', 'es_mx', 'es_pe', 'eu_es', 'eu_fr', 'fc_ca',
|
"es_cl", "es_co", "es_es", "es_mx", "es_pe", "eu_es", "eu_fr", "fc_ca",
|
||||||
'fr_ad', 'fr_be', 'fr_ca', 'fr_ch', 'fr_fr', 'it_ch', 'it_it', 'nl_be',
|
"fr_ad", "fr_be", "fr_ca", "fr_ch", "fr_fr", "it_ch", "it_it", "nl_be",
|
||||||
'nl_nl', 'pt_ad', 'pt_pt',
|
"nl_nl", "pt_ad", "pt_pt",
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
# search-url
|
|
||||||
|
|
||||||
api_url = "https://api.qwant.com/v3/search/"
|
api_url = "https://api.qwant.com/v3/search/"
|
||||||
"""URL of Qwant's API (JSON)"""
|
"""URL of Qwant's API (JSON)"""
|
||||||
|
|
||||||
web_lite_url = "https://lite.qwant.com/"
|
|
||||||
"""URL of Qwant-Lite (HTML)"""
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
def request(query, params):
|
|
||||||
"""Qwant search request"""
|
"""Qwant search request"""
|
||||||
|
|
||||||
if not query:
|
if not query:
|
||||||
return None
|
return
|
||||||
|
|
||||||
q_locale = traits.get_region(params["searxng_locale"], default="en_US")
|
q_locale = traits.get_region(params["searxng_locale"], default="en_US")
|
||||||
|
|
||||||
url = api_url + f"{qwant_categ}?"
|
results_per_page = 10
|
||||||
args = {"q": query}
|
if qwant_categ == "images":
|
||||||
|
results_per_page = 50
|
||||||
|
args = {
|
||||||
|
"q": query,
|
||||||
|
"count": results_per_page,
|
||||||
|
"locale": q_locale,
|
||||||
|
"offset": (params["pageno"] - 1) * results_per_page,
|
||||||
|
"device": "desktop",
|
||||||
|
"safesearch": params["safesearch"],
|
||||||
|
"tgp": 1,
|
||||||
|
"display": True,
|
||||||
|
"llm": True,
|
||||||
|
}
|
||||||
params["raise_for_httperror"] = False
|
params["raise_for_httperror"] = False
|
||||||
|
|
||||||
if qwant_categ == "web-lite":
|
params["url"] = f"{api_url}{qwant_categ}?{urlencode(args)}"
|
||||||
url = web_lite_url + "?"
|
|
||||||
args["locale"] = q_locale.lower()
|
|
||||||
args["l"] = q_locale.split("_")[0]
|
|
||||||
args["s"] = params["safesearch"]
|
|
||||||
args["p"] = params["pageno"]
|
|
||||||
|
|
||||||
params["raise_for_httperror"] = True
|
|
||||||
|
|
||||||
elif qwant_categ == "images":
|
|
||||||
args["count"] = 50
|
|
||||||
args["locale"] = q_locale
|
|
||||||
args["safesearch"] = params["safesearch"]
|
|
||||||
args["tgp"] = 3
|
|
||||||
args["offset"] = (params["pageno"] - 1) * args["count"]
|
|
||||||
|
|
||||||
else: # web, news, videos
|
|
||||||
args["count"] = 10
|
|
||||||
args["locale"] = q_locale
|
|
||||||
args["safesearch"] = params["safesearch"]
|
|
||||||
args["llm"] = "false"
|
|
||||||
args["tgp"] = 3
|
|
||||||
args["offset"] = (params["pageno"] - 1) * args["count"]
|
|
||||||
|
|
||||||
params["url"] = url + urlencode(args)
|
|
||||||
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
|
||||||
if qwant_categ == "web-lite":
|
|
||||||
return parse_web_lite(resp)
|
|
||||||
return parse_web_api(resp)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_web_lite(resp):
|
|
||||||
"""Parse results from Qwant-Lite"""
|
|
||||||
|
|
||||||
results = []
|
|
||||||
dom = lxml.html.fromstring(resp.text)
|
|
||||||
|
|
||||||
for item in eval_xpath_list(dom, "//section/article"):
|
|
||||||
if eval_xpath(item, "./span[contains(@class, 'tooltip')]"):
|
|
||||||
# ignore randomly interspersed advertising adds
|
|
||||||
continue
|
|
||||||
results.append(
|
|
||||||
{
|
|
||||||
"url": extract_text(eval_xpath(item, "./span[contains(@class, 'url partner')]")),
|
|
||||||
"title": extract_text(eval_xpath(item, "./h2/a")),
|
|
||||||
"content": extract_text(eval_xpath(item, "./p")),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def parse_web_api(resp):
|
|
||||||
"""Parse results from Qwant's API"""
|
"""Parse results from Qwant's API"""
|
||||||
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
|
# pylint: disable=too-many-locals, too-many-branches, too-many-statements
|
||||||
|
|
||||||
results = []
|
res = EngineResults()
|
||||||
|
|
||||||
# Try to load JSON result
|
# Try to load JSON result
|
||||||
|
search_results: dict[str, t.Any] = {}
|
||||||
try:
|
try:
|
||||||
search_results = loads(resp.text)
|
search_results = resp.json()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
search_results = {}
|
pass
|
||||||
|
|
||||||
data = search_results.get("data", {})
|
data: dict[str, t.Any] = search_results.get("data", {}) # pyright: ignore[reportAny]
|
||||||
|
|
||||||
# check for an API error
|
# check for an API error
|
||||||
if search_results.get("status") != "success":
|
if search_results.get("status") != "success":
|
||||||
@@ -207,13 +162,13 @@ def parse_web_api(resp):
|
|||||||
|
|
||||||
if qwant_categ == "web":
|
if qwant_categ == "web":
|
||||||
# The WEB query contains a list named 'mainline'. This list can contain
|
# The WEB query contains a list named 'mainline'. This list can contain
|
||||||
# different result types (e.g. mainline[0]['type'] returns type of the
|
# different result types (e.g. mainline[0]["type"] returns type of the
|
||||||
# result items in mainline[0]['items']
|
# result items in mainline[0]["items"]
|
||||||
mainline = data.get("result", {}).get("items", {}).get("mainline", {})
|
mainline = data.get("result", {}).get("items", {}).get("mainline", {})
|
||||||
else:
|
else:
|
||||||
# Queries on News, Images and Videos do not have a list named 'mainline'
|
# Queries on News, Images and Videos do not have a list named 'mainline'
|
||||||
# in the response. The result items are directly in the list
|
# in the response. The result items are directly in the list
|
||||||
# result['items'].
|
# result["items"].
|
||||||
mainline = data.get("result", {}).get("items", [])
|
mainline = data.get("result", {}).get("items", [])
|
||||||
mainline = [
|
mainline = [
|
||||||
{"type": qwant_categ, "items": mainline},
|
{"type": qwant_categ, "items": mainline},
|
||||||
@@ -221,8 +176,9 @@ def parse_web_api(resp):
|
|||||||
|
|
||||||
# return empty array if there are no results
|
# return empty array if there are no results
|
||||||
if not mainline:
|
if not mainline:
|
||||||
return []
|
return res
|
||||||
|
|
||||||
|
row: dict[str, t.Any]
|
||||||
for row in mainline:
|
for row in mainline:
|
||||||
mainline_type = row.get("type", "web")
|
mainline_type = row.get("type", "web")
|
||||||
if mainline_type != qwant_categ:
|
if mainline_type != qwant_categ:
|
||||||
@@ -232,90 +188,98 @@ def parse_web_api(resp):
|
|||||||
# ignore adds
|
# ignore adds
|
||||||
continue
|
continue
|
||||||
|
|
||||||
mainline_items = row.get("items", [])
|
mainline_items: list[dict[str, t.Any]] = row.get("items", [])
|
||||||
for item in mainline_items:
|
for item in mainline_items:
|
||||||
title = item.get("title", None)
|
|
||||||
res_url = item.get("url", None)
|
title: str = item.get("title", "")
|
||||||
|
res_url: str = item.get("url", "")
|
||||||
|
pub_date: datetime | None = None
|
||||||
|
thumbnail: str = ""
|
||||||
|
content: str = item.get("desc", "")
|
||||||
|
|
||||||
|
_date: float | None = item.get("date")
|
||||||
|
if _date:
|
||||||
|
try:
|
||||||
|
pub_date = datetime.fromtimestamp(_date)
|
||||||
|
except ValueError:
|
||||||
|
# news' date value milli seconds
|
||||||
|
pub_date = datetime.fromtimestamp(_date / 1000)
|
||||||
|
|
||||||
if mainline_type == "web":
|
if mainline_type == "web":
|
||||||
content = item["desc"]
|
res.add(
|
||||||
results.append(
|
res.types.MainResult(
|
||||||
{
|
title=title,
|
||||||
"title": title,
|
url=res_url,
|
||||||
"url": res_url,
|
content=content,
|
||||||
"content": content,
|
)
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
elif mainline_type == "news":
|
elif mainline_type == "news":
|
||||||
pub_date = item["date"]
|
|
||||||
if pub_date is not None:
|
|
||||||
pub_date = datetime.fromtimestamp(pub_date)
|
|
||||||
news_media = item.get("media", [])
|
news_media = item.get("media", [])
|
||||||
thumbnail = None
|
|
||||||
if news_media:
|
if news_media:
|
||||||
thumbnail = news_media[0].get("pict", {}).get("url", None)
|
thumbnail = news_media[0].get("pict", {}).get("url", "")
|
||||||
results.append(
|
|
||||||
{
|
res.add(
|
||||||
"title": title,
|
res.types.MainResult(
|
||||||
"url": res_url,
|
title=title,
|
||||||
"publishedDate": pub_date,
|
content=content,
|
||||||
"thumbnail": thumbnail,
|
url=res_url,
|
||||||
}
|
publishedDate=pub_date,
|
||||||
|
thumbnail=thumbnail,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif mainline_type == "images":
|
elif mainline_type == "images":
|
||||||
thumbnail = item["thumbnail"]
|
res.add(
|
||||||
img_src = item["media"]
|
res.types.LegacyResult(
|
||||||
results.append(
|
title=title,
|
||||||
{
|
url=res_url,
|
||||||
"title": title,
|
template="images.html",
|
||||||
"url": res_url,
|
thumbnail_src=item["thumbnail"] or "",
|
||||||
"template": "images.html",
|
img_src=item["media"] or "",
|
||||||
"thumbnail_src": thumbnail,
|
resolution=f"{item['width']} x {item['height']}",
|
||||||
"img_src": img_src,
|
img_format=item.get("thumb_type"),
|
||||||
"resolution": f"{item['width']} x {item['height']}",
|
)
|
||||||
"img_format": item.get("thumb_type"),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
elif mainline_type == "videos":
|
elif mainline_type == "videos":
|
||||||
# some videos do not have a description: while qwant-video
|
# some videos do not have a description: while qwant-video
|
||||||
# returns an empty string, such video from a qwant-web query
|
# returns an empty string, such video from a qwant-web query
|
||||||
# miss the 'desc' key.
|
# miss the 'desc' key.
|
||||||
d, s, c = item.get("desc"), item.get("source"), item.get("channel")
|
|
||||||
content_parts = []
|
d: str = item.get("desc", "")
|
||||||
|
s: str = item.get("source", "")
|
||||||
|
c: str = item.get("channel", "")
|
||||||
|
|
||||||
|
content_parts: list[str] = []
|
||||||
if d:
|
if d:
|
||||||
content_parts.append(d)
|
content_parts.append(f"{d}")
|
||||||
if s:
|
if s:
|
||||||
content_parts.append("%s: %s " % (gettext("Source"), s))
|
content_parts.append(f"{gettext('Source')}: {s} ")
|
||||||
if c:
|
if c:
|
||||||
content_parts.append("%s: %s " % (gettext("Channel"), c))
|
content_parts.append(f"{gettext('Channel')}: {c} ")
|
||||||
content = " // ".join(content_parts)
|
content = " // ".join(content_parts)
|
||||||
length = item["duration"]
|
|
||||||
if length is not None:
|
length = timedelta(milliseconds=(item["duration"] or 0))
|
||||||
length = timedelta(milliseconds=length)
|
thumbnail = item["thumbnail"] or ""
|
||||||
pub_date = item["date"]
|
|
||||||
if pub_date is not None:
|
|
||||||
pub_date = datetime.fromtimestamp(pub_date)
|
|
||||||
thumbnail = item["thumbnail"]
|
|
||||||
# from some locations (DE and others?) the s2 link do
|
# from some locations (DE and others?) the s2 link do
|
||||||
# response a 'Please wait ..' but does not deliver the thumbnail
|
# response a 'Please wait ..' but does not deliver the thumbnail
|
||||||
thumbnail = thumbnail.replace("https://s2.qwant.com", "https://s1.qwant.com", 1)
|
thumbnail = thumbnail.replace("https://s2.qwant.com", "https://s1.qwant.com", 1)
|
||||||
results.append(
|
|
||||||
{
|
res.add(
|
||||||
"title": title,
|
res.types.LegacyResult(
|
||||||
"url": res_url,
|
title=title,
|
||||||
"content": content,
|
url=res_url,
|
||||||
"iframe_src": get_embeded_stream_url(res_url),
|
content=content,
|
||||||
"publishedDate": pub_date,
|
iframe_src=get_embeded_stream_url(res_url),
|
||||||
"thumbnail": thumbnail,
|
publishedDate=pub_date,
|
||||||
"template": "videos.html",
|
thumbnail=thumbnail,
|
||||||
"length": length,
|
template="videos.html",
|
||||||
}
|
length=length,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return results
|
return res
|
||||||
|
|
||||||
|
|
||||||
def fetch_traits(engine_traits: EngineTraits):
|
def fetch_traits(engine_traits: EngineTraits):
|
||||||
@@ -326,7 +290,7 @@ def fetch_traits(engine_traits: EngineTraits):
|
|||||||
from searx.utils import extr
|
from searx.utils import extr
|
||||||
|
|
||||||
resp = get(
|
resp = get(
|
||||||
about["website"],
|
about["website"], # pyright: ignore[reportArgumentType]
|
||||||
timeout=5,
|
timeout=5,
|
||||||
)
|
)
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
@@ -336,7 +300,7 @@ def fetch_traits(engine_traits: EngineTraits):
|
|||||||
|
|
||||||
q_initial_props = loads(json_string)
|
q_initial_props = loads(json_string)
|
||||||
q_locales = q_initial_props.get("locales")
|
q_locales = q_initial_props.get("locales")
|
||||||
eng_tag_list = set()
|
eng_tag_list: set[str] = set()
|
||||||
|
|
||||||
for country, v in q_locales.items():
|
for country, v in q_locales.items():
|
||||||
for lang in v["langs"]:
|
for lang in v["langs"]:
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import socket
|
import socket
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
@@ -28,7 +29,7 @@ about = {
|
|||||||
paging = True
|
paging = True
|
||||||
categories = ["music", "radio"]
|
categories = ["music", "radio"]
|
||||||
|
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
|
|
||||||
station_filters = [] # ['countrycode', 'language']
|
station_filters = [] # ['countrycode', 'language']
|
||||||
"""A list of filters to be applied to the search of radio stations. By default
|
"""A list of filters to be applied to the search of radio stations. By default
|
||||||
@@ -59,7 +60,19 @@ seconds."""
|
|||||||
def init(_):
|
def init(_):
|
||||||
global CACHE # pylint: disable=global-statement
|
global CACHE # pylint: disable=global-statement
|
||||||
CACHE = EngineCache("radio_browser")
|
CACHE = EngineCache("radio_browser")
|
||||||
|
|
||||||
|
# In an environment with competing processes, the initial loading of the
|
||||||
|
# cache is required only once.
|
||||||
|
eng_state: str | None = CACHE.get("eng_state")
|
||||||
|
if not eng_state or not eng_state.startswith("STATE:"):
|
||||||
|
CACHE.set("eng_state", f"STATE: being initialized by PID {os.getpid()}")
|
||||||
|
try:
|
||||||
server_list()
|
server_list()
|
||||||
|
except Exception:
|
||||||
|
CACHE.set("eng_state", f"ERROR: initialization by PID {os.getpid()} failed.")
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
logger.debug(eng_state)
|
||||||
|
|
||||||
|
|
||||||
def server_list() -> list[str]:
|
def server_list() -> list[str]:
|
||||||
@@ -100,8 +113,8 @@ def request(query, params):
|
|||||||
args = {
|
args = {
|
||||||
"name": query,
|
"name": query,
|
||||||
"order": "votes",
|
"order": "votes",
|
||||||
"offset": (params["pageno"] - 1) * number_of_results,
|
"offset": (params["pageno"] - 1) * page_size,
|
||||||
"limit": number_of_results,
|
"limit": page_size,
|
||||||
"hidebroken": "true",
|
"hidebroken": "true",
|
||||||
"reverse": "true",
|
"reverse": "true",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,4 @@ def response(resp):
|
|||||||
|
|
||||||
results.extend({'suggestion': s} for s in response_json['suggestions'])
|
results.extend({'suggestion': s} for s in response_json['suggestions'])
|
||||||
|
|
||||||
results.append({'number_of_results': response_json['number_of_results']})
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Seek ninja (general)"""
|
||||||
|
|
||||||
|
from json import loads
|
||||||
|
from hashlib import sha256
|
||||||
|
from urllib.parse import urlencode, quote_plus
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.network import get
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
from searx.utils import extr, html_to_text
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://seek.ninja",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
safesearch = True
|
||||||
|
|
||||||
|
base_url = "https://seek.ninja"
|
||||||
|
categories = ["general"]
|
||||||
|
|
||||||
|
safe_search_map = {0: "off", 1: "moderate", 2: "strict"}
|
||||||
|
|
||||||
|
PowChallenge = dict[str, t.Any]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_challenge(query: str) -> PowChallenge:
|
||||||
|
"""Extract the challenge parameters (i.e. nonce, difficulty, ...) from the
|
||||||
|
search website."""
|
||||||
|
|
||||||
|
resp = get(f"{base_url}/s?q={quote_plus(query)}")
|
||||||
|
challenge_raw_json = "{" + extr(resp.text, "pow: {", "},") + "}"
|
||||||
|
return loads(challenge_raw_json)
|
||||||
|
|
||||||
|
|
||||||
|
def _solve_pow(challenge: PowChallenge) -> list[int]:
|
||||||
|
"""Solves a Proof of Work SHA256 challenges. This is a 1:1 port of the
|
||||||
|
site's JS code.
|
||||||
|
|
||||||
|
On a high-level, it tries to ``k`` amount of solutions, where its sha256
|
||||||
|
hash begins with: ``leading`` 0s, i.e.
|
||||||
|
|
||||||
|
.. code: js
|
||||||
|
|
||||||
|
sha256(nonce || solution).startswith("0" * leading)
|
||||||
|
"""
|
||||||
|
nonce = challenge["nonce"]
|
||||||
|
k = int(challenge["k"])
|
||||||
|
indifficulty = float(challenge["indifficulty"])
|
||||||
|
|
||||||
|
leading = int(indifficulty)
|
||||||
|
frac = indifficulty - leading
|
||||||
|
prefix = "".join("0" for _ in range(0, leading))
|
||||||
|
|
||||||
|
maxNib = 15 - int(frac * 16) if frac else 15
|
||||||
|
|
||||||
|
solutions: list[int] = []
|
||||||
|
ans = 0
|
||||||
|
while len(solutions) < k:
|
||||||
|
h = sha256(f"{nonce}{ans}".encode()).hexdigest()
|
||||||
|
if h.startswith(prefix) and (not frac or int(h[leading], base=16) <= maxNib):
|
||||||
|
solutions.append(ans)
|
||||||
|
ans += 1
|
||||||
|
return solutions
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: 'OnlineParams') -> None:
|
||||||
|
challenge = _get_challenge(query)
|
||||||
|
solution = _solve_pow(challenge)
|
||||||
|
args = {
|
||||||
|
"q": query,
|
||||||
|
"panswers": ",".join(str(s) for s in solution),
|
||||||
|
"pid": challenge["challengeId"],
|
||||||
|
"adult": safe_search_map[params["safesearch"]],
|
||||||
|
}
|
||||||
|
params["url"] = f"{base_url}/search-sse?{urlencode(args)}"
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: 'SXNG_Response') -> EngineResults:
|
||||||
|
res = EngineResults()
|
||||||
|
# The response is a stream of server-side events,
|
||||||
|
# so it is split into `event: <type>` and `data: {"results": ...}`
|
||||||
|
# see https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/
|
||||||
|
events = resp.text.split("\n\n")
|
||||||
|
for event in events:
|
||||||
|
event_parts = event.split("\n", maxsplit=2)
|
||||||
|
if len(event_parts) != 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
event_name, data = event_parts
|
||||||
|
if not event_name.endswith("resultsUpdate"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
json_data = loads(data.removeprefix("data: "))
|
||||||
|
for result in json_data["results"]:
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["url"],
|
||||||
|
title=result["title"],
|
||||||
|
content=html_to_text(result["blurb"]),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -74,7 +74,7 @@ about = {
|
|||||||
categories = ["science", "scientific publications"]
|
categories = ["science", "scientific publications"]
|
||||||
|
|
||||||
paging = True
|
paging = True
|
||||||
nb_per_page = 10
|
page_size = 10
|
||||||
"""Number of results to return in the request, see `Pagination and Limits`_ for
|
"""Number of results to return in the request, see `Pagination and Limits`_ for
|
||||||
more details.
|
more details.
|
||||||
|
|
||||||
@@ -109,8 +109,8 @@ def request(query: str, params: "OnlineParams") -> None:
|
|||||||
args = {
|
args = {
|
||||||
"api_key": api_key,
|
"api_key": api_key,
|
||||||
"q": query,
|
"q": query,
|
||||||
"s": nb_per_page * (params["pageno"] - 1),
|
"s": page_size * (params["pageno"] - 1),
|
||||||
"p": nb_per_page,
|
"p": page_size,
|
||||||
}
|
}
|
||||||
params["url"] = f"{base_url}?{urlencode(args)}"
|
params["url"] = f"{base_url}?{urlencode(args)}"
|
||||||
# For example, the ``year:`` filter requires a *Premium Plan* subscription.
|
# For example, the ``year:`` filter requires a *Premium Plan* subscription.
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ time_range_support = True
|
|||||||
safesearch = True
|
safesearch = True
|
||||||
|
|
||||||
time_range_dict = {"day": "d", "week": "w", "month": "m", "year": "y"}
|
time_range_dict = {"day": "d", "week": "w", "month": "m", "year": "y"}
|
||||||
safesearch_dict = {0: "1", 1: "0", 2: "0"}
|
safesearch_dict = {0: "none", 1: "moderate", 2: "heavy"}
|
||||||
|
|
||||||
# search-url
|
# search-url
|
||||||
base_url = "https://www.startpage.com"
|
base_url = "https://www.startpage.com"
|
||||||
@@ -251,9 +251,10 @@ def request(query, params):
|
|||||||
"t": "device",
|
"t": "device",
|
||||||
"sc": get_sc_code(params),
|
"sc": get_sc_code(params),
|
||||||
"with_date": time_range_dict.get(params["time_range"], ""),
|
"with_date": time_range_dict.get(params["time_range"], ""),
|
||||||
"abp": "1",
|
|
||||||
"abd": "1",
|
"abd": "1",
|
||||||
"abe": "1",
|
"abe": "1",
|
||||||
|
"qsr": "all",
|
||||||
|
"qadf": safesearch_dict[params["safesearch"]],
|
||||||
}
|
}
|
||||||
|
|
||||||
if engine_language:
|
if engine_language:
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
"""Svgrepo (icons)"""
|
|
||||||
|
|
||||||
from lxml import html
|
|
||||||
from searx.utils import extract_text, eval_xpath, eval_xpath_list
|
|
||||||
|
|
||||||
about = {
|
|
||||||
"website": 'https://www.svgrepo.com',
|
|
||||||
"official_api_documentation": 'https://svgapi.com',
|
|
||||||
"use_official_api": False,
|
|
||||||
"require_api_key": False,
|
|
||||||
"results": 'HTML',
|
|
||||||
}
|
|
||||||
|
|
||||||
paging = True
|
|
||||||
categories = ['images', 'icons']
|
|
||||||
base_url = "https://www.svgrepo.com"
|
|
||||||
|
|
||||||
results_xpath = "//div[@class='style_nodeListing__7Nmro']/div"
|
|
||||||
url_xpath = ".//a/@href"
|
|
||||||
title_xpath = ".//a/@title"
|
|
||||||
img_src_xpath = ".//img/@src"
|
|
||||||
|
|
||||||
|
|
||||||
def request(query, params):
|
|
||||||
params['url'] = f"{base_url}/vectors/{query}/{params['pageno']}/"
|
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp):
|
|
||||||
results = []
|
|
||||||
|
|
||||||
dom = html.fromstring(resp.text)
|
|
||||||
for result in eval_xpath_list(dom, results_xpath):
|
|
||||||
results.append(
|
|
||||||
{
|
|
||||||
'template': 'images.html',
|
|
||||||
'url': base_url + extract_text(eval_xpath(result, url_xpath)),
|
|
||||||
'title': extract_text(eval_xpath(result, title_xpath)).replace(" SVG File", "").replace("Show ", ""),
|
|
||||||
'img_src': extract_text(eval_xpath(result, img_src_xpath)),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return results
|
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
"""Swisscows (general, images, videos)"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import codecs
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from babel.core import get_global
|
||||||
|
|
||||||
|
from searx.result_types import EngineResults, LegacyResult # pyright: ignore[reportPrivateLocalImportUsage]
|
||||||
|
from searx.utils import humanize_number, html_to_text
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://swisscows.com",
|
||||||
|
"wikidata_id": "Q22937452",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
categories = ["general"]
|
||||||
|
swisscows_category = "web" # possible: "web", "videos", "images"
|
||||||
|
|
||||||
|
results_per_page = 50
|
||||||
|
|
||||||
|
time_range_support = True
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
base_url = "https://api.swisscows.com"
|
||||||
|
|
||||||
|
CAESAR_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
NONCE_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
|
||||||
|
|
||||||
|
time_range_map = {"day": "Day", "week": "Week", "month": "Month", "year": "Year"}
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
swisscows_regions: list[str] = [
|
||||||
|
"AR", "AU", "AT", "BE", "BR", "CA", "CL", "CN", "DK", "FI",
|
||||||
|
"FR", "DE", "HK", "HU", "IN", "ID", "IT", "JP", "KR", "LV",
|
||||||
|
"MY", "MX", "NL", "NZ", "NO", "PH", "PL", "PT", "RU", "SA",
|
||||||
|
"ZA", "ES", "SE", "CH", "TW", "TR", "UA", "GB", "US"
|
||||||
|
]
|
||||||
|
"""Regions supported by swisscows."""
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
# swisscows_languages = [
|
||||||
|
# "GB", "DE", "ES", "FR", "IT", "LV", "HU", "NL", "PT", "RU", "UA"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
|
||||||
|
def appropriate_locale(searxng_locale: str, regions: list[str], default: str) -> str:
|
||||||
|
"""Returns the appropriate swisscows locale for the region or language
|
||||||
|
selected by the user. If no value is determined, ``default`` is returned
|
||||||
|
"""
|
||||||
|
_locale = searxng_locale.split("-")
|
||||||
|
|
||||||
|
if _locale[0] == "all":
|
||||||
|
return default
|
||||||
|
|
||||||
|
if len(_locale) == 1 or _locale[1] in regions:
|
||||||
|
return searxng_locale
|
||||||
|
|
||||||
|
sxng_lang = _locale[0]
|
||||||
|
if sxng_lang.upper() in regions:
|
||||||
|
return f"{sxng_lang}-{sxng_lang.upper()}"
|
||||||
|
|
||||||
|
likely_subtag: str | None = get_global("likely_subtags").get(sxng_lang)
|
||||||
|
if likely_subtag:
|
||||||
|
_tag: list[str] = likely_subtag.split("_")
|
||||||
|
if _tag[-1] in regions:
|
||||||
|
return f"{_tag[0]}-{_tag[-1]}"
|
||||||
|
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def generate_nonce(length: int = 32) -> str:
|
||||||
|
"""
|
||||||
|
Generate a random char sequence with the given length.
|
||||||
|
"""
|
||||||
|
return "".join([random.choice(NONCE_ALPHABET) for _ in range(length)])
|
||||||
|
|
||||||
|
|
||||||
|
def caesar_shift_with_switch_case(s: str, offset: int = 13) -> str:
|
||||||
|
"""
|
||||||
|
Caesar shift by :py:obj:`offset` that additionally inverts the casing of all letters
|
||||||
|
(i.e. from lowercase to uppercase and vice versa).
|
||||||
|
"""
|
||||||
|
out = ""
|
||||||
|
for c in s:
|
||||||
|
if c.upper() in CAESAR_ALPHABET:
|
||||||
|
alphabet_index = ord(c.upper()) - ord("A")
|
||||||
|
shifted = CAESAR_ALPHABET[(alphabet_index + offset) % len(CAESAR_ALPHABET)]
|
||||||
|
case_switched = shifted.lower() if c.isupper() else shifted.upper()
|
||||||
|
out += case_switched
|
||||||
|
else:
|
||||||
|
out += c
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_hash_b64_url(s: str) -> str:
|
||||||
|
"""
|
||||||
|
Calculate the SHA256 hash and base64 URL-encodes it.
|
||||||
|
"""
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
hasher.update(s.encode())
|
||||||
|
hashed_bytes = hasher.digest()
|
||||||
|
|
||||||
|
# hashlib generates a byte digest, but since we need to convert it to base64, we
|
||||||
|
# need to do that by hand
|
||||||
|
hash_base64 = codecs.encode(hashed_bytes, "base64").decode("utf-8").rstrip('\n')
|
||||||
|
|
||||||
|
hash_base64_url_encoded = hash_base64.replace("=", "").replace("+", '-').replace("/", '_')
|
||||||
|
return hash_base64_url_encoded
|
||||||
|
|
||||||
|
|
||||||
|
def generate_nonce_and_signature(base_path: str, args: dict[str, t.Any]) -> tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Generate "X-Request-Nonce" and "X-Request-Signature" which are required for accessing
|
||||||
|
Swisscows images (reverse engineered from their official website).
|
||||||
|
"""
|
||||||
|
nonce = generate_nonce()
|
||||||
|
nonce_shifted = caesar_shift_with_switch_case(nonce, 13)
|
||||||
|
|
||||||
|
# in the path, all keys must be sorted in alphabetic order,
|
||||||
|
# otherwise the generated signature won't be accepted!
|
||||||
|
# additionally, the values may not be URL encoded, they have to be plain text
|
||||||
|
# hence we don't use urlencode here
|
||||||
|
args_sorted = sorted(args.items(), key=lambda arg: arg[0])
|
||||||
|
query_string = "&".join(f"{key}={value}" for (key, value) in args_sorted)
|
||||||
|
full_path = f"{base_path}?{query_string}"
|
||||||
|
|
||||||
|
signature = sha256_hash_b64_url(full_path + nonce_shifted)
|
||||||
|
return (nonce, signature)
|
||||||
|
|
||||||
|
|
||||||
|
maximum_page_size = {"web": 20, "images": 50, "videos": 10}
|
||||||
|
|
||||||
|
|
||||||
|
def init(_):
|
||||||
|
if swisscows_category not in ("web", "images", "videos"):
|
||||||
|
raise ValueError("illegal swisscows category: %s" % swisscows_category)
|
||||||
|
|
||||||
|
if results_per_page > maximum_page_size[swisscows_category]:
|
||||||
|
raise ValueError(
|
||||||
|
"results_per_page for swisscows %s can be at most %d"
|
||||||
|
% (swisscows_category, maximum_page_size[swisscows_category])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
# swisscows images only supports 2 pages
|
||||||
|
if swisscows_category == "images" and params["pageno"] > 2:
|
||||||
|
params["url"] = None
|
||||||
|
return
|
||||||
|
|
||||||
|
locale = appropriate_locale(params["searxng_locale"], swisscows_regions, "en-US")
|
||||||
|
base_path = ""
|
||||||
|
args = dict[str, t.Any]
|
||||||
|
if swisscows_category == "web":
|
||||||
|
freshness = "All"
|
||||||
|
if params["time_range"]:
|
||||||
|
freshness = time_range_map[params["time_range"]]
|
||||||
|
args = {
|
||||||
|
"freshness": freshness,
|
||||||
|
"itemsCount": results_per_page,
|
||||||
|
"locale": locale,
|
||||||
|
"offset": (params["pageno"] - 1) * results_per_page,
|
||||||
|
"query": query,
|
||||||
|
"spellcheck": True,
|
||||||
|
}
|
||||||
|
base_path = "/v5/web/search"
|
||||||
|
elif swisscows_category == "images":
|
||||||
|
args = {
|
||||||
|
"itemsCount": results_per_page,
|
||||||
|
"locale": locale,
|
||||||
|
"offset": (params["pageno"] - 1) * results_per_page,
|
||||||
|
"query": query,
|
||||||
|
"spellcheck": True,
|
||||||
|
}
|
||||||
|
base_path = "/v5/images/search"
|
||||||
|
else:
|
||||||
|
args = {
|
||||||
|
"itemsCount": results_per_page,
|
||||||
|
"offset": (params["pageno"] - 1) * results_per_page,
|
||||||
|
"query": query,
|
||||||
|
"region": locale,
|
||||||
|
"spellcheck": True,
|
||||||
|
}
|
||||||
|
base_path = "/v2/videos/search"
|
||||||
|
|
||||||
|
nonce, signature = generate_nonce_and_signature(base_path, args)
|
||||||
|
|
||||||
|
params["headers"].update(
|
||||||
|
{
|
||||||
|
"X-Request-Nonce": nonce,
|
||||||
|
"X-Request-Signature": signature,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
params["url"] = f"{base_url}{base_path}?{urlencode(args)}"
|
||||||
|
|
||||||
|
|
||||||
|
def _video_result(result: dict[str, str]) -> LegacyResult:
|
||||||
|
published_date = None
|
||||||
|
if result.get("datePublished"):
|
||||||
|
published_date = datetime.fromisoformat(result["datePublished"])
|
||||||
|
|
||||||
|
view_count = None
|
||||||
|
if result.get("viewCount"):
|
||||||
|
view_count = humanize_number(result["viewCount"]) # pyright: ignore[reportArgumentType]
|
||||||
|
|
||||||
|
return LegacyResult(
|
||||||
|
{
|
||||||
|
"template": "videos.html",
|
||||||
|
"url": result["url"],
|
||||||
|
"title": html_to_text(result.get("title") or result["name"]),
|
||||||
|
"content": result["description"],
|
||||||
|
"thumbnail": result.get("thumbnailUrl")
|
||||||
|
or result.get("thumbnail", {}).get("url"), # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
"length": result.get("duration"),
|
||||||
|
"iframe_src": result.get("embedUrl"),
|
||||||
|
"publishedDate": published_date,
|
||||||
|
"views": view_count,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
json_data = resp.json()
|
||||||
|
|
||||||
|
# the payload encoding is only used for general and images,
|
||||||
|
# for videos the data gets returned directly as a normal JSON response
|
||||||
|
# payload is encoded as a JSON web token -> 3 parts, separated by "."
|
||||||
|
# the actual data is in the center of the encoded string
|
||||||
|
if "payload" in json_data:
|
||||||
|
payload = json_data["payload"].split(".")[1]
|
||||||
|
# pad with '=' to be valid base64
|
||||||
|
payload = payload + '=' * (4 - len(payload) % 4)
|
||||||
|
decoded = base64.urlsafe_b64decode(payload)
|
||||||
|
json_data = json.loads(decoded.decode())
|
||||||
|
|
||||||
|
result: dict[str, t.Any]
|
||||||
|
for result in json_data["items"]:
|
||||||
|
if result["type"] == "WebPage":
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["url"],
|
||||||
|
title=result["name"],
|
||||||
|
content=html_to_text(result["description"]),
|
||||||
|
thumbnail=result.get("thumbnail", {}).get("url"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif swisscows_category == "videos" and result["type"] == "VideoCollection":
|
||||||
|
for video in result["hasPart"]:
|
||||||
|
res.add(_video_result(video))
|
||||||
|
elif result["type"] == "ImageObject":
|
||||||
|
res.add(
|
||||||
|
res.types.LegacyResult(
|
||||||
|
{
|
||||||
|
"template": "images.html",
|
||||||
|
"url": result["url"],
|
||||||
|
"thumbnail_src": result["thumbnail"]["url"],
|
||||||
|
"img_src": result["contentUrl"],
|
||||||
|
"title": result["name"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif result["type"] == "video":
|
||||||
|
res.add(_video_result(result))
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
"""Swisscows news"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from searx.utils import html_to_text
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
from searx.engines.swisscows import appropriate_locale
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://swisscows.com",
|
||||||
|
"wikidata_id": "Q22937452",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "JSON",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
categories = ["news"]
|
||||||
|
results_per_page = 20
|
||||||
|
|
||||||
|
time_range_support = True
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
base_url = "https://api.swisscows.com"
|
||||||
|
time_range_map = {"day": "Day", "week": "Week", "month": "Month", "year": "Year"}
|
||||||
|
|
||||||
|
swisscows_regions: list[str] = ["DE"]
|
||||||
|
"""Regions supported by swisscows News."""
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams") -> None:
|
||||||
|
|
||||||
|
sxng_locale = params["searxng_locale"].split("-", maxsplit=1)[0]
|
||||||
|
locale: str = appropriate_locale(sxng_locale, swisscows_regions, default="de-DE")
|
||||||
|
if not locale:
|
||||||
|
return
|
||||||
|
|
||||||
|
freshness = "All"
|
||||||
|
if params["time_range"]:
|
||||||
|
freshness = time_range_map[params["time_range"]]
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"query": query,
|
||||||
|
"itemsCount": results_per_page,
|
||||||
|
"region": locale,
|
||||||
|
"language": locale.split("-", maxsplit=1)[0],
|
||||||
|
"offset": (params["pageno"] - 1) * results_per_page,
|
||||||
|
"freshness": freshness,
|
||||||
|
"sortOrder": "Desc",
|
||||||
|
"sortBy": "Created",
|
||||||
|
}
|
||||||
|
url_path = f"/news/search?{urlencode(args)}"
|
||||||
|
|
||||||
|
params["url"] = base_url + url_path
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
res = EngineResults()
|
||||||
|
|
||||||
|
result: dict[str, str]
|
||||||
|
for result in resp.json()["items"]: # pyright: ignore[reportAny]
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=result["uri"],
|
||||||
|
title=html_to_text(result["title"]),
|
||||||
|
content=result["description"],
|
||||||
|
publishedDate=datetime.fromisoformat(result["created"]),
|
||||||
|
thumbnail=result.get("og:image") or "",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""Tiger_ is a Swiss meta search engine.
|
||||||
|
|
||||||
|
.. _Tiger: https://tiger.ch
|
||||||
|
"""
|
||||||
|
|
||||||
|
from json import loads
|
||||||
|
import random
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
from dateutil import parser
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
from searx.exceptions import SearxEngineAPIException
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.network import get, post
|
||||||
|
from searx.result_types import EngineResults
|
||||||
|
from searx.utils import extr, eval_xpath_list, eval_xpath, extract_text
|
||||||
|
from searx.enginelib import EngineCache
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.search.processors import OnlineParams
|
||||||
|
|
||||||
|
about = {
|
||||||
|
"website": "https://tiger.ch",
|
||||||
|
"official_api_documentation": None,
|
||||||
|
"use_official_api": False,
|
||||||
|
"require_api_key": False,
|
||||||
|
"results": "HTML",
|
||||||
|
}
|
||||||
|
|
||||||
|
paging = True
|
||||||
|
|
||||||
|
base_url = "https://tiger.ch"
|
||||||
|
categories = []
|
||||||
|
tiger_category = "Websuche"
|
||||||
|
"""
|
||||||
|
Possible values: "Websuche", "News".
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
CACHE: EngineCache
|
||||||
|
"""Cache to store session codes (result of solved CAPTCHA)."""
|
||||||
|
|
||||||
|
|
||||||
|
def init(_):
|
||||||
|
if tiger_category not in ("Websuche", "News"):
|
||||||
|
raise ValueError("invalid search category: %s" % tiger_category)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(engine_settings: dict[str, t.Any]) -> bool:
|
||||||
|
global CACHE # pylint: disable=global-statement
|
||||||
|
CACHE = EngineCache(engine_settings["name"])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _obtain_session_code() -> str:
|
||||||
|
"""The challenge works like this:
|
||||||
|
|
||||||
|
- We first generate 3 random numbers.
|
||||||
|
- Then we send them to /Human.svc/Make to get the operands (+, -) for the
|
||||||
|
math challenge (i.e. a simple calculation)
|
||||||
|
- Based on the operands, we calculate a result (usually done by the user by
|
||||||
|
hand)
|
||||||
|
- We send the result of the math calculation to the server to obtain a
|
||||||
|
session "code" that has to be sent as cookie parameter for all searches
|
||||||
|
|
||||||
|
E.g., challenges look like ``19-3+5``.
|
||||||
|
"""
|
||||||
|
cached_session = CACHE.get("session")
|
||||||
|
if cached_session:
|
||||||
|
return cached_session
|
||||||
|
|
||||||
|
results_page = get(f"{base_url}/_internCode.aspx")
|
||||||
|
doc = html.fromstring(results_page.text)
|
||||||
|
|
||||||
|
extra_data: dict[str, str] = {}
|
||||||
|
for extra_param in ("__VIEWSTATE", "__VIEWSTATEGENERATOR", "__EVENTVALIDATION"):
|
||||||
|
extra_data[extra_param] = doc.xpath(f"//input[@name='{extra_param}']/@value")[0]
|
||||||
|
|
||||||
|
# var z1 = Math.floor((Math.random() * 8) + 11);
|
||||||
|
# var z2 = Math.floor((Math.random() * 8) + 1);
|
||||||
|
# var z3 = Math.floor((Math.random() * 8) + 1);
|
||||||
|
num1 = random.randint(11, 19)
|
||||||
|
num2 = random.randint(1, 9)
|
||||||
|
num3 = random.randint(1, 9)
|
||||||
|
|
||||||
|
challenge = get(f"{base_url}/Services/Human.svc/Make?M1={num1}&M2={num2}&M3={num3}", cookies=results_page.cookies)
|
||||||
|
signs = loads(challenge.json()["d"])[0]
|
||||||
|
sign1 = signs["Z1"]
|
||||||
|
sign2 = signs["Z2"]
|
||||||
|
|
||||||
|
result = num1
|
||||||
|
for num, sign in [(num2, sign1), (num3, sign2)]:
|
||||||
|
if sign == "+":
|
||||||
|
result += num
|
||||||
|
else:
|
||||||
|
result -= num
|
||||||
|
|
||||||
|
logger.debug(f"got challenge: {num1} {sign1} {num2} {sign2} {num3} = {result}")
|
||||||
|
data = {
|
||||||
|
**extra_data,
|
||||||
|
"txtM": str(result),
|
||||||
|
"btnHuman": "OK",
|
||||||
|
}
|
||||||
|
|
||||||
|
challenge_response = post(
|
||||||
|
f"{base_url}/_internCode.aspx",
|
||||||
|
cookies=results_page.cookies,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
|
||||||
|
cookie = challenge_response.cookies["Tiger.ch"]
|
||||||
|
code = extr(cookie, "Code=", "&")
|
||||||
|
if not code:
|
||||||
|
raise SearxEngineAPIException("failed to obtain session code")
|
||||||
|
|
||||||
|
CACHE.set("session", code, expire=60 * 24 * 60) # cookie is valid for two months
|
||||||
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
def request(query: str, params: "OnlineParams"):
|
||||||
|
code = _obtain_session_code()
|
||||||
|
args = {"w": query, "page": params["pageno"]}
|
||||||
|
params["url"] = f"{base_url}/{tiger_category}?{urlencode(args)}"
|
||||||
|
params["cookies"]["Tiger.ch"] = f"Code={code}"
|
||||||
|
|
||||||
|
|
||||||
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
res = EngineResults()
|
||||||
|
doc = html.fromstring(resp.text)
|
||||||
|
|
||||||
|
if tiger_category == "Websuche":
|
||||||
|
for result in eval_xpath_list(doc, "//div[@id='mainContainer']//table/tr"):
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=extract_text(eval_xpath(result, ".//a[contains(@class, 'weblink')]/@href")),
|
||||||
|
title=extract_text(eval_xpath(result, ".//a[contains(@class, 'weblink')]")) or "",
|
||||||
|
content=extract_text(eval_xpath(result, ".//*[contains(@class, 'webbodynopic')]")) or "",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif tiger_category == "News":
|
||||||
|
for result in eval_xpath_list(doc, "//div[@id='panNews']/div"):
|
||||||
|
publishedDate = None
|
||||||
|
try:
|
||||||
|
date_str = extract_text(eval_xpath(result, ".//span[contains(@class, 'help')]/span")) or ""
|
||||||
|
date_str = date_str.strip().removeprefix("-").strip()
|
||||||
|
publishedDate = parser.parse(date_str)
|
||||||
|
except parser.ParserError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
thumbnail = extract_text(eval_xpath(result, "./img/@src"))
|
||||||
|
if thumbnail:
|
||||||
|
thumbnail = base_url + thumbnail
|
||||||
|
|
||||||
|
res.add(
|
||||||
|
res.types.MainResult(
|
||||||
|
url=extract_text(eval_xpath(result, ".//a[contains(@class, 'webLink')]/@href")),
|
||||||
|
title=extract_text(eval_xpath(result, ".//a[contains(@class, 'webLink')]")) or "",
|
||||||
|
thumbnail=thumbnail or "",
|
||||||
|
publishedDate=publishedDate,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
@@ -211,8 +211,4 @@ def response(resp) -> EngineResults:
|
|||||||
|
|
||||||
# append number of results
|
# append number of results
|
||||||
|
|
||||||
number_of_results = json_data.get('num_matches')
|
|
||||||
if number_of_results:
|
|
||||||
results.append({'number_of_results': number_of_results})
|
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
+35
-18
@@ -1,56 +1,73 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
"""MyMemory Translated"""
|
"""MyMemory Translated"""
|
||||||
|
|
||||||
|
import typing as t
|
||||||
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
|
from searx.utils import html_to_text
|
||||||
from searx.result_types import EngineResults
|
from searx.result_types import EngineResults
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from searx.extended_types import SXNG_Response
|
||||||
|
from searx.search.processors import OnlineDictParams
|
||||||
|
#
|
||||||
# about
|
# about
|
||||||
about = {
|
about = {
|
||||||
"website": 'https://mymemory.translated.net/',
|
"website": "https://mymemory.translated.net/",
|
||||||
"wikidata_id": None,
|
"wikidata_id": None,
|
||||||
"official_api_documentation": 'https://mymemory.translated.net/doc/spec.php',
|
"official_api_documentation": "https://mymemory.translated.net/doc/spec.php",
|
||||||
"use_official_api": True,
|
"use_official_api": True,
|
||||||
"require_api_key": False,
|
"require_api_key": False,
|
||||||
"results": 'JSON',
|
"results": "JSON",
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_type = 'online_dictionary'
|
engine_type = "online_dictionary"
|
||||||
categories = ['general', 'translate']
|
categories = ["general", "translate"]
|
||||||
api_url = "https://api.mymemory.translated.net"
|
api_url = "https://api.mymemory.translated.net"
|
||||||
web_url = "https://mymemory.translated.net"
|
web_url = "https://mymemory.translated.net"
|
||||||
weight = 100
|
weight = 100
|
||||||
|
|
||||||
api_key = ''
|
api_key = ""
|
||||||
|
|
||||||
|
|
||||||
def request(query, params): # pylint: disable=unused-argument
|
def request(_: str, params: "OnlineDictParams") -> None:
|
||||||
|
|
||||||
args = {"q": params["query"], "langpair": f"{params['from_lang'][1]}|{params['to_lang'][1]}"}
|
args = {
|
||||||
|
"q": params["query"],
|
||||||
|
"langpair": f"{params['from_lang'][1]}|{params['to_lang'][1]}",
|
||||||
|
}
|
||||||
if api_key:
|
if api_key:
|
||||||
args["key"] = api_key
|
args["key"] = api_key
|
||||||
|
|
||||||
params['url'] = f"{api_url}/get?{urllib.parse.urlencode(args)}"
|
params['url'] = f"{api_url}/get?{urllib.parse.urlencode(args)}"
|
||||||
return params
|
|
||||||
|
|
||||||
|
|
||||||
def response(resp) -> EngineResults:
|
def response(resp: "SXNG_Response") -> EngineResults:
|
||||||
|
|
||||||
results = EngineResults()
|
results = EngineResults()
|
||||||
data = resp.json()
|
data: dict[str, t.Any] = resp.json()
|
||||||
|
params: "OnlineDictParams" = resp.search_params # pyright: ignore[reportAssignmentType]
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
"q": resp.search_params["query"],
|
"q": params["query"],
|
||||||
"lang": resp.search_params.get("searxng_locale", "en"), # ui language
|
"lang": params.get("searxng_locale", "en"), # ui language
|
||||||
"sl": resp.search_params['from_lang'][1],
|
"sl": params["from_lang"][1],
|
||||||
"tl": resp.search_params['to_lang'][1],
|
"tl": params["to_lang"][1],
|
||||||
}
|
}
|
||||||
|
|
||||||
link = f"{web_url}/search.php?{urllib.parse.urlencode(args)}"
|
link = f"{web_url}/search.php?{urllib.parse.urlencode(args)}"
|
||||||
text = data['responseData']['translatedText']
|
text: str = html_to_text(data["responseData"]["translatedText"])
|
||||||
|
|
||||||
examples = [f"{m['segment']} : {m['translation']}" for m in data['matches'] if m['translation'] != text]
|
examples: set[str] = set()
|
||||||
|
match: dict[str, str]
|
||||||
|
for match in data["matches"]:
|
||||||
|
_text = html_to_text(match["translation"])
|
||||||
|
if _text != text:
|
||||||
|
_seg = html_to_text(match["segment"])
|
||||||
|
examples.add(f"{_seg} : {_text}")
|
||||||
|
|
||||||
item = results.types.Translations.Item(text=text, examples=examples)
|
item = results.types.Translations.Item(text=text, examples=list(examples))
|
||||||
results.add(results.types.Translations(translations=[item], url=link))
|
results.add(results.types.Translations(translations=[item], url=link))
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ about = {
|
|||||||
|
|
||||||
categories: list[str] = []
|
categories: list[str] = []
|
||||||
paging = True
|
paging = True
|
||||||
number_of_results = 10
|
page_size = 10
|
||||||
|
|
||||||
wc_api_url = "https://commons.wikimedia.org/w/api.php"
|
wc_api_url = "https://commons.wikimedia.org/w/api.php"
|
||||||
wc_search_type: str = ""
|
wc_search_type: str = ""
|
||||||
@@ -107,8 +107,8 @@ def request(query: str, params: "OnlineParams") -> None:
|
|||||||
"generator": "search",
|
"generator": "search",
|
||||||
"gsrnamespace": "6", # https://www.mediawiki.org/wiki/Help:Namespaces#Renaming_namespaces
|
"gsrnamespace": "6", # https://www.mediawiki.org/wiki/Help:Namespaces#Renaming_namespaces
|
||||||
"gsrprop": "snippet",
|
"gsrprop": "snippet",
|
||||||
"gsrlimit": number_of_results,
|
"gsrlimit": page_size,
|
||||||
"gsroffset": number_of_results * (params["pageno"] - 1),
|
"gsroffset": page_size * (params["pageno"] - 1),
|
||||||
"gsrsearch": f"filetype:{filetype} {query}",
|
"gsrsearch": f"filetype:{filetype} {query}",
|
||||||
# imageinfo: https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bimageinfo
|
# imageinfo: https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bimageinfo
|
||||||
"iiprop": "url|size|mime",
|
"iiprop": "url|size|mime",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user