1 Commits

Author SHA1 Message Date
searxng-bot
6cc4350a34 [l10n] update translations from Weblate
f290f4ffe - 2025-04-15 - return42 <return42@noreply.codeberg.org>
4a9134b03 - 2025-04-14 - DZDevelopers <dzdevelopers@noreply.codeberg.org>
a8c86581d - 2025-04-14 - aindriu80 <aindriu80@noreply.codeberg.org>
2b5395719 - 2025-04-11 - ayame30 <ayame30@noreply.codeberg.org>
448a443fa - 2025-04-11 - ayame30 <ayame30@noreply.codeberg.org>
2025-04-18 07:10:33 +00:00
29 changed files with 394 additions and 768 deletions

View File

@@ -31,8 +31,8 @@
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"swiped-events": "^1.2.0", "swiped-events": "^1.2.0",
"vite": "^6.3.2", "vite": "^6.2.6",
"vite-plugin-static-copy": "^2.3.1", "vite-plugin-static-copy": "^2.3.0",
"vite-plugin-stylelint": "^6.0.0", "vite-plugin-stylelint": "^6.0.0",
"webpack": "^5.99.5", "webpack": "^5.99.5",
"webpack-cli": "^6.0.1" "webpack-cli": "^6.0.1"
@@ -1486,9 +1486,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz",
"integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", "integrity": "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1500,9 +1500,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz",
"integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", "integrity": "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1514,9 +1514,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz",
"integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", "integrity": "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1528,9 +1528,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz",
"integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", "integrity": "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1542,9 +1542,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": { "node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz",
"integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", "integrity": "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1556,9 +1556,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-x64": { "node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz",
"integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", "integrity": "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1570,9 +1570,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz",
"integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", "integrity": "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1584,9 +1584,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz",
"integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", "integrity": "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1598,9 +1598,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz",
"integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", "integrity": "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1612,9 +1612,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz",
"integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", "integrity": "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1626,9 +1626,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-loongarch64-gnu": { "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz",
"integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", "integrity": "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@@ -1640,9 +1640,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz",
"integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", "integrity": "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -1654,23 +1654,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz",
"integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", "integrity": "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.40.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz",
"integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -1682,9 +1668,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz",
"integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", "integrity": "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -1696,9 +1682,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz",
"integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", "integrity": "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1710,9 +1696,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz",
"integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", "integrity": "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1724,9 +1710,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz",
"integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", "integrity": "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1738,9 +1724,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz",
"integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", "integrity": "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -1752,9 +1738,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz",
"integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", "integrity": "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1829,9 +1815,9 @@
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.7", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -5637,13 +5623,13 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.40.0", "version": "4.34.7",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz",
"integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", "integrity": "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/estree": "1.0.7" "@types/estree": "1.0.6"
}, },
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
@@ -5653,26 +5639,25 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm-eabi": "4.34.7",
"@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-android-arm64": "4.34.7",
"@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.34.7",
"@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-darwin-x64": "4.34.7",
"@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.34.7",
"@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.34.7",
"@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.34.7",
"@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.34.7",
"@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.34.7",
"@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.34.7",
"@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.34.7",
"@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7",
"@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.34.7",
"@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.34.7",
"@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.34.7",
"@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.34.7",
"@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.34.7",
"@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.34.7",
"@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.34.7",
"@rollup/rollup-win32-x64-msvc": "4.40.0",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@@ -6807,18 +6792,15 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.3.2", "version": "6.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
"integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.4.3",
"picomatch": "^4.0.2",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"rollup": "^4.34.9", "rollup": "^4.30.1"
"tinyglobby": "^0.2.12"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
@@ -6882,9 +6864,9 @@
} }
}, },
"node_modules/vite-plugin-static-copy": { "node_modules/vite-plugin-static-copy": {
"version": "2.3.1", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.3.1.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.3.0.tgz",
"integrity": "sha512-EfsPcBm3ewg3UMG8RJaC0ADq6/qnUZnokXx4By4+2cAcipjT9i0Y0owIJGqmZI7d6nxk4qB1q5aXOwNuSyPdyA==", "integrity": "sha512-LLKwhhHetGaCnWz4mas4qqjjguDka6/6b4+SeIohRroj8aCE7QTfiZECfPecslFQkWZ3HdQuq5kOPmWZjNYlKA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -6961,34 +6943,6 @@
} }
} }
}, },
"node_modules/vite/node_modules/fdir": {
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/watchpack": { "node_modules/watchpack": {
"version": "2.4.2", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",

View File

@@ -29,8 +29,8 @@
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"swiped-events": "^1.2.0", "swiped-events": "^1.2.0",
"vite": "^6.3.2", "vite": "^6.2.6",
"vite-plugin-static-copy": "^2.3.1", "vite-plugin-static-copy": "^2.3.0",
"vite-plugin-stylelint": "^6.0.0", "vite-plugin-stylelint": "^6.0.0",
"webpack": "^5.99.5", "webpack": "^5.99.5",
"webpack-cli": "^6.0.1" "webpack-cli": "^6.0.1"

View File

@@ -16,14 +16,8 @@
open_metrics: '' open_metrics: ''
``debug`` : ``$SEARXNG_DEBUG`` ``debug`` : ``$SEARXNG_DEBUG``
In debug mode, the server provides an interactive debugger, will reload when Allow a more detailed log if you run SearXNG directly. Display *detailed* error
code is changed and activates a verbose logging. messages in the browser too, so this must be deactivated in production.
.. attention::
The debug setting is intended for local development server. Don't
activate debug (don't use a development server) when deploying to
production.
``donation_url`` : ``donation_url`` :
Set value to ``true`` to use your own donation page written in the Set value to ``true`` to use your own donation page written in the

View File

@@ -14,7 +14,6 @@
limiter: false limiter: false
public_instance: false public_instance: false
image_proxy: false image_proxy: false
method: "POST"
default_http_headers: default_http_headers:
X-Content-Type-Options : nosniff X-Content-Type-Options : nosniff
X-Download-Options : noopen X-Download-Options : noopen
@@ -51,11 +50,6 @@
``image_proxy`` : ``$SEARXNG_IMAGE_PROXY`` ``image_proxy`` : ``$SEARXNG_IMAGE_PROXY``
Allow your instance of SearXNG of being able to proxy images. Uses memory space. Allow your instance of SearXNG of being able to proxy images. Uses memory space.
.. _method:
``method`` : ``$SEARXNG_METHOD``
Whether to use ``GET`` or ``POST`` HTTP method when searching.
.. _HTTP headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers .. _HTTP headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
``default_http_headers`` : ``default_http_headers`` :

View File

@@ -4,5 +4,10 @@
Mullvad-Leta Mullvad-Leta
============ ============
.. contents:: Contents
:depth: 2
:local:
:backlinks: entry
.. automodule:: searx.engines.mullvad_leta .. automodule:: searx.engines.mullvad_leta
:members: :members:

View File

@@ -22,18 +22,18 @@ searx_dir = abspath(dirname(__file__))
searx_parent_dir = abspath(dirname(dirname(__file__))) searx_parent_dir = abspath(dirname(dirname(__file__)))
settings = {} settings = {}
sxng_debug = False searx_debug = False
logger = logging.getLogger('searx') logger = logging.getLogger('searx')
_unset = object() _unset = object()
def init_settings(): def init_settings():
"""Initialize global ``settings`` and ``sxng_debug`` variables and """Initialize global ``settings`` and ``searx_debug`` variables and
``logger`` from ``SEARXNG_SETTINGS_PATH``. ``logger`` from ``SEARXNG_SETTINGS_PATH``.
""" """
global settings, sxng_debug # pylint: disable=global-variable-not-assigned global settings, searx_debug # pylint: disable=global-variable-not-assigned
cfg, msg = searx.settings_loader.load_settings(load_user_settings=True) cfg, msg = searx.settings_loader.load_settings(load_user_settings=True)
cfg = cfg or {} cfg = cfg or {}
@@ -42,8 +42,8 @@ def init_settings():
settings.clear() settings.clear()
settings.update(cfg) settings.update(cfg)
sxng_debug = get_setting("general.debug") searx_debug = settings['general']['debug']
if sxng_debug: if searx_debug:
_logging_config_debug() _logging_config_debug()
else: else:
logging.basicConfig(level=LOG_LEVEL_PROD, format=LOG_FORMAT_PROD) logging.basicConfig(level=LOG_LEVEL_PROD, format=LOG_FORMAT_PROD)

View File

@@ -6628,172 +6628,6 @@
"to-TO": "to" "to-TO": "to"
} }
}, },
"mullvadleta": {
"all_locale": null,
"custom": {},
"data_type": "traits_v1",
"languages": {
"ar": "ar",
"bg": "bg",
"ca": "ca",
"cs": "cs",
"da": "da",
"de": "de",
"en": "en",
"es": "es",
"et": "et",
"fi": "fi",
"fr": "fr",
"he": "he",
"hr": "hr",
"hu": "hu",
"is": "is",
"it": "it",
"ja": "jp",
"ko": "ko",
"lt": "lt",
"lv": "lv",
"nb": "nb",
"nl": "nl",
"pl": "pl",
"pt": "pt",
"ro": "ro",
"ru": "ru",
"sk": "sk",
"sl": "sl",
"sr": "sr",
"sv": "sv",
"tr": "tr",
"zh_Hans": "zh-hant",
"zh_Hant": "zh-hans"
},
"regions": {
"ar-SA": "sa",
"da-DK": "dk",
"de-AT": "at",
"de-BE": "be",
"de-CH": "ch",
"de-DE": "de",
"en-AU": "au",
"en-CA": "ca",
"en-GB": "uk",
"en-IN": "in",
"en-NZ": "nz",
"en-PH": "ph",
"en-US": "us",
"en-ZA": "za",
"es-AR": "ar",
"es-CL": "cl",
"es-ES": "es",
"es-MX": "mx",
"fi-FI": "fi",
"fr-BE": "be",
"fr-CA": "ca",
"fr-CH": "ch",
"fr-FR": "fr",
"id-ID": "id",
"it-CH": "ch",
"it-IT": "it",
"ja-JP": "jp",
"ko-KR": "kr",
"ms-MY": "my",
"nb-NO": "no",
"nl-BE": "be",
"nl-NL": "nl",
"pl-PL": "pl",
"pt-BR": "br",
"pt-PT": "pt",
"ru-RU": "ru",
"se-SE": "se",
"tr-TR": "tr",
"zh-CN": "cn",
"zh-HK": "hk",
"zh-TW": "tw"
}
},
"mullvadleta brave": {
"all_locale": null,
"custom": {},
"data_type": "traits_v1",
"languages": {
"ar": "ar",
"bg": "bg",
"ca": "ca",
"cs": "cs",
"da": "da",
"de": "de",
"en": "en",
"es": "es",
"et": "et",
"fi": "fi",
"fr": "fr",
"he": "he",
"hr": "hr",
"hu": "hu",
"is": "is",
"it": "it",
"ja": "jp",
"ko": "ko",
"lt": "lt",
"lv": "lv",
"nb": "nb",
"nl": "nl",
"pl": "pl",
"pt": "pt",
"ro": "ro",
"ru": "ru",
"sk": "sk",
"sl": "sl",
"sr": "sr",
"sv": "sv",
"tr": "tr",
"zh_Hans": "zh-hant",
"zh_Hant": "zh-hans"
},
"regions": {
"ar-SA": "sa",
"da-DK": "dk",
"de-AT": "at",
"de-BE": "be",
"de-CH": "ch",
"de-DE": "de",
"en-AU": "au",
"en-CA": "ca",
"en-GB": "uk",
"en-IN": "in",
"en-NZ": "nz",
"en-PH": "ph",
"en-US": "us",
"en-ZA": "za",
"es-AR": "ar",
"es-CL": "cl",
"es-ES": "es",
"es-MX": "mx",
"fi-FI": "fi",
"fr-BE": "be",
"fr-CA": "ca",
"fr-CH": "ch",
"fr-FR": "fr",
"id-ID": "id",
"it-CH": "ch",
"it-IT": "it",
"ja-JP": "jp",
"ko-KR": "kr",
"ms-MY": "my",
"nb-NO": "no",
"nl-BE": "be",
"nl-NL": "nl",
"pl-PL": "pl",
"pt-BR": "br",
"pt-PT": "pt",
"ru-RU": "ru",
"se-SE": "se",
"tr-TR": "tr",
"zh-CN": "cn",
"zh-HK": "hk",
"zh-TW": "tw"
}
},
"odysee": { "odysee": {
"all_locale": null, "all_locale": null,
"custom": {}, "custom": {},

View File

@@ -1,61 +1,46 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Mullvad Leta is a search engine proxy. Currently Leta only offers text
search results not image, news or any other types of search result. Leta acts """This is the implementation of the Mullvad-Leta meta-search engine.
as a proxy to Google and Brave search results. You can select which backend
search engine you wish to use, see (:py:obj:`leta_engine`). This engine **REQUIRES** that searxng operate within a Mullvad VPN
If using docker, consider using gluetun for easily connecting to the Mullvad
- https://github.com/qdm12/gluetun
Otherwise, follow instructions provided by Mullvad for enabling the VPN on Linux
- https://mullvad.net/en/help/install-mullvad-app-linux
.. hint:: .. hint::
Leta caches each search for up to 30 days. For example, if you use search The :py:obj:`EngineTraits` is empty by default. Maintainers have to run
terms like ``news``, contrary to your intention you'll get very old results! ``make data.traits`` (in the Mullvad VPN / :py:obj:`fetch_traits`) and rebase
the modified JSON file ``searx/data/engine_traits.json`` on every single
update of SearXNG!
Configuration
=============
The engine has the following additional settings:
- :py:obj:`leta_engine` (:py:obj:`LetaEnginesType`)
You can configure one Leta engine for Google and one for Brave:
.. code:: yaml
- name: mullvadleta
engine: mullvad_leta
leta_engine: google
shortcut: ml
- name: mullvadleta brave
engine: mullvad_leta
network: mullvadleta # use network from engine "mullvadleta" configured above
leta_engine: brave
shortcut: mlb
Implementations
===============
""" """
from __future__ import annotations from __future__ import annotations
import typing from typing import TYPE_CHECKING
from urllib.parse import urlencode
import babel
from httpx import Response from httpx import Response
from lxml import html from lxml import html
from searx.enginelib.traits import EngineTraits from searx.enginelib.traits import EngineTraits
from searx.locales import get_official_locales, language_tag, region_tag from searx.locales import region_tag, get_official_locales
from searx.utils import eval_xpath_list from searx.utils import eval_xpath, extract_text, eval_xpath_list
from searx.result_types import EngineResults, MainResult from searx.exceptions import SearxEngineResponseException
if typing.TYPE_CHECKING: if TYPE_CHECKING:
import logging import logging
logger = logging.getLogger() logger = logging.getLogger()
traits: EngineTraits traits: EngineTraits
use_cache: bool = True # non-cache use only has 100 searches per day!
leta_engine: str = 'google'
search_url = "https://leta.mullvad.net" search_url = "https://leta.mullvad.net"
# about # about
@@ -69,205 +54,154 @@ about = {
} }
# engine dependent config # engine dependent config
categories = ["general", "web"] categories = ['general', 'web']
paging = True paging = True
max_page = 10 max_page = 50
time_range_support = True time_range_support = True
time_range_dict = { time_range_dict = {
"day": "d", "day": "d1",
"week": "w", "week": "w1",
"month": "m", "month": "m1",
"year": "y", "year": "y1",
} }
LetaEnginesType = typing.Literal["google", "brave"] available_leta_engines = [
"""Engine types supported by mullvadleta.""" 'google', # first will be default if provided engine is invalid
'brave',
leta_engine: LetaEnginesType = "google" ]
"""Select Leta's engine type from :py:obj:`LetaEnginesType`."""
def init(_): def is_vpn_connected(dom: html.HtmlElement) -> bool:
l = typing.get_args(LetaEnginesType) """Returns true if the VPN is connected, False otherwise"""
if leta_engine not in l: connected_text = extract_text(eval_xpath(dom, '//main/div/p[1]'))
raise ValueError(f"leta_engine '{leta_engine}' is invalid, use one of {', '.join(l)}") return connected_text != 'You are not connected to Mullvad VPN.'
class DataNodeQueryMetaDataIndices(typing.TypedDict): def assign_headers(headers: dict) -> dict:
"""Indices into query metadata.""" """Assigns the headers to make a request to Mullvad Leta"""
headers['Accept'] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
success: int headers['Content-Type'] = "application/x-www-form-urlencoded"
q: int # pylint: disable=invalid-name headers['Host'] = "leta.mullvad.net"
country: int headers['Origin'] = "https://leta.mullvad.net"
language: int return headers
lastUpdated: int
engine: int
items: int
infobox: int
news: int
timestamp: int
altered: int
page: int
next: int # if -1, there no more results are available
previous: int
class DataNodeResultIndices(typing.TypedDict):
"""Indices into query resultsdata."""
link: int
snippet: int
title: int
favicon: int
def request(query: str, params: dict): def request(query: str, params: dict):
params["method"] = "GET" country = traits.get_region(params.get('searxng_locale', 'all'), traits.all_locale) # type: ignore
args = {
result_engine = leta_engine
if leta_engine not in available_leta_engines:
result_engine = available_leta_engines[0]
logger.warning(
'Configured engine "%s" not one of the available engines %s, defaulting to "%s"',
leta_engine,
available_leta_engines,
result_engine,
)
params['url'] = search_url
params['method'] = 'POST'
params['data'] = {
"q": query, "q": query,
"engine": leta_engine, "gl": country if country is str else '',
"x-sveltekit-invalidated": "001", # hardcoded from all requests seen 'engine': result_engine,
} }
# pylint: disable=undefined-variable
if use_cache:
params['data']['oc'] = "on"
# pylint: enable=undefined-variable
country = traits.get_region(params.get("searxng_locale"), traits.all_locale) # type: ignore if params['time_range'] in time_range_dict:
if country: params['dateRestrict'] = time_range_dict[params['time_range']]
args["country"] = country else:
params['dateRestrict'] = ''
language = traits.get_language(params.get("searxng_locale"), traits.all_locale) # type: ignore if params['pageno'] > 1:
if language: # Page 1 is n/a, Page 2 is 11, page 3 is 21, ...
args["language"] = language params['data']['start'] = ''.join([str(params['pageno'] - 1), "1"])
if params["time_range"] in time_range_dict: if params['headers'] is None:
args["lastUpdated"] = time_range_dict[params["time_range"]] params['headers'] = {}
if params["pageno"] > 1:
args["page"] = params["pageno"]
params["url"] = f"{search_url}/search/__data.json?{urlencode(args)}"
assign_headers(params['headers'])
return params return params
def response(resp: Response) -> EngineResults: def extract_result(dom_result: list[html.HtmlElement]):
json_response = resp.json() # Infoboxes sometimes appear in the beginning and will have a length of 0
if len(dom_result) == 3:
[a_elem, h3_elem, p_elem] = dom_result
elif len(dom_result) == 4:
[_, a_elem, h3_elem, p_elem] = dom_result
else:
return None
nodes = json_response["nodes"] return {
# 0: is None 'url': extract_text(a_elem.text),
# 1: has "connected=True", not useful 'title': extract_text(h3_elem),
# 2: query results within "data" 'content': extract_text(p_elem),
}
data_nodes = nodes[2]["data"]
# Instead of nested object structure, all objects are flattened into a
# list. Rather, the first object in data_node provides indices into the
# "data_nodes" to access each searchresult (which is an object of more
# indices)
#
# Read the relative TypedDict definitions for details
query_meta_data: DataNodeQueryMetaDataIndices = data_nodes[0]
query_items_indices = query_meta_data["items"]
results = EngineResults()
for idx in data_nodes[query_items_indices]:
query_item_indices: DataNodeResultIndices = data_nodes[idx]
results.add(
MainResult(
url=data_nodes[query_item_indices["link"]],
title=data_nodes[query_item_indices["title"]],
content=data_nodes[query_item_indices["snippet"]],
)
)
return results
def fetch_traits(engine_traits: EngineTraits) -> None: def extract_results(search_results: html.HtmlElement):
"""Fetch languages and regions from Mullvad-Leta""" for search_result in search_results:
dom_result = eval_xpath_list(search_result, 'div/div/*')
result = extract_result(dom_result)
if result is not None:
yield result
def extract_table_data(table):
for row in table.xpath(".//tr")[2:]:
cells = row.xpath(".//td | .//th") # includes headers and data
if len(cells) > 1: # ensure the column exists
cell0 = cells[0].text_content().strip()
cell1 = cells[1].text_content().strip()
yield [cell0, cell1]
def response(resp: Response):
"""Checks if connected to Mullvad VPN, then extracts the search results from
the DOM resp: requests response object"""
dom = html.fromstring(resp.text)
if not is_vpn_connected(dom):
raise SearxEngineResponseException('Not connected to Mullvad VPN')
search_results = eval_xpath(dom.body, '//main/div[2]/div')
return list(extract_results(search_results))
def fetch_traits(engine_traits: EngineTraits):
"""Fetch languages and regions from Mullvad-Leta
.. warning::
Fetching the engine traits also requires a Mullvad VPN connection. If
not connected, then an error message will print and no traits will be
updated.
"""
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
# see https://github.com/searxng/searxng/issues/762 # see https://github.com/searxng/searxng/issues/762
from searx.network import get as http_get from searx.network import post as http_post
# pylint: enable=import-outside-toplevel # pylint: enable=import-outside-toplevel
resp = http_post(search_url, headers=assign_headers({}))
resp = http_get(f"{search_url}/documentation")
if not isinstance(resp, Response): if not isinstance(resp, Response):
print("ERROR: failed to get response from mullvad-leta. Are you connected to the VPN?") print("ERROR: failed to get response from mullvad-leta. Are you connected to the VPN?")
return return
if not resp.ok: if not resp.ok:
print("ERROR: response from mullvad-leta is not OK. Are you connected to the VPN?") print("ERROR: response from mullvad-leta is not OK. Are you connected to the VPN?")
return return
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
if not is_vpn_connected(dom):
print('ERROR: Not connected to Mullvad VPN')
return
# supported region codes
options = eval_xpath_list(dom.body, '//main/div/form/div[2]/div/select[1]/option')
if options is None or len(options) <= 0:
print('ERROR: could not find any results. Are you connected to the VPN?')
for x in options:
eng_country = x.get("value")
# There are 4 HTML tables on the documentation page for extracting information: sxng_locales = get_official_locales(eng_country, engine_traits.languages.keys(), regional=True)
# 0. Keyboard Shortcuts
# 1. Query Parameters (shoutout to Mullvad for accessible docs for integration)
# 2. Country Codes [Country, Code]
# 3. Language Codes [Language, Code]
tables = eval_xpath_list(dom.body, "//table")
if tables is None or len(tables) <= 0:
print("ERROR: could not find any tables. Was the page updated?")
language_table = tables[3] if not sxng_locales:
lang_map = { print(
"zh-hant": "zh_Hans", "ERROR: can't map from Mullvad-Leta country %s (%s) to a babel region."
"zh-hans": "zh_Hant", % (x.get('data-name'), eng_country)
"jp": "ja", )
}
for language, code in extract_table_data(language_table):
locale_tag = lang_map.get(code, code).replace("-", "_") # type: ignore
try:
locale = babel.Locale.parse(locale_tag)
except babel.UnknownLocaleError:
print(f"ERROR: Mullvad-Leta language {language} ({code}) is unknown by babel")
continue continue
sxng_tag = language_tag(locale) for sxng_locale in sxng_locales:
engine_traits.languages[sxng_tag] = code engine_traits.regions[region_tag(sxng_locale)] = eng_country
country_table = tables[2]
country_map = {
"cn": "zh-CN",
"hk": "zh-HK",
"jp": "ja-JP",
"my": "ms-MY",
"tw": "zh-TW",
"uk": "en-GB",
"us": "en-US",
}
for country, code in extract_table_data(country_table):
sxng_tag = country_map.get(code)
if sxng_tag:
engine_traits.regions[sxng_tag] = code
continue
try:
locale = babel.Locale.parse(f"{code.lower()}_{code.upper()}")
except babel.UnknownLocaleError:
locale = None
if locale:
engine_traits.regions[region_tag(locale)] = code
continue
official_locales = get_official_locales(code, engine_traits.languages.keys(), regional=True)
if not official_locales:
print(f"ERROR: Mullvad-Leta country '{code}' ({country}) could not be mapped as expected.")
continue
for locale in official_locales:
engine_traits.regions[region_tag(locale)] = code

View File

@@ -11,7 +11,7 @@ from searx.exceptions import SearxEngineAPIException, SearxEngineCaptchaExceptio
# Metadata # Metadata
about = { about = {
"website": "https://quark.sm.cn/", "website": "https://m.quark.cn/",
"wikidata_id": "Q48816502", "wikidata_id": "Q48816502",
"use_official_api": False, "use_official_api": False,
"require_api_key": False, "require_api_key": False,
@@ -53,7 +53,7 @@ def request(query, params):
category_config = { category_config = {
'general': { 'general': {
'endpoint': 'https://quark.sm.cn/s', 'endpoint': 'https://m.quark.cn/s',
'params': { 'params': {
"q": query, "q": query,
"layout": "html", "layout": "html",

View File

@@ -11,7 +11,7 @@ from lxml import html
from searx.network import get as http_get from searx.network import get as http_get
about = { about = {
"website": "https://soundcloud.com", "website": "ttps://soundcloud.com",
"wikidata_id": "Q568769", "wikidata_id": "Q568769",
"official_api_documentation": "https://developers.soundcloud.com/docs/api/guide", "official_api_documentation": "https://developers.soundcloud.com/docs/api/guide",
"use_official_api": False, "use_official_api": False,

View File

@@ -1,51 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Steam (store) for SearXNG."""
from urllib.parse import urlencode
from searx.utils import html_to_text
from searx.result_types import EngineResults, MainResult
about = {
"website": 'https://store.steampowered.com/',
"wikidata_id": 'Q337535',
"use_official_api": False,
"require_api_key": False,
"results": 'JSON',
}
categories = []
base_url = "https://store.steampowered.com"
def request(query, params):
query_params = {"term": query, "cc": "us", "l": "en"}
params['url'] = f'{base_url}/api/storesearch/?{urlencode(query_params)}'
return params
def response(resp) -> EngineResults:
results = EngineResults()
search_results = resp.json()
for item in search_results.get('items', []):
app_id = item.get('id')
currency = item.get('price', {}).get('currency', 'USD')
price = item.get('price', {}).get('final', 0) / 100
platforms = ', '.join([platform for platform, supported in item.get('platforms', {}).items() if supported])
content = [f'Price: {price:.2f} {currency}', f'Platforms: {platforms}']
results.add(
MainResult(
title=item.get('name'),
content=html_to_text(' | '.join(content)),
url=f'{base_url}/app/{app_id}',
thumbnail=item.get('tiny_image', ''),
)
)
return results

View File

@@ -12,7 +12,7 @@ from typing import Dict
import httpx import httpx
from searx import logger, sxng_debug from searx import logger, searx_debug
from searx.extended_types import SXNG_Response from searx.extended_types import SXNG_Response
from .client import new_client, get_loop, AsyncHTTPTransportNoHttp from .client import new_client, get_loop, AsyncHTTPTransportNoHttp
from .raise_for_httperror import raise_for_httperror from .raise_for_httperror import raise_for_httperror
@@ -186,7 +186,7 @@ class Network:
local_address = next(self._local_addresses_cycle) local_address = next(self._local_addresses_cycle)
proxies = next(self._proxies_cycle) # is a tuple so it can be part of the key proxies = next(self._proxies_cycle) # is a tuple so it can be part of the key
key = (verify, max_redirects, local_address, proxies) key = (verify, max_redirects, local_address, proxies)
hook_log_response = self.log_response if sxng_debug else None hook_log_response = self.log_response if searx_debug else None
if key not in self._clients or self._clients[key].is_closed: if key not in self._clients or self._clients[key].is_closed:
client = new_client( client = new_client(
self.enable_http, self.enable_http,

View File

@@ -155,7 +155,7 @@ class ExternalBangParser(QueryPartParser):
return raw_value.startswith('!!') and len(raw_value) > 2 return raw_value.startswith('!!') and len(raw_value) > 2
def __call__(self, raw_value): def __call__(self, raw_value):
value = raw_value[2:].lower() value = raw_value[2:]
found, bang_ac_list = self._parse(value) if len(value) > 0 else (False, []) found, bang_ac_list = self._parse(value) if len(value) > 0 else (False, [])
if self.enable_autocomplete: if self.enable_autocomplete:
self._autocomplete(bang_ac_list) self._autocomplete(bang_ac_list)
@@ -183,7 +183,7 @@ class BangParser(QueryPartParser):
return raw_value[0] == '!' and (len(raw_value) < 2 or raw_value[1] != '!') return raw_value[0] == '!' and (len(raw_value) < 2 or raw_value[1] != '!')
def __call__(self, raw_value): def __call__(self, raw_value):
value = raw_value[1:].replace('-', ' ').replace('_', ' ').lower() value = raw_value[1:].replace('-', ' ').replace('_', ' ')
found = self._parse(value) if len(value) > 0 else False found = self._parse(value) if len(value) > 0 else False
if found and raw_value[0] == '!': if found and raw_value[0] == '!':
self.raw_text_query.specific = True self.raw_text_query.specific = True

View File

@@ -10,7 +10,7 @@ from typing import Any, Dict, List, Literal, Optional, Tuple, TypedDict, Union
import redis.exceptions import redis.exceptions
from searx import logger, settings, sxng_debug from searx import logger, settings, searx_debug
from searx.redisdb import client as get_redis_client from searx.redisdb import client as get_redis_client
from searx.exceptions import SearxSettingsException from searx.exceptions import SearxSettingsException
from searx.search.processors import PROCESSORS from searx.search.processors import PROCESSORS
@@ -139,7 +139,7 @@ def initialize():
signal.signal(signal.SIGUSR1, _signal_handler) signal.signal(signal.SIGUSR1, _signal_handler)
# special case when debug is activate # special case when debug is activate
if sxng_debug and settings['checker']['off_when_debug']: if searx_debug and settings['checker']['off_when_debug']:
logger.info('debug mode: checker is disabled') logger.info('debug mode: checker is disabled')
return return

View File

@@ -101,8 +101,7 @@ server:
# 1.0 and 1.1 are supported # 1.0 and 1.1 are supported
http_protocol_version: "1.0" http_protocol_version: "1.0"
# POST queries are more secure as they don't show up in history but may cause # POST queries are more secure as they don't show up in history but may cause
# problems when using Firefox containers. # problems when using Firefox containers
# Is overwritten by ${SEARXNG_METHOD}
method: "POST" method: "POST"
default_http_headers: default_http_headers:
X-Content-Type-Options: nosniff X-Content-Type-Options: nosniff
@@ -711,6 +710,26 @@ engines:
search_type: news search_type: news
disabled: true disabled: true
- name: curlie
engine: xpath
shortcut: cl
categories: general
disabled: true
paging: true
lang_all: ''
search_url: https://curlie.org/search?q={query}&lang={lang}&start={pageno}&stime=92452189
page_size: 20
results_xpath: //div[@id="site-list-content"]/div[@class="site-item"]
url_xpath: ./div[@class="title-and-desc"]/a/@href
title_xpath: ./div[@class="title-and-desc"]/a/div
content_xpath: ./div[@class="title-and-desc"]/div[@class="site-descr"]
about:
website: https://curlie.org/
wikidata_id: Q60715723
use_official_api: false
require_api_key: false
results: HTML
- name: currency - name: currency
engine: currency_convert engine: currency_convert
categories: general categories: general
@@ -1400,21 +1419,14 @@ engines:
require_api_key: false require_api_key: false
results: JSON results: JSON
# https://docs.searxng.org/dev/engines/online/mullvad_leta.html # read https://docs.searxng.org/dev/engines/online/mullvad_leta.html
- name: mullvadleta # - name: mullvadleta
engine: mullvad_leta # engine: mullvad_leta
disabled: true # leta_engine: google # choose one of the following: google, brave
leta_engine: google # use_cache: true # Only 100 non-cache searches per day, suggested only for private instances
categories: [general, web] # search_url: https://leta.mullvad.net
shortcut: ml # categories: [general, web]
# shortcut: ml
- name: mullvadleta brave
engine: mullvad_leta
network: mullvadleta
disabled: true
leta_engine: brave
categories: [general, web]
shortcut: mlb
- name: odysee - name: odysee
engine: odysee engine: odysee
@@ -1959,11 +1971,6 @@ engines:
categories: [images, web] categories: [images, web]
shortcut: spi shortcut: spi
- name: steam
engine: steam
shortcut: stm
disabled: true
- name: tokyotoshokan - name: tokyotoshokan
engine: tokyotoshokan engine: tokyotoshokan
shortcut: tt shortcut: tt

View File

@@ -182,7 +182,7 @@ SCHEMA = {
'base_url': SettingsValue((False, str), False, 'SEARXNG_BASE_URL'), 'base_url': SettingsValue((False, str), False, 'SEARXNG_BASE_URL'),
'image_proxy': SettingsValue(bool, False, 'SEARXNG_IMAGE_PROXY'), 'image_proxy': SettingsValue(bool, False, 'SEARXNG_IMAGE_PROXY'),
'http_protocol_version': SettingsValue(('1.0', '1.1'), '1.0'), 'http_protocol_version': SettingsValue(('1.0', '1.1'), '1.0'),
'method': SettingsValue(('POST', 'GET'), 'POST', 'SEARXNG_METHOD'), 'method': SettingsValue(('POST', 'GET'), 'POST'),
'default_http_headers': SettingsValue(dict, {}), 'default_http_headers': SettingsValue(dict, {}),
}, },
'redis': { 'redis': {

View File

@@ -23,7 +23,6 @@ sxng_locales = (
('da-DK', 'Dansk', 'Danmark', 'Danish', '\U0001f1e9\U0001f1f0'), ('da-DK', 'Dansk', 'Danmark', 'Danish', '\U0001f1e9\U0001f1f0'),
('de', 'Deutsch', '', 'German', '\U0001f310'), ('de', 'Deutsch', '', 'German', '\U0001f310'),
('de-AT', 'Deutsch', 'Österreich', 'German', '\U0001f1e6\U0001f1f9'), ('de-AT', 'Deutsch', 'Österreich', 'German', '\U0001f1e6\U0001f1f9'),
('de-BE', 'Deutsch', 'Belgien', 'German', '\U0001f1e7\U0001f1ea'),
('de-CH', 'Deutsch', 'Schweiz', 'German', '\U0001f1e8\U0001f1ed'), ('de-CH', 'Deutsch', 'Schweiz', 'German', '\U0001f1e8\U0001f1ed'),
('de-DE', 'Deutsch', 'Deutschland', 'German', '\U0001f1e9\U0001f1ea'), ('de-DE', 'Deutsch', 'Deutschland', 'German', '\U0001f1e9\U0001f1ea'),
('el', 'Ελληνικά', '', 'Greek', '\U0001f310'), ('el', 'Ελληνικά', '', 'Greek', '\U0001f310'),

View File

@@ -43,14 +43,13 @@
# pxrb <pxrb@users.noreply.translate.codeberg.org>, 2025. # pxrb <pxrb@users.noreply.translate.codeberg.org>, 2025.
# curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025. # curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025. # return42 <return42@noreply.codeberg.org>, 2025.
# Atul_Eterno <atul_eterno@noreply.codeberg.org>, 2025.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: searx\n" "Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n" "POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-04-24 14:06+0000\n" "PO-Revision-Date: 2025-04-08 11:34+0000\n"
"Last-Translator: Atul_Eterno <atul_eterno@noreply.codeberg.org>\n" "Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language-Team: Spanish <https://translate.codeberg.org/projects/searxng/" "Language-Team: Spanish <https://translate.codeberg.org/projects/searxng/"
"searxng/es/>\n" "searxng/es/>\n"
"Language: es\n" "Language: es\n"
@@ -554,7 +553,7 @@ msgstr "Calidad del archivo"
#: searx/plugins/ahmia_filter.py:32 #: searx/plugins/ahmia_filter.py:32
msgid "Ahmia blacklist" msgid "Ahmia blacklist"
msgstr "Lista negra de Ahmia" msgstr ""
#: searx/plugins/ahmia_filter.py:33 #: searx/plugins/ahmia_filter.py:33
msgid "Filter out onion results that appear in Ahmia's blacklist." msgid "Filter out onion results that appear in Ahmia's blacklist."
@@ -1211,8 +1210,6 @@ msgid ""
"A URL containing your preferences. This URL can be used to restore your " "A URL containing your preferences. This URL can be used to restore your "
"settings on a different device." "settings on a different device."
msgstr "" msgstr ""
"Una URL conteniendo sus preferencias. Esta URL puede ser usada para "
"restaurar sus ajustes en un dispositivo diferente."
#: searx/templates/simple/preferences/cookies.html:46 #: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash" msgid "Copy preferences hash"

View File

@@ -16,21 +16,20 @@
# return42 <return42@noreply.codeberg.org>, 2025. # return42 <return42@noreply.codeberg.org>, 2025.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: searx\n" "Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n" "POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-04-20 12:41+0000\n" "PO-Revision-Date: 2025-03-27 13:44+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n" "Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language-Team: Lithuanian <https://translate.codeberg.org/projects/searxng/"
"searxng/lt/>\n"
"Language: lt\n" "Language: lt\n"
"Language-Team: Lithuanian "
"<https://translate.codeberg.org/projects/searxng/searxng/lt/>\n"
"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100"
" < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < "
"11) ? 1 : n % 1 != 0 ? 2: 3);\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < "
"11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 :"
" n % 1 != 0 ? 2: 3);\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n" "Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING'] #. CONSTANT_NAMES['NO_SUBGROUPING']
@@ -526,15 +525,15 @@ msgstr "Failo kokybė"
#: searx/plugins/ahmia_filter.py:32 #: searx/plugins/ahmia_filter.py:32
msgid "Ahmia blacklist" msgid "Ahmia blacklist"
msgstr "Ahmia juodasis sąrašas" msgstr ""
#: searx/plugins/ahmia_filter.py:33 #: searx/plugins/ahmia_filter.py:33
msgid "Filter out onion results that appear in Ahmia's blacklist." msgid "Filter out onion results that appear in Ahmia's blacklist."
msgstr "Išfiltruoti onion rezultatus esančius Ahmia juodajame sąraše." msgstr ""
#: searx/plugins/calculator.py:38 #: searx/plugins/calculator.py:38
msgid "Basic Calculator" msgid "Basic Calculator"
msgstr "Bazinis skaičiuotuvas" msgstr ""
#: searx/plugins/calculator.py:39 #: searx/plugins/calculator.py:39
msgid "Calculate mathematical expressions via the search bar" msgid "Calculate mathematical expressions via the search bar"
@@ -583,8 +582,6 @@ msgid ""
"Displays your IP if the query is \"ip\" and your user agent if the query " "Displays your IP if the query is \"ip\" and your user agent if the query "
"is \"user-agent\"." "is \"user-agent\"."
msgstr "" msgstr ""
"Rodo tavo IP jei užklausa yra „ip“ ir tavo naudotojo agentą jei užklausa yra "
"„user-agent“."
#: searx/plugins/self_info.py:52 #: searx/plugins/self_info.py:52
msgid "Your IP is: " msgid "Your IP is: "
@@ -592,7 +589,7 @@ msgstr "Jūsų IP adresas: "
#: searx/plugins/self_info.py:55 #: searx/plugins/self_info.py:55
msgid "Your user-agent is: " msgid "Your user-agent is: "
msgstr "Tavo naudotojo agentas (user-agent) yra: " msgstr ""
#: searx/plugins/tor_check.py:42 #: searx/plugins/tor_check.py:42
msgid "Tor check plugin" msgid "Tor check plugin"
@@ -609,15 +606,15 @@ msgstr ""
#: searx/plugins/tor_check.py:65 #: searx/plugins/tor_check.py:65
msgid "Could not download the list of Tor exit-nodes from" msgid "Could not download the list of Tor exit-nodes from"
msgstr "Nepavyko atsisiųsti Tor išėjimo mazgų iš" msgstr ""
#: searx/plugins/tor_check.py:72 #: searx/plugins/tor_check.py:72
msgid "You are using Tor and it looks like you have the external IP address" msgid "You are using Tor and it looks like you have the external IP address"
msgstr "Jūs naudojate Tor ir atrodo, kad turite išorinį IP adresą" msgstr ""
#: searx/plugins/tor_check.py:76 #: searx/plugins/tor_check.py:76
msgid "You are not using Tor and you have the external IP address" msgid "You are not using Tor and you have the external IP address"
msgstr "Jūs nenaudojate Tor, tačiau atrodo, kad turite išorinį IP adresą" msgstr ""
#: searx/plugins/tracker_url_remover.py:37 #: searx/plugins/tracker_url_remover.py:37
msgid "Tracker URL remover" msgid "Tracker URL remover"
@@ -629,11 +626,11 @@ msgstr "Šalinti seklių argumentus iš grąžinamų URL"
#: searx/plugins/unit_converter.py:49 #: searx/plugins/unit_converter.py:49
msgid "Unit converter plugin" msgid "Unit converter plugin"
msgstr "Matavimo vienetų konvertavimo papildinys" msgstr ""
#: searx/plugins/unit_converter.py:50 #: searx/plugins/unit_converter.py:50
msgid "Convert between units" msgid "Convert between units"
msgstr "Konvertuoti tarp matavimo vienetų" msgstr ""
#: searx/templates/simple/404.html:4 #: searx/templates/simple/404.html:4
msgid "Page not found" msgid "Page not found"
@@ -945,7 +942,7 @@ msgstr "Pavyzdžiai"
#: searx/templates/simple/answer/translations.html:21 #: searx/templates/simple/answer/translations.html:21
msgid "Definitions" msgid "Definitions"
msgstr "Apibrėžimai" msgstr ""
#: searx/templates/simple/answer/translations.html:30 #: searx/templates/simple/answer/translations.html:30
msgid "Synonyms" msgid "Synonyms"
@@ -969,7 +966,7 @@ msgstr "Pranešimai iš paieškos sistemų"
#: searx/templates/simple/elements/engines_msg.html:7 #: searx/templates/simple/elements/engines_msg.html:7
msgid "seconds" msgid "seconds"
msgstr "sekundės" msgstr ""
#: searx/templates/simple/elements/search_url.html:3 #: searx/templates/simple/elements/search_url.html:3
msgid "Search URL" msgid "Search URL"
@@ -1087,11 +1084,11 @@ msgstr "Pakeiskite nuostatose naudojamą paieškos variklį:"
#: searx/templates/simple/messages/no_results.html:22 #: searx/templates/simple/messages/no_results.html:22
msgid "Switch to another instance:" msgid "Switch to another instance:"
msgstr "Pakeisti instanciją:" msgstr ""
#: searx/templates/simple/messages/no_results.html:24 #: searx/templates/simple/messages/no_results.html:24
msgid "Search for another query or select another category." msgid "Search for another query or select another category."
msgstr "Ieškoti kitos užklausos arba pasirinkti kitą kategoriją." msgstr ""
#: searx/templates/simple/messages/no_results.html:25 #: searx/templates/simple/messages/no_results.html:25
msgid "Go back to the previous page using the previous page button." msgid "Go back to the previous page using the previous page button."
@@ -1104,7 +1101,7 @@ msgstr "Leisti"
#: searx/templates/simple/preferences/answerers.html:5 #: searx/templates/simple/preferences/answerers.html:5
msgid "Keywords (first word in query)" msgid "Keywords (first word in query)"
msgstr "Raktažodžiai (pirmasis užklausos žodis)" msgstr ""
#: searx/templates/simple/preferences/answerers.html:6 #: searx/templates/simple/preferences/answerers.html:6
#: searx/templates/simple/result_templates/packages.html:7 #: searx/templates/simple/result_templates/packages.html:7
@@ -1181,8 +1178,6 @@ msgid ""
"A URL containing your preferences. This URL can be used to restore your " "A URL containing your preferences. This URL can be used to restore your "
"settings on a different device." "settings on a different device."
msgstr "" msgstr ""
"URL adresas su jūsų nustatymais. Šis URL adresas gali būti panaudotas "
"atstatyti jūsų nustatymus kitame įrenginyje."
#: searx/templates/simple/preferences/cookies.html:46 #: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash" msgid "Copy preferences hash"
@@ -1198,7 +1193,7 @@ msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:1 #: searx/templates/simple/preferences/doi_resolver.html:1
msgid "Digital Object Identifier (DOI)" msgid "Digital Object Identifier (DOI)"
msgstr "Skaitmeninis objekto identifikatorius (DOI)" msgstr ""
#: searx/templates/simple/preferences/doi_resolver.html:6 #: searx/templates/simple/preferences/doi_resolver.html:6
msgid "Open Access DOI resolver" msgid "Open Access DOI resolver"
@@ -2063,3 +2058,4 @@ msgstr "slėpti vaizdo įrašą"
#~ "Nurodant tinkintus nustatymus nuostatų URL," #~ "Nurodant tinkintus nustatymus nuostatų URL,"
#~ " jūs galite susinchronizuoti nuostatas tarp" #~ " jūs galite susinchronizuoti nuostatas tarp"
#~ " prietaisų." #~ " prietaisų."

View File

@@ -14,23 +14,21 @@
# Vision <vision@users.noreply.translate.codeberg.org>, 2025. # Vision <vision@users.noreply.translate.codeberg.org>, 2025.
# curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025. # curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025. # return42 <return42@noreply.codeberg.org>, 2025.
# whytf <whytf@noreply.codeberg.org>, 2025.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: searx\n" "Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n" "POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-04-23 19:16+0000\n" "PO-Revision-Date: 2025-03-27 13:44+0000\n"
"Last-Translator: whytf <whytf@noreply.codeberg.org>\n" "Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language-Team: Slovak <https://translate.codeberg.org/projects/searxng/"
"searxng/sk/>\n"
"Language: sk\n" "Language: sk\n"
"Language-Team: Slovak "
"<https://translate.codeberg.org/projects/searxng/searxng/sk/>\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 "
"&& n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n "
">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n" "Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING'] #. CONSTANT_NAMES['NO_SUBGROUPING']
@@ -435,12 +433,12 @@ msgstr "Pozastavené"
#: searx/webutils.py:314 #: searx/webutils.py:314
#, python-brace-format #, python-brace-format
msgid "{minutes} minute(s) ago" msgid "{minutes} minute(s) ago"
msgstr "pred {minutes} minútami" msgstr "pred {minutes} min."
#: searx/webutils.py:315 #: searx/webutils.py:315
#, python-brace-format #, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago" msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "pred {hours} hodinami, {minutes} minútami" msgstr "pred {hours} hod., {minutes} min."
#: searx/answerers/random.py:69 #: searx/answerers/random.py:69
msgid "Generate different random values" msgid "Generate different random values"
@@ -526,15 +524,15 @@ msgstr "Kvalita súboru"
#: searx/plugins/ahmia_filter.py:32 #: searx/plugins/ahmia_filter.py:32
msgid "Ahmia blacklist" msgid "Ahmia blacklist"
msgstr "Čierna listina Ahmia" msgstr ""
#: searx/plugins/ahmia_filter.py:33 #: searx/plugins/ahmia_filter.py:33
msgid "Filter out onion results that appear in Ahmia's blacklist." msgid "Filter out onion results that appear in Ahmia's blacklist."
msgstr "Odfiltruj výsledky onion, ktoré sa zobrazujú na čiernej listine Ahmia." msgstr ""
#: searx/plugins/calculator.py:38 #: searx/plugins/calculator.py:38
msgid "Basic Calculator" msgid "Basic Calculator"
msgstr "Základná Kalkulačka" msgstr ""
#: searx/plugins/calculator.py:39 #: searx/plugins/calculator.py:39
msgid "Calculate mathematical expressions via the search bar" msgid "Calculate mathematical expressions via the search bar"
@@ -628,7 +626,7 @@ msgstr "Odstrániť sledovacie argumenty z vrátenej URL"
#: searx/plugins/unit_converter.py:49 #: searx/plugins/unit_converter.py:49
msgid "Unit converter plugin" msgid "Unit converter plugin"
msgstr "Modul konvertora jednotiek" msgstr ""
#: searx/plugins/unit_converter.py:50 #: searx/plugins/unit_converter.py:50
msgid "Convert between units" msgid "Convert between units"
@@ -1184,8 +1182,6 @@ msgid ""
"A URL containing your preferences. This URL can be used to restore your " "A URL containing your preferences. This URL can be used to restore your "
"settings on a different device." "settings on a different device."
msgstr "" msgstr ""
"Adresa URL obsahujúca vaše preferencie. Túto adresu URL môžete použiť na "
"obnovenie nastavení v inom zariadení."
#: searx/templates/simple/preferences/cookies.html:46 #: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash" msgid "Copy preferences hash"
@@ -2083,3 +2079,4 @@ msgstr "skryť video"
#~ "Zadaním osobitých nastavení v adrese " #~ "Zadaním osobitých nastavení v adrese "
#~ "(URL) nastavení je možné synchronizovať " #~ "(URL) nastavení je možné synchronizovať "
#~ "nastavenia do iných zariadení." #~ "nastavenia do iných zariadení."

View File

@@ -24,22 +24,21 @@
# return42 <return42@users.noreply.translate.codeberg.org>, 2025. # return42 <return42@users.noreply.translate.codeberg.org>, 2025.
# Eshan-K-I <eshan-k-i@users.noreply.translate.codeberg.org>, 2025. # Eshan-K-I <eshan-k-i@users.noreply.translate.codeberg.org>, 2025.
# rajeeban <rajeeban@users.noreply.translate.codeberg.org>, 2025. # rajeeban <rajeeban@users.noreply.translate.codeberg.org>, 2025.
# prashere <prashere@noreply.codeberg.org>, 2025.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: searx\n" "Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n" "POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-04-22 10:45+0000\n" "PO-Revision-Date: 2025-01-20 03:36+0000\n"
"Last-Translator: prashere <prashere@noreply.codeberg.org>\n" "Last-Translator: rajeeban <rajeeban@users.noreply.translate.codeberg.org>"
"Language-Team: Tamil <https://translate.codeberg.org/projects/searxng/" "\n"
"searxng/ta/>\n"
"Language: ta\n" "Language: ta\n"
"Language-Team: Tamil "
"<https://translate.codeberg.org/projects/searxng/searxng/ta/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n" "Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING'] #. CONSTANT_NAMES['NO_SUBGROUPING']
@@ -150,7 +149,7 @@ msgstr "கிடங்கு"
#. CATEGORY_GROUPS['SOFTWARE_WIKIS'] #. CATEGORY_GROUPS['SOFTWARE_WIKIS']
#: searx/searxng.msg #: searx/searxng.msg
msgid "software wikis" msgid "software wikis"
msgstr "மென்பொருள் விக்கி" msgstr "மென்பொருள் விகி"
#. CATEGORY_GROUPS['WEB'] #. CATEGORY_GROUPS['WEB']
#: searx/searxng.msg #: searx/searxng.msg
@@ -2065,3 +2064,4 @@ msgstr "காணொளிகளை மறை"
#~ "preferences URL can be used to " #~ "preferences URL can be used to "
#~ "sync preferences across devices." #~ "sync preferences across devices."
#~ msgstr "" #~ msgstr ""

View File

@@ -6,7 +6,6 @@
# pylint: disable=use-dict-literal # pylint: disable=use-dict-literal
from __future__ import annotations from __future__ import annotations
import inspect
import hashlib import hashlib
import hmac import hmac
import json import json
@@ -30,8 +29,6 @@ from pygments import highlight
from pygments.lexers import get_lexer_by_name from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module
from werkzeug.serving import is_running_from_reloader
import flask import flask
from flask import ( from flask import (
@@ -51,12 +48,12 @@ from flask_babel import (
format_decimal, format_decimal,
) )
import searx
from searx.extended_types import sxng_request from searx.extended_types import sxng_request
from searx import ( from searx import (
logger, logger,
get_setting, get_setting,
settings, settings,
searx_debug,
) )
from searx import infopage from searx import infopage
@@ -84,6 +81,7 @@ from searx.webutils import (
exception_classname_to_text, exception_classname_to_text,
new_hmac, new_hmac,
is_hmac_of, is_hmac_of,
is_flask_run_cmdline,
group_engines_in_tab, group_engines_in_tab,
) )
from searx.webadapter import ( from searx.webadapter import (
@@ -130,6 +128,11 @@ logger = logger.getChild('webapp')
warnings.simplefilter("always") warnings.simplefilter("always")
# check secret_key
if not searx_debug and settings['server']['secret_key'] == 'ultrasecretkey':
logger.error('server.secret_key is not changed. Please use something else instead of ultrasecretkey.')
sys.exit(1)
# about static # about static
logger.debug('static directory is %s', settings['ui']['static_path']) logger.debug('static directory is %s', settings['ui']['static_path'])
static_files = get_static_files(settings['ui']['static_path']) static_files = get_static_files(settings['ui']['static_path'])
@@ -1326,112 +1329,45 @@ def page_not_found(_e):
return render('404.html'), 404 return render('404.html'), 404
def run(): # see https://flask.palletsprojects.com/en/1.1.x/cli/
"""Runs the application on a local development server. # True if "FLASK_APP=searx/webapp.py FLASK_ENV=development flask run"
flask_run_development = (
os.environ.get("FLASK_APP") is not None and os.environ.get("FLASK_ENV") == 'development' and is_flask_run_cmdline()
)
This run method is only called when SearXNG is started via ``__main__``:: # True if reload feature is activated of werkzeug, False otherwise (including uwsgi, etc..)
# __name__ != "__main__" if searx.webapp is imported (make test, make docs, uwsgi...)
python -m searx.webapp # see run() at the end of this file : searx_debug activates the reload feature.
werkzeug_reloader = flask_run_development or (searx_debug and __name__ == "__main__")
Do not use :ref:`run() <flask.Flask.run>` in a production setting. It is
not intended to meet security and performance requirements for a production
server.
It is not recommended to use this function for development with automatic
reloading as this is badly supported. Instead you should be using the flask
command line scripts run support::
flask --app searx.webapp run --debug --reload --host 127.0.0.1 --port 8888
.. _Flask.run: https://flask.palletsprojects.com/en/stable/api/#flask.Flask.run
"""
host: str = get_setting("server.bind_address") # type: ignore
port: int = get_setting("server.port") # type: ignore
if searx.sxng_debug:
logger.debug("run local development server (DEBUG) on %s:%s", host, port)
app.run(
debug=True,
port=port,
host=host,
threaded=True,
extra_files=[DEFAULT_SETTINGS_FILE],
)
else:
logger.debug("run local development server on %s:%s", host, port)
app.run(port=port, host=host, threaded=True)
def is_werkzeug_reload_active() -> bool:
"""Returns ``True`` if server is is launched by :ref:`werkzeug.serving` and
the ``use_reload`` argument was set to ``True``. If this is the case, it
should be avoided that the server is initialized twice (:py:obj:`init`,
:py:obj:`run`).
.. _werkzeug.serving:
https://werkzeug.palletsprojects.com/en/stable/serving/#werkzeug.serving.run_simple
"""
if "uwsgi" in sys.argv:
# server was launched by uWSGI
return False
# https://github.com/searxng/searxng/pull/1656#issuecomment-1214198941
# https://github.com/searxng/searxng/pull/1616#issuecomment-1206137468
frames = inspect.stack()
if len(frames) > 1 and frames[-2].filename.endswith('flask/cli.py'):
# server was launched by "flask run", is argument "--reload" set?
if "--reload" in sys.argv or "--debug" in sys.argv:
return True
elif frames[0].filename.endswith('searx/webapp.py'):
# server was launched by "python -m searx.webapp" / see run()
if searx.sxng_debug:
return True
return False
def init():
if searx.sxng_debug or app.debug:
app.debug = True
searx.sxng_debug = True
# check secret_key in production
if not app.debug and get_setting("server.secret_key") == 'ultrasecretkey':
logger.error("server.secret_key is not changed. Please use something else instead of ultrasecretkey.")
sys.exit(1)
# When automatic reloading is activated stop Flask from initialising twice.
# - https://github.com/pallets/flask/issues/5307#issuecomment-1774646119
# - https://stackoverflow.com/a/25504196
reloader_active = is_werkzeug_reload_active()
werkzeug_run_main = is_running_from_reloader()
if reloader_active and not werkzeug_run_main:
logger.info("in reloading mode and not in main loop, cancel the initialization")
return
# initialize the engines except on the first run of the werkzeug server.
if not werkzeug_reloader or (werkzeug_reloader and os.environ.get("WERKZEUG_RUN_MAIN") == "true"):
locales_initialize() locales_initialize()
redis_initialize() redis_initialize()
searx.plugins.initialize(app) searx.plugins.initialize(app)
searx.search.initialize(
metrics: bool = get_setting("general.enable_metrics") # type: ignore enable_checker=True,
searx.search.initialize(enable_checker=True, check_network=True, enable_metrics=metrics) check_network=True,
enable_metrics=get_setting("general.enable_metrics"),
)
limiter.initialize(app, settings) limiter.initialize(app, settings)
favicons.init() favicons.init()
def run():
logger.debug('starting webserver on %s:%s', settings['server']['bind_address'], settings['server']['port'])
app.run(
debug=searx_debug,
use_debugger=searx_debug,
port=settings['server']['port'],
host=settings['server']['bind_address'],
threaded=True,
extra_files=[DEFAULT_SETTINGS_FILE],
)
application = app application = app
patch_application(app) patch_application(app)
init()
if __name__ == "__main__": if __name__ == "__main__":
run() run()

View File

@@ -9,6 +9,7 @@ import csv
import hashlib import hashlib
import hmac import hmac
import re import re
import inspect
import itertools import itertools
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
@@ -315,6 +316,21 @@ def searxng_l10n_timespan(dt: datetime) -> str: # pylint: disable=invalid-name
return format_date(dt) return format_date(dt)
def is_flask_run_cmdline():
"""Check if the application was started using "flask run" command line
Inspect the callstack.
See https://github.com/pallets/flask/blob/master/src/flask/__main__.py
Returns:
bool: True if the application was started using "flask run".
"""
frames = inspect.stack()
if len(frames) < 2:
return False
return frames[-2].filename.endswith('flask/cli.py')
NO_SUBGROUPING = 'without further subgrouping' NO_SUBGROUPING = 'without further subgrouping'

View File

@@ -5,8 +5,13 @@ import pathlib
import os import os
import aiounittest import aiounittest
# Before import from the searx package, we need to set up the (debug)
# environment. The import of the searx package initialize the searx.settings
# and this in turn takes the defaults from the environment!
os.environ.pop('SEARXNG_SETTINGS_PATH', None) os.environ.pop('SEARXNG_SETTINGS_PATH', None)
os.environ['SEARXNG_DEBUG'] = '1'
os.environ['SEARXNG_DEBUG_LOG_LEVEL'] = 'WARNING'
os.environ['SEARXNG_DISABLE_ETC_SETTINGS'] = '1' os.environ['SEARXNG_DISABLE_ETC_SETTINGS'] = '1'

View File

@@ -27,6 +27,15 @@ class SearxRobotLayer:
webapp = str(tests_path.parent / 'searx' / 'webapp.py') webapp = str(tests_path.parent / 'searx' / 'webapp.py')
exe = 'python' exe = 'python'
# The Flask app is started by Flask.run(...), don't enable Flask's debug
# mode, the debugger from Flask will cause wired process model, where
# the server never dies. Further read:
#
# - debug mode: https://flask.palletsprojects.com/quickstart/#debug-mode
# - Flask.run(..): https://flask.palletsprojects.com/api/#flask.Flask.run
os.environ['SEARXNG_DEBUG'] = '0'
# set robot settings path # set robot settings path
os.environ['SEARXNG_SETTINGS_PATH'] = str(tests_path / 'robot' / 'settings_robot.yml') os.environ['SEARXNG_SETTINGS_PATH'] = str(tests_path / 'robot' / 'settings_robot.yml')