Merge pull request #1 from asciimoo/master

-
This commit is contained in:
Apply55gx 2017-10-25 10:44:28 +02:00 committed by GitHub
commit d800e3fcfa
49 changed files with 754 additions and 1473 deletions

31
.codecov.yml Normal file
View File

@ -0,0 +1,31 @@
comment: false
coverage:
status:
project:
default:
# basic
target: auto
threshold: null
base: auto
# advanced
branches: null
if_no_uploads: error
if_not_found: success
if_ci_failed: error
only_pulls: false
flags: null
paths: null
patch:
default:
# basic
target: auto
threshold: null
base: auto
# advanced
branches: null
if_no_uploads: error
if_not_found: success
if_ci_failed: error
only_pulls: false
flags: null
paths: null

View File

@ -13,21 +13,21 @@ python:
before_install: before_install:
- "export DISPLAY=:99.0" - "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start" - "sh -e /etc/init.d/xvfb start"
- npm install less less-plugin-clean-css grunt-cli - npm install less@2.7 less-plugin-clean-css grunt-cli
- export PATH=`pwd`/node_modules/.bin:$PATH - export PATH=`pwd`/node_modules/.bin:$PATH
- ./manage.sh install_geckodriver ~/drivers - ./manage.sh install_geckodriver ~/drivers
- export PATH=~/drivers:$PATH - export PATH=~/drivers:$PATH
install: install:
- ./manage.sh npm_packages - ./manage.sh npm_packages
- ./manage.sh update_dev_packages - ./manage.sh update_dev_packages
- pip install coveralls - pip install codecov
script: script:
- ./manage.sh styles - ./manage.sh styles
- ./manage.sh grunt_build - ./manage.sh grunt_build
- ./manage.sh tests - ./manage.sh tests
after_success: after_success:
- ./manage.sh py_test_coverage - ./manage.sh py_test_coverage
- coveralls - codecov
notifications: notifications:
irc: irc:
channels: channels:

View File

@ -9,7 +9,8 @@ instances <https://github.com/asciimoo/searx/wiki/Searx-instances>`__.
See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information. See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information.
|Flattr searx| |OpenCollective searx backers|
|OpenCollective searx sponsors|
Installation Installation
~~~~~~~~~~~~ ~~~~~~~~~~~~
@ -41,5 +42,10 @@ More about searx
- `twitter <https://twitter.com/Searx_engine>`__ - `twitter <https://twitter.com/Searx_engine>`__
- IRC: #searx @ freenode - IRC: #searx @ freenode
.. |Flattr searx| image:: http://api.flattr.com/button/flattr-badge-large.png
:target: https://flattr.com/submit/auto?user_id=asciimoo&url=https://github.com/asciimoo/searx&title=searx&language=&tags=github&category=software .. |OpenCollective searx backers| image:: https://opencollective.com/searx/backers/badge.svg
:target: https://opencollective.com/searx#backer
.. |OpenCollective searx sponsors| image:: https://opencollective.com/searx/sponsors/badge.svg
:target: https://opencollective.com/searx#sponsor

View File

@ -1,11 +1,11 @@
#!/bin/sh #!/bin/sh
BASE_DIR=$(dirname "`readlink -f "$0"`") BASE_DIR="$(dirname -- "`readlink -f -- "$0"`")"
PYTHONPATH=$BASE_DIR PYTHONPATH="$BASE_DIR"
SEARX_DIR="$BASE_DIR/searx" SEARX_DIR="$BASE_DIR/searx"
ACTION=$1 ACTION="$1"
cd "$BASE_DIR" cd -- "$BASE_DIR"
update_packages() { update_packages() {
pip install --upgrade pip pip install --upgrade pip
@ -22,40 +22,40 @@ install_geckodriver() {
echo '[!] Checking geckodriver' echo '[!] Checking geckodriver'
# TODO : check the current geckodriver version # TODO : check the current geckodriver version
set -e set -e
geckodriver -V 2>1 > /dev/null || NOTFOUND=1 geckodriver -V > /dev/null 2>&1 || NOTFOUND=1
set +e set +e
if [ -z $NOTFOUND ]; then if [ -z "$NOTFOUND" ]; then
return return
fi fi
GECKODRIVER_VERSION="v0.18.0" GECKODRIVER_VERSION="v0.18.0"
PLATFORM=`python -c "import six; import platform; six.print_(platform.system().lower(), platform.architecture()[0])"` PLATFORM="`python -c "import six; import platform; six.print_(platform.system().lower(), platform.architecture()[0])"`"
case $PLATFORM in case "$PLATFORM" in
"linux 32bit" | "linux2 32bit") ARCH="linux32";; "linux 32bit" | "linux2 32bit") ARCH="linux32";;
"linux 64bit" | "linux2 64bit") ARCH="linux64";; "linux 64bit" | "linux2 64bit") ARCH="linux64";;
"windows 32 bit") ARCH="win32";; "windows 32 bit") ARCH="win32";;
"windows 64 bit") ARCH="win64";; "windows 64 bit") ARCH="win64";;
"mac 64bit") ARCH="macos";; "mac 64bit") ARCH="macos";;
esac esac
GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-$ARCH.tar.gz"; GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-$ARCH.tar.gz";
if [ -z "$1" ]; then if [ -z "$1" ]; then
if [ -z "$VIRTUAL_ENV" ]; then if [ -z "$VIRTUAL_ENV" ]; then
echo "geckodriver can't be installed because VIRTUAL_ENV is not set, you should download it from\n $GECKODRIVER_URL" echo "geckodriver can't be installed because VIRTUAL_ENV is not set, you should download it from\n $GECKODRIVER_URL"
exit exit
else else
GECKODRIVER_DIR="$VIRTUAL_ENV/bin" GECKODRIVER_DIR="$VIRTUAL_ENV/bin"
fi fi
else else
GECKODRIVER_DIR="$1" GECKODRIVER_DIR="$1"
mkdir -p "$GECKODRIVER_DIR" mkdir -p -- "$GECKODRIVER_DIR"
fi fi
echo "Installing $GECKODRIVER_DIR/geckodriver from\n $GECKODRIVER_URL" echo "Installing $GECKODRIVER_DIR/geckodriver from\n $GECKODRIVER_URL"
FILE=`mktemp` FILE="`mktemp`"
wget "$GECKODRIVER_URL" -qO $FILE && tar xz -C "$GECKODRIVER_DIR" -f $FILE geckodriver wget -qO "$FILE" -- "$GECKODRIVER_URL" && tar xz -C "$GECKODRIVER_DIR" -f "$FILE" geckodriver
rm $FILE rm -- "$FILE"
chmod 777 "$GECKODRIVER_DIR/geckodriver" chmod 777 -- "$GECKODRIVER_DIR/geckodriver"
} }
pep8_check() { pep8_check() {
@ -73,14 +73,14 @@ unit_tests() {
py_test_coverage() { py_test_coverage() {
echo '[!] Running python test coverage' echo '[!] Running python test coverage'
PYTHONPATH=`pwd` python -m nose2 -C --coverage "$SEARX_DIR" -s "$BASE_DIR/tests/unit" PYTHONPATH="`pwd`" python -m nose2 -C --log-capture --with-coverage --coverage "$SEARX_DIR" -s "$BASE_DIR/tests/unit" \
coverage report && coverage report \
coverage html && coverage html
} }
robot_tests() { robot_tests() {
echo '[!] Running robot tests' echo '[!] Running robot tests'
PYTHONPATH=`pwd` python "$SEARX_DIR/testing.py" robot PYTHONPATH="`pwd`" python "$SEARX_DIR/testing.py" robot
} }
tests() { tests() {
@ -113,11 +113,11 @@ styles() {
npm_packages() { npm_packages() {
echo '[!] install NPM packages for oscar theme' echo '[!] install NPM packages for oscar theme'
cd $BASE_DIR/searx/static/themes/oscar cd -- "$BASE_DIR/searx/static/themes/oscar"
npm install npm install
echo '[!] install NPM packages for simple theme' echo '[!] install NPM packages for simple theme'
cd $BASE_DIR/searx/static/themes/simple cd -- "$BASE_DIR/searx/static/themes/simple"
npm install npm install
} }
@ -133,7 +133,7 @@ locales() {
} }
help() { help() {
[ -z "$1" ] || printf "Error: $1\n" [ -z "$1" ] || printf 'Error: %s\n' "$1"
echo "Searx manage.sh help echo "Searx manage.sh help
Commands Commands
@ -156,4 +156,4 @@ Commands
[ "$(command -V "$ACTION" | grep ' function$')" = "" ] \ [ "$(command -V "$ACTION" | grep ' function$')" = "" ] \
&& help "action not found" \ && help "action not found" \
|| $ACTION "$2" || "$ACTION" "$2"

View File

@ -1,6 +1,7 @@
babel==2.3.4 babel==2.3.4
mock==2.0.0 mock==2.0.0
nose2[coverage-plugin] nose2[coverage-plugin]
cov-core==1.15.0
pep8==1.7.0 pep8==1.7.0
plone.testing==5.0.0 plone.testing==5.0.0
splinter==0.7.5 splinter==0.7.5

View File

@ -7,4 +7,4 @@ pygments==2.1.3
pyopenssl==17.2.0 pyopenssl==17.2.0
python-dateutil==2.6.1 python-dateutil==2.6.1
pyyaml==3.12 pyyaml==3.12
requests[socks]==2.14.2 requests[socks]==2.18.4

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,6 @@
from lxml import html from lxml import html
from json import loads from json import loads
import re import re
from searx.engines.bing import _fetch_supported_languages, supported_languages_url
from searx.url_utils import urlencode from searx.url_utils import urlencode
# engine dependent config # engine dependent config
@ -26,6 +25,8 @@ categories = ['images']
paging = True paging = True
safesearch = True safesearch = True
time_range_support = True time_range_support = True
language_support = True
supported_languages_url = 'https://www.bing.com/account/general'
# search-url # search-url
base_url = 'https://www.bing.com/' base_url = 'https://www.bing.com/'
@ -45,23 +46,41 @@ safesearch_types = {2: 'STRICT',
_quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U) _quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U)
# get supported region code
def get_region_code(lang, lang_list=None):
region = None
if lang in (lang_list or supported_languages):
region = lang
elif lang.startswith('no'):
region = 'nb-NO'
else:
# try to get a supported country code with language
lang = lang.split('-')[0]
for lc in (lang_list or supported_languages):
if lang == lc.split('-')[0]:
region = lc
break
if region:
return region.lower()
else:
return 'en-us'
# do search-request # do search-request
def request(query, params): def request(query, params):
offset = (params['pageno'] - 1) * 10 + 1 offset = (params['pageno'] - 1) * 10 + 1
# required for cookie
if params['language'] == 'all':
language = 'en-US'
else:
language = params['language']
search_path = search_string.format( search_path = search_string.format(
query=urlencode({'q': query}), query=urlencode({'q': query}),
offset=offset) offset=offset)
language = get_region_code(params['language'])
params['cookies']['SRCHHPGUSR'] = \ params['cookies']['SRCHHPGUSR'] = \
'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0] +\ 'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
'&ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
params['cookies']['_EDGE_S'] = 'mkt=' + language +\
'&ui=' + language + '&F=1'
params['url'] = base_url + search_path params['url'] = base_url + search_path
if params['time_range'] in time_range_dict: if params['time_range'] in time_range_dict:
@ -106,3 +125,22 @@ def response(resp):
# return results # return results
return results return results
# get supported languages from their site
def _fetch_supported_languages(resp):
supported_languages = []
dom = html.fromstring(resp.text)
regions_xpath = '//div[@id="region-section-content"]' \
+ '//ul[@class="b_vList"]/li/a/@href'
regions = dom.xpath(regions_xpath)
for region in regions:
code = re.search('setmkt=[^\&]+', region).group()[7:]
if code == 'nb-NO':
code = 'no-NO'
supported_languages.append(code)
return supported_languages

View File

@ -12,6 +12,7 @@
from json import loads from json import loads
from lxml import html from lxml import html
from searx.engines.bing_images import _fetch_supported_languages, supported_languages_url, get_region_code
from searx.engines.xpath import extract_text from searx.engines.xpath import extract_text
from searx.url_utils import urlencode from searx.url_utils import urlencode
@ -21,6 +22,7 @@ paging = True
safesearch = True safesearch = True
time_range_support = True time_range_support = True
number_of_results = 10 number_of_results = 10
language_support = True
search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\ search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\
'first={offset}&count={number_of_results}&CW=1366&CH=25&FORM=R5VR5' 'first={offset}&count={number_of_results}&CW=1366&CH=25&FORM=R5VR5'
@ -45,7 +47,8 @@ def request(query, params):
'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE') 'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
# language cookie # language cookie
params['cookies']['_EDGE_S'] = 'mkt=' + params['language'].lower() + '&F=1' region = get_region_code(params['language'], lang_list=supported_languages)
params['cookies']['_EDGE_S'] = 'mkt=' + region + '&F=1'
# query and paging # query and paging
params['url'] = search_url.format(query=urlencode({'q': query}), params['url'] = search_url.format(query=urlencode({'q': query}),

View File

@ -1,70 +0,0 @@
"""
Blekko (Images)
@website https://blekko.com
@provide-api yes (inofficial)
@using-api yes
@results JSON
@stable yes
@parse url, title, img_src
"""
from json import loads
from searx.url_utils import urlencode
# engine dependent config
categories = ['images']
paging = True
safesearch = True
# search-url
base_url = 'https://blekko.com'
search_url = '/api/images?{query}&c={c}'
# safesearch definitions
safesearch_types = {2: '1',
1: '',
0: '0'}
# do search-request
def request(query, params):
c = (params['pageno'] - 1) * 48
params['url'] = base_url +\
search_url.format(query=urlencode({'q': query}),
c=c)
if params['pageno'] != 1:
params['url'] += '&page={pageno}'.format(pageno=(params['pageno'] - 1))
# let Blekko know we wan't have profiling
params['cookies']['tag_lesslogging'] = '1'
# parse safesearch argument
params['cookies']['safesearch'] = safesearch_types.get(params['safesearch'], '')
return params
# get response from search-request
def response(resp):
results = []
search_results = loads(resp.text)
# return empty array if there are no results
if not search_results:
return []
for result in search_results:
# append result
results.append({'url': result['page_url'],
'title': result['title'],
'content': '',
'img_src': result['url'],
'template': 'images.html'})
# return results
return results

View File

@ -10,6 +10,8 @@
@parse url, title, content, publishedDate, thumbnail @parse url, title, content, publishedDate, thumbnail
""" """
import random
import string
from dateutil import parser from dateutil import parser
from json import loads from json import loads
from lxml import html from lxml import html
@ -30,12 +32,17 @@ title_xpath = './/h2//a//text()'
content_xpath = './/p//text()' content_xpath = './/p//text()'
pubdate_xpath = './/time' pubdate_xpath = './/time'
digg_cookie_chars = string.ascii_uppercase + string.ascii_lowercase +\
string.digits + "+_"
# do search-request # do search-request
def request(query, params): def request(query, params):
offset = (params['pageno'] - 1) * 10 offset = (params['pageno'] - 1) * 10
params['url'] = search_url.format(position=offset, params['url'] = search_url.format(position=offset,
query=quote_plus(query)) query=quote_plus(query))
params['cookies']['frontend.auid'] = ''.join(random.choice(
digg_cookie_chars) for _ in range(22))
return params return params

View File

@ -134,4 +134,4 @@ def _fetch_supported_languages(resp):
regions_json = loads(response_page) regions_json = loads(response_page)
supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys()) supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
return supported_languages return list(supported_languages)

View File

@ -4,7 +4,7 @@
@website http://www.faroo.com @website http://www.faroo.com
@provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key @provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key
@using-api yes @using-api no
@results JSON @results JSON
@stable yes @stable yes
@parse url, title, content, publishedDate, img_src @parse url, title, content, publishedDate, img_src
@ -20,18 +20,16 @@ categories = ['general', 'news']
paging = True paging = True
language_support = True language_support = True
number_of_results = 10 number_of_results = 10
api_key = None
# search-url # search-url
url = 'http://www.faroo.com/' url = 'http://www.faroo.com/'
search_url = url + 'api?{query}'\ search_url = url + 'instant.json?{query}'\
'&start={offset}'\ '&start={offset}'\
'&length={number_of_results}'\ '&length={number_of_results}'\
'&l={language}'\ '&l={language}'\
'&src={categorie}'\ '&src={categorie}'\
'&i=false'\ '&i=false'\
'&f=json'\ '&c=false'
'&key={api_key}' # noqa
search_category = {'general': 'web', search_category = {'general': 'web',
'news': 'news'} 'news': 'news'}
@ -57,21 +55,15 @@ def request(query, params):
number_of_results=number_of_results, number_of_results=number_of_results,
query=urlencode({'q': query}), query=urlencode({'q': query}),
language=language, language=language,
categorie=categorie, categorie=categorie)
api_key=api_key)
# using searx User-Agent params['headers']['Referer'] = url
params['headers']['User-Agent'] = searx_useragent()
return params return params
# get response from search-request # get response from search-request
def response(resp): def response(resp):
# HTTP-Code 401: api-key is not valide
if resp.status_code == 401:
raise Exception("API key is not valide")
# HTTP-Code 429: rate limit exceeded # HTTP-Code 429: rate limit exceeded
if resp.status_code == 429: if resp.status_code == 429:
raise Exception("rate limit has been exceeded!") raise Exception("rate limit has been exceeded!")
@ -86,31 +78,19 @@ def response(resp):
# parse results # parse results
for result in search_res['results']: for result in search_res['results']:
publishedDate = None
result_json = {'url': result['url'], 'title': result['title'],
'content': result['kwic']}
if result['news']: if result['news']:
# timestamp (milliseconds since 1970) result_json['publishedDate'] = \
publishedDate = datetime.datetime.fromtimestamp(result['date'] / 1000.0) # noqa datetime.datetime.fromtimestamp(result['date'] / 1000.0)
# append news result
results.append({'url': result['url'],
'title': result['title'],
'publishedDate': publishedDate,
'content': result['kwic']})
else:
# append general result
# TODO, publishedDate correct?
results.append({'url': result['url'],
'title': result['title'],
'content': result['kwic']})
# append image result if image url is set # append image result if image url is set
# TODO, show results with an image like in faroo
if result['iurl']: if result['iurl']:
results.append({'template': 'images.html', result_json['template'] = 'videos.html'
'url': result['url'], result_json['thumbnail'] = result['iurl']
'title': result['title'],
'content': result['kwic'], results.append(result_json)
'img_src': result['iurl']})
# return results # return results
return results return results

View File

@ -1,62 +0,0 @@
"""
General Files (Files)
@website http://www.general-files.org
@provide-api no (nothing found)
@using-api no (because nothing found)
@results HTML (using search portal)
@stable no (HTML can change)
@parse url, title, content
@todo detect torrents?
"""
from lxml import html
# engine dependent config
categories = ['files']
paging = True
# search-url
base_url = 'http://www.general-file.com'
search_url = base_url + '/files-{letter}/{query}/{pageno}'
# specific xpath variables
result_xpath = '//table[@class="block-file"]'
title_xpath = './/h2/a//text()'
url_xpath = './/h2/a/@href'
content_xpath = './/p//text()'
# do search-request
def request(query, params):
params['url'] = search_url.format(query=query,
letter=query[0],
pageno=params['pageno'])
return params
# get response from search-request
def response(resp):
results = []
dom = html.fromstring(resp.text)
# parse results
for result in dom.xpath(result_xpath):
url = result.xpath(url_xpath)[0]
# skip fast download links
if not url.startswith('/'):
continue
# append result
results.append({'url': base_url + url,
'title': ''.join(result.xpath(title_xpath)),
'content': ''.join(result.xpath(content_xpath))})
# return results
return results

View File

@ -10,6 +10,7 @@
@parse url, title, content @parse url, title, content
""" """
import random
from json import loads from json import loads
from time import time from time import time
from lxml.html import fromstring from lxml.html import fromstring
@ -32,7 +33,8 @@ search_string = 'search?{query}'\
'&qh=0'\ '&qh=0'\
'&qlang={lang}'\ '&qlang={lang}'\
'&ff={safesearch}'\ '&ff={safesearch}'\
'&rxikd={rxikd}' # random number - 9 digits '&rxieu={rxieu}'\
'&rand={rxikd}' # current unix timestamp
# specific xpath variables # specific xpath variables
results_xpath = '//response//result' results_xpath = '//response//result'
@ -59,10 +61,12 @@ def request(query, params):
else: else:
safesearch = 0 safesearch = 0
# rxieu is some kind of hash from the search query, but accepts random atm
search_path = search_string.format(query=urlencode({'q': query}), search_path = search_string.format(query=urlencode({'q': query}),
offset=offset, offset=offset,
number_of_results=number_of_results, number_of_results=number_of_results,
rxikd=str(time())[:9], rxikd=int(time() * 1000),
rxieu=random.randint(1000000000, 9999999999),
lang=language, lang=language,
safesearch=safesearch) safesearch=safesearch)

View File

@ -67,8 +67,8 @@ def response(resp):
for result in dom.xpath('//div[@class="g"]|//div[@class="g _cy"]'): for result in dom.xpath('//div[@class="g"]|//div[@class="g _cy"]'):
try: try:
r = { r = {
'url': result.xpath('.//div[@class="_cnc"]//a/@href')[0], 'url': result.xpath('.//a[@class="l _PMs"]')[0].attrib.get("href"),
'title': ''.join(result.xpath('.//div[@class="_cnc"]//h3//text()')), 'title': ''.join(result.xpath('.//a[@class="l _PMs"]//text()')),
'content': ''.join(result.xpath('.//div[@class="st"]//text()')), 'content': ''.join(result.xpath('.//div[@class="st"]//text()')),
} }
except: except:

View File

@ -1,7 +1,7 @@
""" """
Nyaa.se (Anime Bittorrent tracker) Nyaa.si (Anime Bittorrent tracker)
@website http://www.nyaa.se/ @website http://www.nyaa.si/
@provide-api no @provide-api no
@using-api no @using-api no
@results HTML @results HTML
@ -12,50 +12,25 @@
from lxml import html from lxml import html
from searx.engines.xpath import extract_text from searx.engines.xpath import extract_text
from searx.url_utils import urlencode from searx.url_utils import urlencode
from searx.utils import get_torrent_size, int_or_zero
# engine dependent config # engine dependent config
categories = ['files', 'images', 'videos', 'music'] categories = ['files', 'images', 'videos', 'music']
paging = True paging = True
# search-url # search-url
base_url = 'http://www.nyaa.se/' base_url = 'http://www.nyaa.si/'
search_url = base_url + '?page=search&{query}&offset={offset}' search_url = base_url + '?page=search&{query}&offset={offset}'
# xpath queries # xpath queries
xpath_results = '//table[@class="tlist"]//tr[contains(@class, "tlistrow")]' xpath_results = '//table[contains(@class, "torrent-list")]//tr[not(th)]'
xpath_category = './/td[@class="tlisticon"]/a' xpath_category = './/td[1]/a[1]'
xpath_title = './/td[@class="tlistname"]/a' xpath_title = './/td[2]/a[last()]'
xpath_torrent_file = './/td[@class="tlistdownload"]/a' xpath_torrent_links = './/td[3]/a'
xpath_filesize = './/td[@class="tlistsize"]/text()' xpath_filesize = './/td[4]/text()'
xpath_seeds = './/td[@class="tlistsn"]/text()' xpath_seeds = './/td[6]/text()'
xpath_leeches = './/td[@class="tlistln"]/text()' xpath_leeches = './/td[7]/text()'
xpath_downloads = './/td[@class="tlistdn"]/text()' xpath_downloads = './/td[8]/text()'
# convert a variable to integer or return 0 if it's not a number
def int_or_zero(num):
if isinstance(num, list):
if len(num) < 1:
return 0
num = num[0]
if num.isdigit():
return int(num)
return 0
# get multiplier to convert torrent size to bytes
def get_filesize_mul(suffix):
return {
'KB': 1024,
'MB': 1024 ** 2,
'GB': 1024 ** 3,
'TB': 1024 ** 4,
'KIB': 1024,
'MIB': 1024 ** 2,
'GIB': 1024 ** 3,
'TIB': 1024 ** 4
}[str(suffix).upper()]
# do search-request # do search-request
@ -72,25 +47,32 @@ def response(resp):
dom = html.fromstring(resp.text) dom = html.fromstring(resp.text)
for result in dom.xpath(xpath_results): for result in dom.xpath(xpath_results):
# defaults
filesize = 0
magnet_link = ""
torrent_link = ""
# category in which our torrent belongs # category in which our torrent belongs
category = result.xpath(xpath_category)[0].attrib.get('title') try:
category = result.xpath(xpath_category)[0].attrib.get('title')
except:
pass
# torrent title # torrent title
page_a = result.xpath(xpath_title)[0] page_a = result.xpath(xpath_title)[0]
title = extract_text(page_a) title = extract_text(page_a)
# link to the page # link to the page
href = page_a.attrib.get('href') href = base_url + page_a.attrib.get('href')
# link to the torrent file for link in result.xpath(xpath_torrent_links):
torrent_link = result.xpath(xpath_torrent_file)[0].attrib.get('href') url = link.attrib.get('href')
if 'magnet' in url:
# torrent size # link to the magnet
try: magnet_link = url
file_size, suffix = result.xpath(xpath_filesize)[0].split(' ') else:
file_size = int(float(file_size) * get_filesize_mul(suffix)) # link to the torrent file
except: torrent_link = url
file_size = None
# seed count # seed count
seed = int_or_zero(result.xpath(xpath_seeds)) seed = int_or_zero(result.xpath(xpath_seeds))
@ -101,6 +83,14 @@ def response(resp):
# torrent downloads count # torrent downloads count
downloads = int_or_zero(result.xpath(xpath_downloads)) downloads = int_or_zero(result.xpath(xpath_downloads))
# let's try to calculate the torrent size
try:
filesize_info = result.xpath(xpath_filesize)[0]
filesize, filesize_multiplier = filesize_info.split()
filesize = get_torrent_size(filesize, filesize_multiplier)
except:
pass
# content string contains all information not included into template # content string contains all information not included into template
content = 'Category: "{category}". Downloaded {downloads} times.' content = 'Category: "{category}". Downloaded {downloads} times.'
content = content.format(category=category, downloads=downloads) content = content.format(category=category, downloads=downloads)
@ -110,8 +100,9 @@ def response(resp):
'content': content, 'content': content,
'seed': seed, 'seed': seed,
'leech': leech, 'leech': leech,
'filesize': file_size, 'filesize': filesize,
'torrentfile': torrent_link, 'torrentfile': torrent_link,
'magnetlink': magnet_link,
'template': 'torrent.html'}) 'template': 'torrent.html'})
return results return results

View File

@ -118,7 +118,7 @@ def _fetch_supported_languages(resp):
dom = fromstring(resp.text) dom = fromstring(resp.text)
options = dom.xpath('//div[@id="regions-popup"]//ul/li/a') options = dom.xpath('//div[@id="regions-popup"]//ul/li/a')
for option in options: for option in options:
code = option.xpath('./@data-val')[0] code = option.xpath('./@data-search-language')[0]
if code.startswith('nb-'): if code.startswith('nb-'):
code = code.replace('nb', 'no', 1) code = code.replace('nb', 'no', 1)
supported_languages.append(code) supported_languages.append(code)

View File

@ -14,8 +14,8 @@ import re
from lxml import html from lxml import html
from searx.engines.xpath import extract_text from searx.engines.xpath import extract_text
from datetime import datetime from datetime import datetime
from searx.engines.nyaa import int_or_zero, get_filesize_mul
from searx.url_utils import urlencode from searx.url_utils import urlencode
from searx.utils import get_torrent_size, int_or_zero
# engine dependent config # engine dependent config
categories = ['files', 'videos', 'music'] categories = ['files', 'videos', 'music']
@ -76,8 +76,7 @@ def response(resp):
try: try:
# ('1.228', 'GB') # ('1.228', 'GB')
groups = size_re.match(item).groups() groups = size_re.match(item).groups()
multiplier = get_filesize_mul(groups[1]) params['filesize'] = get_torrent_size(groups[0], groups[1])
params['filesize'] = int(multiplier * float(groups[0]))
except: except:
pass pass
elif item.startswith('Date:'): elif item.startswith('Date:'):

View File

@ -1,7 +1,7 @@
""" """
Torrentz.eu (BitTorrent meta-search engine) Torrentz2.eu (BitTorrent meta-search engine)
@website https://torrentz.eu/ @website https://torrentz2.eu/
@provide-api no @provide-api no
@using-api no @using-api no
@ -14,24 +14,24 @@
import re import re
from lxml import html from lxml import html
from datetime import datetime from datetime import datetime
from searx.engines.nyaa import int_or_zero, get_filesize_mul
from searx.engines.xpath import extract_text from searx.engines.xpath import extract_text
from searx.url_utils import urlencode from searx.url_utils import urlencode
from searx.utils import get_torrent_size
# engine dependent config # engine dependent config
categories = ['files', 'videos', 'music'] categories = ['files', 'videos', 'music']
paging = True paging = True
# search-url # search-url
# https://torrentz.eu/search?f=EXAMPLE&p=6 # https://torrentz2.eu/search?f=EXAMPLE&p=6
base_url = 'https://torrentz.eu/' base_url = 'https://torrentz2.eu/'
search_url = base_url + 'search?{query}' search_url = base_url + 'search?{query}'
# do search-request # do search-request
def request(query, params): def request(query, params):
page = params['pageno'] - 1 page = params['pageno'] - 1
query = urlencode({'q': query, 'p': page}) query = urlencode({'f': query, 'p': page})
params['url'] = search_url.format(query=query) params['url'] = search_url.format(query=query)
return params return params
@ -54,22 +54,29 @@ def response(resp):
# extract url and remove a slash in the beginning # extract url and remove a slash in the beginning
link = links[0].attrib.get('href').lstrip('/') link = links[0].attrib.get('href').lstrip('/')
seed = result.xpath('./dd/span[@class="u"]/text()')[0].replace(',', '') seed = 0
leech = result.xpath('./dd/span[@class="d"]/text()')[0].replace(',', '') leech = 0
try:
seed = int(result.xpath('./dd/span[4]/text()')[0].replace(',', ''))
leech = int(result.xpath('./dd/span[5]/text()')[0].replace(',', ''))
except:
pass
params = { params = {
'url': base_url + link, 'url': base_url + link,
'title': title, 'title': title,
'seed': int_or_zero(seed), 'seed': seed,
'leech': int_or_zero(leech), 'leech': leech,
'template': 'torrent.html' 'template': 'torrent.html'
} }
# let's try to calculate the torrent size # let's try to calculate the torrent size
try: try:
size_str = result.xpath('./dd/span[@class="s"]/text()')[0] filesize_info = result.xpath('./dd/span[3]/text()')[0]
size, suffix = size_str.split() filesize, filesize_multiplier = filesize_info.split()
params['filesize'] = int(size) * get_filesize_mul(suffix) filesize = get_torrent_size(filesize, filesize_multiplier)
params['filesize'] = filesize
except: except:
pass pass
@ -80,9 +87,8 @@ def response(resp):
# extract and convert creation date # extract and convert creation date
try: try:
date_str = result.xpath('./dd/span[@class="a"]/span')[0].attrib.get('title') date_ts = result.xpath('./dd/span[2]')[0].attrib.get('title')
# Fri, 25 Mar 2016 16:29:01 date = datetime.fromtimestamp(float(date_ts))
date = datetime.strptime(date_str, '%a, %d %b %Y %H:%M:%S')
params['publishedDate'] = date params['publishedDate'] = date
except: except:
pass pass

View File

@ -5,6 +5,11 @@
language_codes = ( language_codes = (
(u"ar-SA", u"العربية", u"", u"Arabic"), (u"ar-SA", u"العربية", u"", u"Arabic"),
(u"bg-BG", u"Български", u"", u"Bulgarian"), (u"bg-BG", u"Български", u"", u"Bulgarian"),
(u"ca", u"Català", u"", u"Catalan"),
(u"ca-AD", u"Català", u"Andorra", u"Catalan"),
(u"ca-CT", u"Català", u"", u"Catalan"),
(u"ca-ES", u"Català", u"Espanya", u"Catalan"),
(u"ca-FR", u"Català", u"França", u"Catalan"),
(u"cs-CZ", u"Čeština", u"", u"Czech"), (u"cs-CZ", u"Čeština", u"", u"Czech"),
(u"da-DK", u"Dansk", u"", u"Danish"), (u"da-DK", u"Dansk", u"", u"Danish"),
(u"de", u"Deutsch", u"", u"German"), (u"de", u"Deutsch", u"", u"German"),
@ -15,9 +20,7 @@ language_codes = (
(u"en", u"English", u"", u"English"), (u"en", u"English", u"", u"English"),
(u"en-AU", u"English", u"Australia", u"English"), (u"en-AU", u"English", u"Australia", u"English"),
(u"en-CA", u"English", u"Canada", u"English"), (u"en-CA", u"English", u"Canada", u"English"),
(u"en-CY", u"English", u"Cyprus", u"English"),
(u"en-GB", u"English", u"United Kingdom", u"English"), (u"en-GB", u"English", u"United Kingdom", u"English"),
(u"en-GD", u"English", u"Grenada", u"English"),
(u"en-ID", u"English", u"Indonesia", u"English"), (u"en-ID", u"English", u"Indonesia", u"English"),
(u"en-IE", u"English", u"Ireland", u"English"), (u"en-IE", u"English", u"Ireland", u"English"),
(u"en-IN", u"English", u"India", u"English"), (u"en-IN", u"English", u"India", u"English"),
@ -28,6 +31,7 @@ language_codes = (
(u"en-US", u"English", u"United States", u"English"), (u"en-US", u"English", u"United States", u"English"),
(u"en-ZA", u"English", u"South Africa", u"English"), (u"en-ZA", u"English", u"South Africa", u"English"),
(u"es", u"Español", u"", u"Spanish"), (u"es", u"Español", u"", u"Spanish"),
(u"es-AD", u"Español", u"Andorra", u"Spanish"),
(u"es-AR", u"Español", u"Argentina", u"Spanish"), (u"es-AR", u"Español", u"Argentina", u"Spanish"),
(u"es-CL", u"Español", u"Chile", u"Spanish"), (u"es-CL", u"Español", u"Chile", u"Spanish"),
(u"es-CO", u"Español", u"Colombia", u"Spanish"), (u"es-CO", u"Español", u"Colombia", u"Spanish"),
@ -38,38 +42,32 @@ language_codes = (
(u"et-EE", u"Eesti", u"", u"Estonian"), (u"et-EE", u"Eesti", u"", u"Estonian"),
(u"fi-FI", u"Suomi", u"", u"Finnish"), (u"fi-FI", u"Suomi", u"", u"Finnish"),
(u"fr", u"Français", u"", u"French"), (u"fr", u"Français", u"", u"French"),
(u"fr-AD", u"Français", u"Andorre", u"French"),
(u"fr-BE", u"Français", u"Belgique", u"French"), (u"fr-BE", u"Français", u"Belgique", u"French"),
(u"fr-CA", u"Français", u"Canada", u"French"), (u"fr-CA", u"Français", u"Canada", u"French"),
(u"fr-CH", u"Français", u"Suisse", u"French"), (u"fr-CH", u"Français", u"Suisse", u"French"),
(u"fr-FR", u"Français", u"France", u"French"), (u"fr-FR", u"Français", u"France", u"French"),
(u"he-IL", u"עברית", u"", u"Hebrew"), (u"he-IL", u"עברית", u"", u"Hebrew"),
(u"hr-HR", u"Hrvatski", u"", u"Croatian"),
(u"hu-HU", u"Magyar", u"", u"Hungarian"), (u"hu-HU", u"Magyar", u"", u"Hungarian"),
(u"id-ID", u"Bahasa Indonesia", u"", u"Indonesian"),
(u"it", u"Italiano", u"", u"Italian"), (u"it", u"Italiano", u"", u"Italian"),
(u"it-CH", u"Italiano", u"Svizzera", u"Italian"), (u"it-CH", u"Italiano", u"Svizzera", u"Italian"),
(u"it-IT", u"Italiano", u"Italia", u"Italian"), (u"it-IT", u"Italiano", u"Italia", u"Italian"),
(u"ja-JP", u"日本語", u"", u"Japanese"), (u"ja-JP", u"日本語", u"", u"Japanese"),
(u"ko-KR", u"한국어", u"", u"Korean"), (u"ko-KR", u"한국어", u"", u"Korean"),
(u"lt-LT", u"Lietuvių", u"", u"Lithuanian"),
(u"lv-LV", u"Latviešu", u"", u"Latvian"),
(u"ms-MY", u"Bahasa Melayu", u"", u"Malay"),
(u"nl", u"Nederlands", u"", u"Dutch"), (u"nl", u"Nederlands", u"", u"Dutch"),
(u"nl-BE", u"Nederlands", u"België", u"Dutch"), (u"nl-BE", u"Nederlands", u"België", u"Dutch"),
(u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"), (u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"),
(u"no-NO", u"Norsk", u"", u"Norwegian"), (u"no-NO", u"Norsk", u"", u"Norwegian"),
(u"pl-PL", u"Polski", u"", u"Polish"), (u"pl-PL", u"Polski", u"", u"Polish"),
(u"pt", u"Português", u"", u"Portuguese"), (u"pt", u"Português", u"", u"Portuguese"),
(u"pt-AD", u"Português", u"Andorra", u"Portuguese"),
(u"pt-BR", u"Português", u"Brasil", u"Portuguese"), (u"pt-BR", u"Português", u"Brasil", u"Portuguese"),
(u"pt-PT", u"Português", u"Portugal", u"Portuguese"), (u"pt-PT", u"Português", u"Portugal", u"Portuguese"),
(u"ro-RO", u"Română", u"", u"Romanian"), (u"ro-RO", u"Română", u"", u"Romanian"),
(u"ru-RU", u"Русский", u"", u"Russian"), (u"ru-RU", u"Русский", u"", u"Russian"),
(u"sk-SK", u"Slovenčina", u"", u"Slovak"),
(u"sl", u"Slovenščina", u"", u"Slovenian"),
(u"sv-SE", u"Svenska", u"", u"Swedish"), (u"sv-SE", u"Svenska", u"", u"Swedish"),
(u"th-TH", u"ไทย", u"", u"Thai"), (u"th-TH", u"ไทย", u"", u"Thai"),
(u"tr-TR", u"Türkçe", u"", u"Turkish"), (u"tr-TR", u"Türkçe", u"", u"Turkish"),
(u"vi-VN", u"Tiếng Việt", u"", u"Vietnamese"),
(u"zh", u"中文", u"", u"Chinese"), (u"zh", u"中文", u"", u"Chinese"),
(u"zh-CN", u"中文", u"中国", u"Chinese"), (u"zh-CN", u"中文", u"中国", u"Chinese"),
(u"zh-HK", u"中文", u"香港", u"Chinese"), (u"zh-HK", u"中文", u"香港", u"Chinese"),

View File

@ -189,11 +189,10 @@ engines:
shortcut : et shortcut : et
disabled : True disabled : True
# api-key required: http://www.faroo.com/hp/api/api.html#key - name : faroo
# - name : faroo engine : faroo
# engine : faroo shortcut : fa
# shortcut : fa disabled : True
# api_key : 'apikey' # required!
- name : 500px - name : 500px
engine : www500px engine : www500px
@ -247,15 +246,16 @@ engines:
disabled: True disabled: True
- name : gitlab - name : gitlab
engine : xpath engine : json_engine
paging : True paging : True
search_url : https://gitlab.com/search?page={pageno}&search={query} search_url : https://gitlab.com/api/v4/projects?search={query}&page={pageno}
url_xpath : //li[@class="project-row"]//a[@class="project"]/@href url_query : web_url
title_xpath : //li[@class="project-row"]//span[contains(@class, "project-full-name")] title_query : name_with_namespace
content_xpath : //li[@class="project-row"]//div[@class="description"]/p content_query : description
page_size : 20
categories : it categories : it
shortcut : gl shortcut : gl
timeout : 5.0 timeout : 10.0
disabled : True disabled : True
- name : github - name : github
@ -326,9 +326,9 @@ engines:
engine : xpath engine : xpath
paging : True paging : True
search_url : https://geektimes.ru/search/page{pageno}/?q={query} search_url : https://geektimes.ru/search/page{pageno}/?q={query}
url_xpath : //div[@class="search_results"]//a[@class="post__title_link"]/@href url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
title_xpath : //div[@class="search_results"]//a[@class="post__title_link"] title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
content_xpath : //div[@class="search_results"]//div[contains(@class, "content")] content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
categories : it categories : it
timeout : 4.0 timeout : 4.0
disabled : True disabled : True
@ -338,9 +338,9 @@ engines:
engine : xpath engine : xpath
paging : True paging : True
search_url : https://habrahabr.ru/search/page{pageno}/?q={query} search_url : https://habrahabr.ru/search/page{pageno}/?q={query}
url_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")]/@href url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
title_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")] title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
content_xpath : //div[@class="search_results"]//div[contains(@class, "content")] content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
categories : it categories : it
timeout : 4.0 timeout : 4.0
disabled : True disabled : True
@ -556,6 +556,12 @@ engines:
timeout : 6.0 timeout : 6.0
disabled : True disabled : True
- name : torrentz
engine : torrentz
shortcut : tor
url: https://torrentz2.eu/
timeout : 3.0
- name : twitter - name : twitter
engine : twitter engine : twitter
shortcut : tw shortcut : tw
@ -579,6 +585,7 @@ engines:
- name : yahoo - name : yahoo
engine : yahoo engine : yahoo
shortcut : yh shortcut : yh
disabled : True
- name : yandex - name : yandex
engine : yandex engine : yandex
@ -639,10 +646,10 @@ engines:
engine: xpath engine: xpath
shortcut: vo shortcut: vo
categories: social media categories: social media
search_url : https://voat.co/search?q={query} search_url : https://searchvoat.co/?t={query}
url_xpath : //p[contains(@class, "title")]/a/@href url_xpath : //div[@class="entry"]/p/a[@class="title"]/@href
title_xpath : //p[contains(@class, "title")]/a title_xpath : //div[@class="entry"]/p/a[@class="title"]
content_xpath : //span[@class="domain"] content_xpath : //div[@class="entry"]/p/span[@class="domain"]
timeout : 10.0 timeout : 10.0
disabled : True disabled : True
@ -651,12 +658,6 @@ engines:
shortcut : 1337x shortcut : 1337x
disabled : True disabled : True
#The blekko technology and team have joined IBM Watson! -> https://blekko.com/
# - name : blekko images
# engine : blekko_images
# locale : en-US
# shortcut : bli
# - name : yacy # - name : yacy
# engine : yacy # engine : yacy
# shortcut : ya # shortcut : ya
@ -676,7 +677,6 @@ locales:
bg : Български (Bulgarian) bg : Български (Bulgarian)
cs : Čeština (Czech) cs : Čeština (Czech)
de : Deutsch (German) de : Deutsch (German)
de_DE : Deutsch (German_Germany)
el_GR : Ελληνικά (Greek_Greece) el_GR : Ελληνικά (Greek_Greece)
eo : Esperanto (Esperanto) eo : Esperanto (Esperanto)
es : Español (Spanish) es : Español (Spanish)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
/*! oscar/searx.min.js | 25-07-2016 | https://github.com/asciimoo/searx */ /*! oscar/searx.min.js | 06-10-2017 | https://github.com/asciimoo/searx */
requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&($(this).hasClass("collapsed")?new_html=$(this).html().replace(a,b):new_html=$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&($(this).hasClass("btn-default")?new_html=$(this).html().replace(b,c):new_html=$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");void 0!==c&&c!==!1||b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||i.indexOf(d)==-1){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(b.tags[d].indexOf(":")!=-1){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(a){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";var h=L.map(b),i="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",j='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',k=new L.TileLayer(i,{minZoom:1,maxZoom:19,attribution:j}),l="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",m='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';new L.TileLayer(l,{minZoom:1,maxZoom:19,attribution:m});map_bounds?setTimeout(function(){h.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?h.setView(new L.LatLng(d,c),e):h.setView(new L.LatLng(d,c),8)),h.addLayer(k);var n={"OSM Mapnik":k};L.control.layers(n).addTo(h),g&&L.geoJson(g).addTo(h)}),$(this).off(a)})}); requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"./autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&($(this).hasClass("collapsed")?new_html=$(this).html().replace(a,b):new_html=$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&($(this).hasClass("btn-default")?new_html=$(this).html().replace(b,c):new_html=$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");void 0!==c&&c!==!1||b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||i.indexOf(d)==-1){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(b.tags[d].indexOf(":")!=-1){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(a){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";var h=L.map(b),i="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",j='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',k=new L.TileLayer(i,{minZoom:1,maxZoom:19,attribution:j}),l="https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png",m='Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';new L.TileLayer(l,{minZoom:1,maxZoom:19,attribution:m});map_bounds?setTimeout(function(){h.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?h.setView(new L.LatLng(d,c),e):h.setView(new L.LatLng(d,c),8)),h.addLayer(k);var n={"OSM Mapnik":k};L.control.layers(n).addTo(h),g&&L.geoJson(g).addTo(h)}),$(this).off(a)})});

View File

@ -19,3 +19,5 @@
@import "cursor.less"; @import "cursor.less";
@import "code.less"; @import "code.less";
@import "preferences.less";

View File

@ -0,0 +1,3 @@
.table > tbody > tr > td, .table > tbody > tr > th {
vertical-align: middle !important;
}

View File

@ -17,3 +17,5 @@
@import "code.less"; @import "code.less";
@import "navbar.less"; @import "navbar.less";
@import "preferences.less";

View File

@ -0,0 +1,3 @@
.table > tbody > tr > td, .table > tbody > tr > th {
vertical-align: middle !important;
}

View File

@ -1,5 +1,5 @@
<div class="result {{ result.class }}"> <div class="result {{ result.class }}">
<h3 class="result_title">{% if result['favicon'] %}<img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" alt="{{result['favicon']}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3> <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
{% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %} {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
<p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p> <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
{% if result.repository %}<p class="content"><a href="{{ result.repository|safe }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %} {% if result.repository %}<p class="content"><a href="{{ result.repository|safe }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %}

View File

@ -1,5 +1,5 @@
<div class="result {{ result.class }}"> <div class="result {{ result.class }}">
<h3 class="result_title"> {% if result['favicon'] %}<img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" alt="{{result['favicon']}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3> <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
<p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a></p> <p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a></p>
{% if result.publishedDate %}<p class="published_date">{{ result.publishedDate }}</p>{% endif %} {% if result.publishedDate %}<p class="published_date">{{ result.publishedDate }}</p>{% endif %}
<p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p> <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>

View File

@ -4,9 +4,8 @@
{%- endmacro %} {%- endmacro %}
<!-- Draw favicon --> <!-- Draw favicon -->
<!-- TODO: using url_for methode -->
{% macro draw_favicon(favicon) -%} {% macro draw_favicon(favicon) -%}
<img width="32" height="32" class="favicon" src="static/themes/oscar/img/icons/{{ favicon }}.png" alt="{{ favicon }}" /> <img width="32" height="32" class="favicon" src="{{ url_for('static', filename='themes/oscar/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
{%- endmacro %} {%- endmacro %}
{%- macro result_link(url, title, classes='') -%} {%- macro result_link(url, title, classes='') -%}
@ -86,3 +85,15 @@
</label> </label>
</div> </div>
{%- endmacro %} {%- endmacro %}
{% macro support_toggle(supports) -%}
{% if supports %}
<span class="label label-success">
{{ _("supported") }}
</span>
{% else %}
<span class="label label-danger">
{{ _("not supported") }}
</span>
{% endif %}
{%- endmacro %}

View File

@ -1,7 +1,8 @@
{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle %} {% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle %}
{% extends "oscar/base.html" %} {% extends "oscar/base.html" %}
{% block title %}{{ _('preferences') }} - {% endblock %} {% block title %}{{ _('preferences') }} - {% endblock %}
{% block content %} {% block content %}
<div> <div>
<h1>{{ _('Preferences') }}</h1> <h1>{{ _('Preferences') }}</h1>
@ -148,7 +149,7 @@
<th>{{ _("Allow") }}</th> <th>{{ _("Allow") }}</th>
<th>{{ _("Engine name") }}</th> <th>{{ _("Engine name") }}</th>
<th>{{ _("Shortcut") }}</th> <th>{{ _("Shortcut") }}</th>
<th>{{ _("Supports selected language") }}</th> <th>{{ _("Selected language") }}</th>
<th>{{ _("SafeSearch") }}</th> <th>{{ _("SafeSearch") }}</th>
<th>{{ _("Time range") }}</th> <th>{{ _("Time range") }}</th>
<th>{{ _("Avg. time") }}</th> <th>{{ _("Avg. time") }}</th>
@ -156,8 +157,9 @@
{% else %} {% else %}
<th>{{ _("Max time") }}</th> <th>{{ _("Max time") }}</th>
<th>{{ _("Avg. time") }}</th> <th>{{ _("Avg. time") }}</th>
<th>{{ _("Time range") }}</th>
<th>{{ _("SafeSearch") }}</th> <th>{{ _("SafeSearch") }}</th>
<th>{{ _("Supports selected language") }}</th> <th>{{ _("Selected language") }}</th>
<th>{{ _("Shortcut") }}</th> <th>{{ _("Shortcut") }}</th>
<th>{{ _("Engine name") }}</th> <th>{{ _("Engine name") }}</th>
<th>{{ _("Allow") }}</th> <th>{{ _("Allow") }}</th>
@ -172,17 +174,18 @@
</td> </td>
<th>{{ search_engine.name }}</th> <th>{{ search_engine.name }}</th>
<td class="name">{{ shortcuts[search_engine.name] }}</td> <td class="name">{{ shortcuts[search_engine.name] }}</td>
<td><input type="checkbox" {{ "checked" if current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ support_toggle(current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages) }}</td>
<td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
<td><input type="checkbox" {{ "checked" if search_engine.time_range_support==True else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
<td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td> <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td> <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
{% else %} {% else %}
<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td> <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
<td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td> <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
<td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
<td><input type="checkbox" {{ "checked" if current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages else ""}} readonly="readonly" disabled="disabled"></td> <td>{{ support_toggle(search_engine.safesearch==True) }}</td>
<td>{{ shortcuts[search_engine.name] }}</td> <td>{{ support_toggle(current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages) }}</td>
<td>{{ shortcuts[search_engine.name] }}</td>
<th>{{ search_engine.name }}</th> <th>{{ search_engine.name }}</th>
<td class="onoff-checkbox"> <td class="onoff-checkbox">
{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }} {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}

View File

@ -8,9 +8,8 @@
{%- endmacro %} {%- endmacro %}
<!-- Draw favicon --> <!-- Draw favicon -->
<!-- TODO: using url_for methode -->
{% macro draw_favicon(favicon) -%} {% macro draw_favicon(favicon) -%}
<img width="14" height="14" class="favicon" src="static/themes/simple/img/icons/{{ favicon }}.png" alt="{{ favicon }}" /> <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='themes/simple/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
{%- endmacro %} {%- endmacro %}
{% macro result_open_link(url, classes='') -%} {% macro result_open_link(url, classes='') -%}

View File

@ -1,844 +0,0 @@
# Translations template for PROJECT.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
#
# Translators:
# Bamstam, 2016-2017
# Benjamin Richter <benjamin@hacktherack.de>, 2015
# cy8aer <cybaer42@web.de>, 2016-2017
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-12-29 10:42+0100\n"
"PO-Revision-Date: 2017-05-19 20:17+0000\n"
"Last-Translator: cy8aer <cybaer42@web.de>\n"
"Language-Team: German (Germany) (http://www.transifex.com/asciimoo/searx/language/de_DE/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"Language: de_DE\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: searx/webapp.py:123
msgid "files"
msgstr "Dateien"
#: searx/webapp.py:124
msgid "general"
msgstr "Allgemein"
#: searx/webapp.py:125
msgid "music"
msgstr "Musik"
#: searx/webapp.py:126
msgid "social media"
msgstr "Soziale Medien"
#: searx/webapp.py:127
msgid "images"
msgstr "Fotos"
#: searx/webapp.py:128
msgid "videos"
msgstr "Videos"
#: searx/webapp.py:129
msgid "it"
msgstr "IT"
#: searx/webapp.py:130
msgid "news"
msgstr "Nachrichten"
#: searx/webapp.py:131
msgid "map"
msgstr "Karten"
#: searx/webapp.py:132
msgid "science"
msgstr "Wissenschaft"
#: searx/webapp.py:384 searx/webapp.py:594
msgid "Invalid settings, please edit your preferences"
msgstr "Ungültige Auswahl, bitte überprüfen Sie die Einstellungen"
#: searx/webapp.py:425
msgid "search error"
msgstr "Fehler bei der Suche"
#: searx/webapp.py:467
msgid "{minutes} minute(s) ago"
msgstr "vor {minutes} Minute(n)"
#: searx/webapp.py:469
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "vor {hours} Stunde(n). {minutes} Minute(n)"
#: searx/answerers/random/answerer.py:48
msgid "Random value generator"
msgstr "Zufallswertgenerator"
#: searx/answerers/random/answerer.py:49
msgid "Generate different random values"
msgstr "Zufallswerte generieren"
#: searx/answerers/statistics/answerer.py:49
msgid "Statistics functions"
msgstr "Statistik-Funktionen"
#: searx/answerers/statistics/answerer.py:50
msgid "Compute {functions} of the arguments"
msgstr "{functions} der Argumente berechnen"
#: searx/engines/__init__.py:192
msgid "Engine time (sec)"
msgstr "Zeitbedarf (s)"
#: searx/engines/__init__.py:196
msgid "Page loads (sec)"
msgstr "Ladezeit (s)"
#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
msgid "Number of results"
msgstr "Anzahl Ergebnisse"
#: searx/engines/__init__.py:204
msgid "Scores"
msgstr "Punktwerte"
#: searx/engines/__init__.py:208
msgid "Scores per result"
msgstr "Punktwerte pro Ergebnis"
#: searx/engines/__init__.py:212
msgid "Errors"
msgstr "Fehler"
#: searx/engines/pdbe.py:87
msgid "{title}&nbsp;(OBSOLETE)"
msgstr "{title}&nbsp;(OBSOLET)"
#: searx/engines/pdbe.py:91
msgid "This entry has been superseded by"
msgstr "Dieser Eintrag wurde ersetzt durch"
#: searx/plugins/doai_rewrite.py:7
msgid "DOAI rewrite"
msgstr "DOAI-Umgehung"
#: searx/plugins/doai_rewrite.py:8
msgid ""
"Avoid paywalls by redirecting to open-access versions of publications when "
"available"
msgstr "Paywalls umgehen, indem wenn möglich auf Open-Access-Versionen von Publikationen umgeleitet wird"
#: searx/plugins/https_rewrite.py:29
msgid "Rewrite HTTP links to HTTPS if possible"
msgstr "Umschreiben von HTTP-Links nach HTTPS, wenn möglich"
#: searx/plugins/infinite_scroll.py:3
msgid "Infinite scroll"
msgstr "Unbegrenztes Scrollen"
#: searx/plugins/infinite_scroll.py:4
msgid "Automatically load next page when scrolling to bottom of current page"
msgstr "Nächste Seite automatisch laden, wenn zum Seitenende gescrollt wird"
#: searx/plugins/open_results_on_new_tab.py:18
#: searx/templates/oscar/preferences.html:113
msgid "Open result links on new browser tabs"
msgstr "Öffne Links in einem neuen Browser-Tab"
#: searx/plugins/open_results_on_new_tab.py:19
msgid ""
"Results are opened in the same window by default. This plugin overwrites the"
" default behaviour to open links on new tabs/windows. (JavaScript required)"
msgstr "Suchergebnisse werden standardmäßig im gleichen Fenster geöffnet. Dieses Plug-in überschreibt dieses Standardverhalten und öffnet Links in neuen Tabs/Fenstern (benötigt JavaScript)."
#: searx/plugins/search_on_category_select.py:18
msgid "Search on category select"
msgstr "Suchen nach Kategorie"
#: searx/plugins/search_on_category_select.py:19
msgid ""
"Perform search immediately if a category selected. Disable to select "
"multiple categories. (JavaScript required)"
msgstr "Suche sofort durchführen, wenn eine Kategorie ausgewählt wird. Deaktivieren Sie diese Option, um mehrere Kategorien auswählen zu können (benötigt JavaScript)."
#: searx/plugins/self_info.py:20
msgid ""
"Displays your IP if the query is \"ip\" and your user agent if the query "
"contains \"user agent\"."
msgstr "Zeigt Ihre IP-Adresse an, wenn \"ip\" als Suchanfrage eingegeben wird und den User Agent bzw. das verwendete Client-Programm, wenn die Suchanfrage den Ausdruck \"user agent\" enthält."
#: searx/plugins/tracker_url_remover.py:26
msgid "Tracker URL remover"
msgstr "Tracking-URLs bereinigen"
#: searx/plugins/tracker_url_remover.py:27
msgid "Remove trackers arguments from the returned URL"
msgstr "Tracker-Argumente der erhaltenen URL entfernen"
#: searx/plugins/vim_hotkeys.py:3
msgid "Vim-like hotkeys"
msgstr "Vim-ähnliche Hotkeys"
#: searx/plugins/vim_hotkeys.py:4
msgid ""
"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
"\"h\" key on main or result page to get help."
msgstr "Durch Suchergebnisse navigieren mit Vim-ähnlichen Hotkeys (benötigt JavaScript). \"h\" drücken auf der Hauptseite oder der Ergebnisseite, um Hilfe zu erhalten."
#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
msgid "Page not found"
msgstr "Seite nicht gefunden"
#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
#, python-format
msgid "Go to %(search_page)s."
msgstr "Gehe zu %(search_page)s."
#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
msgid "search page"
msgstr "Seite durchsuchen"
#: searx/templates/courgette/index.html:9
#: searx/templates/courgette/index.html:13
#: searx/templates/courgette/results.html:5
#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
#: searx/templates/oscar/navbar.html:12
#: searx/templates/oscar/preferences.html:3
#: searx/templates/pix-art/index.html:8
msgid "preferences"
msgstr "Einstellungen"
#: searx/templates/courgette/index.html:11
#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
msgid "about"
msgstr "Über uns"
#: searx/templates/courgette/preferences.html:5
#: searx/templates/legacy/preferences.html:5
#: searx/templates/oscar/preferences.html:7
#: searx/templates/pix-art/preferences.html:5
msgid "Preferences"
msgstr "Einstellungen"
#: searx/templates/courgette/preferences.html:9
#: searx/templates/legacy/preferences.html:9
#: searx/templates/oscar/preferences.html:32
#: searx/templates/oscar/preferences.html:34
msgid "Default categories"
msgstr "Standardkategorien"
#: searx/templates/courgette/preferences.html:13
#: searx/templates/legacy/preferences.html:14
#: searx/templates/oscar/preferences.html:40
#: searx/templates/pix-art/preferences.html:9
msgid "Search language"
msgstr "Suchsprache"
#: searx/templates/courgette/preferences.html:16
#: searx/templates/legacy/preferences.html:17
#: searx/templates/oscar/languages.html:6
#: searx/templates/pix-art/preferences.html:12
msgid "Default language"
msgstr "Standardsprache"
#: searx/templates/courgette/preferences.html:24
#: searx/templates/legacy/preferences.html:25
#: searx/templates/oscar/preferences.html:46
#: searx/templates/pix-art/preferences.html:20
msgid "Interface language"
msgstr "Sprache der Benutzeroberfläche"
#: searx/templates/courgette/preferences.html:34
#: searx/templates/legacy/preferences.html:35
#: searx/templates/oscar/preferences.html:56
msgid "Autocomplete"
msgstr "Autovervollständigen"
#: searx/templates/courgette/preferences.html:45
#: searx/templates/legacy/preferences.html:46
#: searx/templates/oscar/preferences.html:67
msgid "Image proxy"
msgstr "Proxy-Server für Bilder"
#: searx/templates/courgette/preferences.html:48
#: searx/templates/legacy/preferences.html:49
#: searx/templates/oscar/preferences.html:71
msgid "Enabled"
msgstr "Aktiviert"
#: searx/templates/courgette/preferences.html:49
#: searx/templates/legacy/preferences.html:50
#: searx/templates/oscar/preferences.html:72
msgid "Disabled"
msgstr "Deaktiviert"
#: searx/templates/courgette/preferences.html:54
#: searx/templates/legacy/preferences.html:55
#: searx/templates/oscar/preferences.html:76
#: searx/templates/pix-art/preferences.html:30
msgid "Method"
msgstr "Methode"
#: searx/templates/courgette/preferences.html:63
#: searx/templates/legacy/preferences.html:64
#: searx/templates/oscar/preferences.html:85
#: searx/templates/oscar/preferences.html:152
#: searx/templates/oscar/preferences.html:159
msgid "SafeSearch"
msgstr "SafeSearch"
#: searx/templates/courgette/preferences.html:66
#: searx/templates/legacy/preferences.html:67
#: searx/templates/oscar/preferences.html:89
msgid "Strict"
msgstr "Streng"
#: searx/templates/courgette/preferences.html:67
#: searx/templates/legacy/preferences.html:68
#: searx/templates/oscar/preferences.html:90
msgid "Moderate"
msgstr "Moderat"
#: searx/templates/courgette/preferences.html:68
#: searx/templates/legacy/preferences.html:69
#: searx/templates/oscar/preferences.html:91
msgid "None"
msgstr "Keine"
#: searx/templates/courgette/preferences.html:73
#: searx/templates/legacy/preferences.html:74
#: searx/templates/oscar/preferences.html:95
#: searx/templates/pix-art/preferences.html:39
msgid "Themes"
msgstr "Oberflächen"
#: searx/templates/courgette/preferences.html:83
msgid "Color"
msgstr "Farbe"
#: searx/templates/courgette/preferences.html:86
msgid "Blue (default)"
msgstr "Blau (Standard)"
#: searx/templates/courgette/preferences.html:87
msgid "Violet"
msgstr "Violett"
#: searx/templates/courgette/preferences.html:88
msgid "Green"
msgstr "Grün"
#: searx/templates/courgette/preferences.html:89
msgid "Cyan"
msgstr "Türkis"
#: searx/templates/courgette/preferences.html:90
msgid "Orange"
msgstr "Orange"
#: searx/templates/courgette/preferences.html:91
msgid "Red"
msgstr "Rot"
#: searx/templates/courgette/preferences.html:96
#: searx/templates/legacy/preferences.html:93
#: searx/templates/pix-art/preferences.html:49
msgid "Currently used search engines"
msgstr "Momentan genutzte Suchmaschinen"
#: searx/templates/courgette/preferences.html:100
#: searx/templates/legacy/preferences.html:97
#: searx/templates/oscar/preferences.html:149
#: searx/templates/oscar/preferences.html:162
#: searx/templates/pix-art/preferences.html:53
msgid "Engine name"
msgstr "Suchmaschinen-Name"
#: searx/templates/courgette/preferences.html:101
#: searx/templates/legacy/preferences.html:98
msgid "Category"
msgstr "Kategorie"
#: searx/templates/courgette/preferences.html:102
#: searx/templates/courgette/preferences.html:113
#: searx/templates/legacy/preferences.html:99
#: searx/templates/legacy/preferences.html:110
#: searx/templates/oscar/preferences.html:148
#: searx/templates/oscar/preferences.html:163
#: searx/templates/pix-art/preferences.html:54
#: searx/templates/pix-art/preferences.html:64
msgid "Allow"
msgstr "Zulassen"
#: searx/templates/courgette/preferences.html:102
#: searx/templates/courgette/preferences.html:114
#: searx/templates/legacy/preferences.html:99
#: searx/templates/legacy/preferences.html:111
#: searx/templates/pix-art/preferences.html:54
#: searx/templates/pix-art/preferences.html:65
msgid "Block"
msgstr "Blockieren"
#: searx/templates/courgette/preferences.html:122
#: searx/templates/legacy/preferences.html:119
#: searx/templates/oscar/preferences.html:282
#: searx/templates/pix-art/preferences.html:73
msgid ""
"These settings are stored in your cookies, this allows us not to store this "
"data about you."
msgstr "Diese Einstellungen werden in Ihren Cookies gespeichert, deshalb müssen wir diese persönlichen Daten nicht bei uns speichern."
#: searx/templates/courgette/preferences.html:124
#: searx/templates/legacy/preferences.html:121
#: searx/templates/oscar/preferences.html:284
#: searx/templates/pix-art/preferences.html:75
msgid ""
"These cookies serve your sole convenience, we don't use these cookies to "
"track you."
msgstr "Diese Cookies ermöglichen lediglich eine komfortablere Nutzung, wir verwenden diese Cookies nicht, um Sie zu tracken."
#: searx/templates/courgette/preferences.html:127
#: searx/templates/legacy/preferences.html:124
#: searx/templates/oscar/preferences.html:287
#: searx/templates/pix-art/preferences.html:78
msgid "save"
msgstr "speichern"
#: searx/templates/courgette/preferences.html:128
#: searx/templates/legacy/preferences.html:125
#: searx/templates/oscar/preferences.html:289
msgid "Reset defaults"
msgstr "Voreinstellungen wiederherstellen"
#: searx/templates/courgette/preferences.html:129
#: searx/templates/legacy/preferences.html:126
#: searx/templates/oscar/preferences.html:288
#: searx/templates/pix-art/preferences.html:79
msgid "back"
msgstr "zurück"
#: searx/templates/courgette/results.html:12
#: searx/templates/legacy/results.html:13
#: searx/templates/oscar/results.html:124
msgid "Search URL"
msgstr "Such-URL"
#: searx/templates/courgette/results.html:16
#: searx/templates/legacy/results.html:17
#: searx/templates/oscar/results.html:129
msgid "Download results"
msgstr "Suchergebnisse herunterladen"
#: searx/templates/courgette/results.html:34
#: searx/templates/legacy/results.html:35
msgid "Answers"
msgstr "Antworten"
#: searx/templates/courgette/results.html:42
#: searx/templates/legacy/results.html:43
#: searx/templates/oscar/results.html:104
msgid "Suggestions"
msgstr "Vorschläge"
#: searx/templates/courgette/results.html:70
#: searx/templates/legacy/results.html:81
#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
msgid "previous page"
msgstr "vorherige Seite"
#: searx/templates/courgette/results.html:81
#: searx/templates/legacy/results.html:92
#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
msgid "next page"
msgstr "nächste Seite"
#: searx/templates/courgette/search.html:3
#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
#: searx/templates/oscar/search_full.html:9
#: searx/templates/pix-art/search.html:3
msgid "Search for..."
msgstr "Suchen nach ..."
#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
msgid "Engine stats"
msgstr "Suchmaschinen-Statistiken"
#: searx/templates/courgette/result_templates/images.html:4
#: searx/templates/legacy/result_templates/images.html:4
#: searx/templates/pix-art/result_templates/images.html:4
msgid "original context"
msgstr "Ursprünglicher Kontext"
#: searx/templates/courgette/result_templates/torrent.html:7
#: searx/templates/legacy/result_templates/torrent.html:11
#: searx/templates/oscar/result_templates/torrent.html:6
msgid "Seeder"
msgstr "Seeder"
#: searx/templates/courgette/result_templates/torrent.html:7
#: searx/templates/legacy/result_templates/torrent.html:11
#: searx/templates/oscar/result_templates/torrent.html:6
msgid "Leecher"
msgstr "Leecher"
#: searx/templates/courgette/result_templates/torrent.html:9
#: searx/templates/legacy/result_templates/torrent.html:9
#: searx/templates/oscar/macros.html:24
msgid "magnet link"
msgstr "Magnet-Link"
#: searx/templates/courgette/result_templates/torrent.html:10
#: searx/templates/legacy/result_templates/torrent.html:10
#: searx/templates/oscar/macros.html:25
msgid "torrent file"
msgstr "Torrent-Datei"
#: searx/templates/legacy/categories.html:8
msgid "Click on the magnifier to perform search"
msgstr "Klicken Sie auf das Vergrößerungsglas, um die Suche zu starten"
#: searx/templates/legacy/preferences.html:84
#: searx/templates/oscar/preferences.html:112
msgid "Results on new tabs"
msgstr "Ergebnisse in neuen Tabs"
#: searx/templates/legacy/preferences.html:87
#: searx/templates/oscar/preferences.html:116
msgid "On"
msgstr "An"
#: searx/templates/legacy/preferences.html:88
#: searx/templates/oscar/preferences.html:117
msgid "Off"
msgstr "Aus"
#: searx/templates/legacy/result_templates/code.html:3
#: searx/templates/legacy/result_templates/default.html:3
#: searx/templates/legacy/result_templates/map.html:9
#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
msgid "cached"
msgstr "im Cache"
#: searx/templates/oscar/advanced.html:4
msgid "Advanced settings"
msgstr "Erweiterte Einstellungen"
#: searx/templates/oscar/base.html:62
#: searx/templates/oscar/messages/first_time.html:4
#: searx/templates/oscar/messages/no_results.html:5
#: searx/templates/oscar/messages/save_settings_successfull.html:5
#: searx/templates/oscar/messages/unknow_error.html:5
msgid "Close"
msgstr "Schließen"
#: searx/templates/oscar/base.html:64
msgid "Error!"
msgstr "Fehler!"
#: searx/templates/oscar/base.html:90
msgid "Powered by"
msgstr "Bereitgestellt von"
#: searx/templates/oscar/base.html:90
msgid "a privacy-respecting, hackable metasearch engine"
msgstr "eine die Privatsphäre respektierende, hackbare Meta-Suchmaschine"
#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
msgid "proxied"
msgstr "via Proxy-Server"
#: searx/templates/oscar/preferences.html:12
#: searx/templates/oscar/preferences.html:21
msgid "General"
msgstr "Allgemein"
#: searx/templates/oscar/preferences.html:13
#: searx/templates/oscar/preferences.html:133
msgid "Engines"
msgstr "Suchmaschinen"
#: searx/templates/oscar/preferences.html:14
#: searx/templates/oscar/preferences.html:204
msgid "Plugins"
msgstr "Plug-ins"
#: searx/templates/oscar/preferences.html:15
#: searx/templates/oscar/preferences.html:230
msgid "Answerers"
msgstr "Instant Answers/Sofortantworten"
#: searx/templates/oscar/preferences.html:16
#: searx/templates/oscar/preferences.html:257
msgid "Cookies"
msgstr "Cookies"
#: searx/templates/oscar/preferences.html:41
msgid "What language do you prefer for search?"
msgstr "Welche Sprache möchten Sie für die Suche verwenden?"
#: searx/templates/oscar/preferences.html:47
msgid "Change the language of the layout"
msgstr "Sprache des Layouts ändern"
#: searx/templates/oscar/preferences.html:57
msgid "Find stuff as you type"
msgstr "Bereits während der Eingabe suchen"
#: searx/templates/oscar/preferences.html:68
msgid "Proxying image results through searx"
msgstr "Bilder-Suchergebnisse über den searx-Proxy-Server laden"
#: searx/templates/oscar/preferences.html:77
msgid ""
"Change how forms are submited, <a "
"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
" rel=\"external\">learn more about request methods</a>"
msgstr "HTTP-Anfragemethode ändern <a href=\"https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP-Anfragemethoden\" rel=\"external\">(weiterführende Informationen zu HTTP-Anfragemethoden)</a>"
#: searx/templates/oscar/preferences.html:86
msgid "Filter content"
msgstr "Inhalte filtern"
#: searx/templates/oscar/preferences.html:96
msgid "Change searx layout"
msgstr "searx-Layout ändern"
#: searx/templates/oscar/preferences.html:105
#: searx/templates/oscar/preferences.html:110
msgid "Choose style for this theme"
msgstr "Stilrichtung für diese Benutzeroberfläche auswählen"
#: searx/templates/oscar/preferences.html:105
#: searx/templates/oscar/preferences.html:110
msgid "Style"
msgstr "Stilrichtung"
#: searx/templates/oscar/preferences.html:150
#: searx/templates/oscar/preferences.html:161
msgid "Shortcut"
msgstr "Kürzel"
#: searx/templates/oscar/preferences.html:151
#: searx/templates/oscar/preferences.html:160
msgid "Supports selected language"
msgstr "Unterstützt die ausgewähle Sprache"
#: searx/templates/oscar/preferences.html:153
msgid "Time range"
msgstr "Zeitraum"
#: searx/templates/oscar/preferences.html:154
#: searx/templates/oscar/preferences.html:158
msgid "Avg. time"
msgstr "Durchschn. Zeit"
#: searx/templates/oscar/preferences.html:155
#: searx/templates/oscar/preferences.html:157
msgid "Max time"
msgstr "Maximale Zeit"
#: searx/templates/oscar/preferences.html:233
msgid "This is the list of searx's instant answering modules."
msgstr "Auflistung der searx-Module für Sofortantworten:"
#: searx/templates/oscar/preferences.html:237
msgid "Name"
msgstr "Name"
#: searx/templates/oscar/preferences.html:238
msgid "Keywords"
msgstr "Schlüsselwörter"
#: searx/templates/oscar/preferences.html:239
msgid "Description"
msgstr "Beschreibung"
#: searx/templates/oscar/preferences.html:240
msgid "Examples"
msgstr "Beispiele"
#: searx/templates/oscar/preferences.html:260
msgid ""
"This is the list of cookies and their values searx is storing on your "
"computer."
msgstr "Hier werden die Cookies und die gespeicherten Cookie-Informationen aufgelistet, die searx auf Ihrem Computer speichert."
#: searx/templates/oscar/preferences.html:261
msgid "With that list, you can assess searx transparency."
msgstr "Mit Hilfe dieser Auflistung können Sie die Transparenz der searx-Suche einschätzen."
#: searx/templates/oscar/preferences.html:266
msgid "Cookie name"
msgstr "Cookie-Name"
#: searx/templates/oscar/preferences.html:267
msgid "Value"
msgstr "Wert"
#: searx/templates/oscar/results.html:7
msgid "Search results"
msgstr "Durchsuche Ergebnisse"
#: searx/templates/oscar/results.html:119
msgid "Links"
msgstr "Links"
#: searx/templates/oscar/search.html:6
#: searx/templates/oscar/search_full.html:11
msgid "Start search"
msgstr "Suche starten"
#: searx/templates/oscar/stats.html:2
msgid "stats"
msgstr "Statistiken"
#: searx/templates/oscar/time-range.html:3
msgid "Anytime"
msgstr "Beliebiger Zeitunkt"
#: searx/templates/oscar/time-range.html:6
msgid "Last day"
msgstr "Gestern"
#: searx/templates/oscar/time-range.html:9
msgid "Last week"
msgstr "Letzte Woche"
#: searx/templates/oscar/time-range.html:12
msgid "Last month"
msgstr "Letzter Monat"
#: searx/templates/oscar/time-range.html:15
msgid "Last year"
msgstr "Letztes Jahr"
#: searx/templates/oscar/messages/first_time.html:6
#: searx/templates/oscar/messages/no_data_available.html:3
msgid "Heads up!"
msgstr "Aufgepasst!"
#: searx/templates/oscar/messages/first_time.html:7
msgid "It look like you are using searx first time."
msgstr "Anscheinend benutzen Sie searx zum ersten Mal."
#: searx/templates/oscar/messages/no_cookies.html:3
msgid "Information!"
msgstr "Zur Information!"
#: searx/templates/oscar/messages/no_cookies.html:4
msgid "currently, there are no cookies defined."
msgstr "Zur Zeit sind keine Cookies definiert."
#: searx/templates/oscar/messages/no_data_available.html:4
msgid "There is currently no data available. "
msgstr "Zur Zeit sind keine Daten verfügbar."
#: searx/templates/oscar/messages/no_results.html:7
msgid "Sorry!"
msgstr "Entschuldigung!"
#: searx/templates/oscar/messages/no_results.html:8
msgid ""
"we didn't find any results. Please use another query or search in more "
"categories."
msgstr "Leider konnten wir keine Suchergebnisse finden. Bitte verwenden Sie eine andere Suchabfrage oder erweitern Sie die Suche auf mehr Kategorien."
#: searx/templates/oscar/messages/save_settings_successfull.html:7
msgid "Well done!"
msgstr "Gut gemacht!"
#: searx/templates/oscar/messages/save_settings_successfull.html:8
msgid "Settings saved successfully."
msgstr "Einstellungen erfolgreich gespeichert."
#: searx/templates/oscar/messages/unknow_error.html:7
msgid "Oh snap!"
msgstr "Hoppla!"
#: searx/templates/oscar/messages/unknow_error.html:8
msgid "Something went wrong."
msgstr "Ein Fehler ist aufgetreten."
#: searx/templates/oscar/result_templates/default.html:7
msgid "show media"
msgstr "Medien anzeigen"
#: searx/templates/oscar/result_templates/default.html:7
msgid "hide media"
msgstr "Medien verbergen"
#: searx/templates/oscar/result_templates/images.html:30
msgid "Get image"
msgstr "Bild herunterladen"
#: searx/templates/oscar/result_templates/images.html:33
msgid "View source"
msgstr "Quelle anzeigen"
#: searx/templates/oscar/result_templates/map.html:7
msgid "show map"
msgstr "Karte anzeigen"
#: searx/templates/oscar/result_templates/map.html:7
msgid "hide map"
msgstr "Karte verbergen"
#: searx/templates/oscar/result_templates/map.html:11
msgid "show details"
msgstr "Details anzeigen"
#: searx/templates/oscar/result_templates/map.html:11
msgid "hide details"
msgstr "Details verbergen"
#: searx/templates/oscar/result_templates/torrent.html:7
msgid "Filesize"
msgstr "Dateigröße"
#: searx/templates/oscar/result_templates/torrent.html:9
msgid "Bytes"
msgstr "Bytes"
#: searx/templates/oscar/result_templates/torrent.html:10
msgid "kiB"
msgstr "kiB"
#: searx/templates/oscar/result_templates/torrent.html:11
msgid "MiB"
msgstr "MiB"
#: searx/templates/oscar/result_templates/torrent.html:12
msgid "GiB"
msgstr "GiB"
#: searx/templates/oscar/result_templates/torrent.html:13
msgid "TiB"
msgstr "TiB"
#: searx/templates/oscar/result_templates/torrent.html:15
msgid "Number of Files"
msgstr "Anzahl Dateien"
#: searx/templates/oscar/result_templates/videos.html:7
msgid "show video"
msgstr "Video anzeigen"
#: searx/templates/oscar/result_templates/videos.html:7
msgid "hide video"
msgstr "Video verbergen"
#: searx/templates/pix-art/results.html:28
msgid "Load more..."
msgstr "Mehr anzeigen ..."

View File

@ -1,4 +1,6 @@
import csv import csv
import hashlib
import hmac
import os import os
import re import re
@ -290,6 +292,15 @@ def convert_str_to_int(number_str):
return 0 return 0
# convert a variable to integer or return 0 if it's not a number
def int_or_zero(num):
if isinstance(num, list):
if len(num) < 1:
return 0
num = num[0]
return convert_str_to_int(num)
def is_valid_lang(lang): def is_valid_lang(lang):
is_abbr = (len(lang) == 2) is_abbr = (len(lang) == 2)
if is_abbr: if is_abbr:
@ -312,3 +323,10 @@ def load_module(filename, module_dir):
module = load_source(modname, filepath) module = load_source(modname, filepath)
module.name = modname module.name = modname
return module return module
def new_hmac(secret_key, url):
if sys.version_info[0] == 2:
return hmac.new(bytes(secret_key), url, hashlib.sha256).hexdigest()
else:
return hmac.new(bytes(secret_key, 'utf-8'), url, hashlib.sha256).hexdigest()

View File

@ -69,6 +69,7 @@ from searx.plugins import plugins
from searx.preferences import Preferences, ValidationException from searx.preferences import Preferences, ValidationException
from searx.answerers import answerers from searx.answerers import answerers
from searx.url_utils import urlencode, urlparse, urljoin from searx.url_utils import urlencode, urlparse, urljoin
from searx.utils import new_hmac
# check if the pyopenssl package is installed. # check if the pyopenssl package is installed.
# It is needed for SSL connection without trouble, see #298 # It is needed for SSL connection without trouble, see #298
@ -290,7 +291,7 @@ def image_proxify(url):
if settings.get('result_proxy'): if settings.get('result_proxy'):
return proxify(url) return proxify(url)
h = hmac.new(settings['server']['secret_key'], url.encode('utf-8'), hashlib.sha256).hexdigest() h = new_hmac(settings['server']['secret_key'], url.encode('utf-8'))
return '{0}?{1}'.format(url_for('image_proxy'), return '{0}?{1}'.format(url_for('image_proxy'),
urlencode(dict(url=url.encode('utf-8'), h=h))) urlencode(dict(url=url.encode('utf-8'), h=h)))
@ -704,7 +705,7 @@ def image_proxy():
if not url: if not url:
return '', 400 return '', 400
h = hmac.new(settings['server']['secret_key'], url, hashlib.sha256).hexdigest() h = new_hmac(settings['server']['secret_key'], url)
if h != request.args.get('h'): if h != request.args.get('h'):
return '', 400 return '', 400
@ -731,7 +732,7 @@ def image_proxy():
logger.debug('image-proxy: wrong content-type: {0}'.format(resp.headers.get('content-type'))) logger.debug('image-proxy: wrong content-type: {0}'.format(resp.headers.get('content-type')))
return '', 400 return '', 400
img = '' img = b''
chunk_counter = 0 chunk_counter = 0
for chunk in resp.iter_content(1024 * 1024): for chunk in resp.iter_content(1024 * 1024):
@ -792,7 +793,8 @@ def opensearch():
@app.route('/favicon.ico') @app.route('/favicon.ico')
def favicon(): def favicon():
return send_from_directory(os.path.join(app.root_path, return send_from_directory(os.path.join(app.root_path,
'static/themes', static_path,
'themes',
get_current_theme_name(), get_current_theme_name(),
'img'), 'img'),
'favicon.png', 'favicon.png',

View File

@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
from collections import defaultdict
import mock
from searx.engines import base
from searx.testing import SearxTestCase
class TestBaseEngine(SearxTestCase):
def test_request(self):
query = 'test_query'
dicto = defaultdict(dict)
dicto['pageno'] = 1
params = base.request(query, dicto)
self.assertIn('url', params)
self.assertIn('base-search.net', params['url'])
def test_response(self):
self.assertRaises(AttributeError, base.response, None)
self.assertRaises(AttributeError, base.response, [])
self.assertRaises(AttributeError, base.response, '')
self.assertRaises(AttributeError, base.response, '[]')
response = mock.Mock(text='<response></response>')
self.assertEqual(base.response(response), [])
xml_mock = """<?xml version="1.0"?>
<response>
<lst name="responseHeader">
<int name="status">0</int>
<int name="QTime">1</int>
</lst>
<result name="response" numFound="1" start="0">
<doc>
<date name="dchdate">2000-01-01T01:01:01Z</date>
<str name="dcdocid">1</str>
<str name="dccontinent">cna</str>
<str name="dccountry">us</str>
<str name="dccollection">ftciteseerx</str>
<str name="dcprovider">CiteSeerX</str>
<str name="dctitle">Science and more</str>
<arr name="dccreator">
<str>Someone</str>
</arr>
<arr name="dcperson">
<str>Someone</str>
</arr>
<arr name="dcsubject">
<str>Science and more</str>
</arr>
<str name="dcdescription">Science, and even more.</str>
<arr name="dccontributor">
<str>The neighbour</str>
</arr>
<str name="dcdate">2001</str>
<int name="dcyear">2001</int>
<arr name="dctype">
<str>text</str>
</arr>
<arr name="dctypenorm">
<str>1</str>
</arr>
<arr name="dcformat">
<str>application/pdf</str>
</arr>
<arr name="dccontenttype">
<str>application/pdf</str>
</arr>
<arr name="dcidentifier">
<str>http://example.org/</str>
</arr>
<str name="dclink">http://example.org</str>
<str name="dcsource">http://example.org</str>
<arr name="dclanguage">
<str>en</str>
</arr>
<str name="dcrights">Under the example.org licence</str>
<int name="dcoa">1</int>
<arr name="dclang">
<str>eng</str>
</arr>
</doc>
</result>
</response>"""
response = mock.Mock(text=xml_mock.encode('utf-8'))
results = base.response(response)
self.assertEqual(type(results), list)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['title'], 'Science and more')
self.assertEqual(results[0]['content'], 'Science, and even more.')

View File

@ -8,10 +8,12 @@ from searx.testing import SearxTestCase
class TestBingImagesEngine(SearxTestCase): class TestBingImagesEngine(SearxTestCase):
def test_request(self): def test_request(self):
bing_images.supported_languages = ['fr-FR', 'en-US']
query = 'test_query' query = 'test_query'
dicto = defaultdict(dict) dicto = defaultdict(dict)
dicto['pageno'] = 1 dicto['pageno'] = 1
dicto['language'] = 'fr_FR' dicto['language'] = 'fr-FR'
dicto['safesearch'] = 1 dicto['safesearch'] = 1
dicto['time_range'] = '' dicto['time_range'] = ''
params = bing_images.request(query, dicto) params = bing_images.request(query, dicto)
@ -19,12 +21,19 @@ class TestBingImagesEngine(SearxTestCase):
self.assertTrue(query in params['url']) self.assertTrue(query in params['url'])
self.assertTrue('bing.com' in params['url']) self.assertTrue('bing.com' in params['url'])
self.assertTrue('SRCHHPGUSR' in params['cookies']) self.assertTrue('SRCHHPGUSR' in params['cookies'])
self.assertTrue('fr' in params['cookies']['SRCHHPGUSR']) self.assertTrue('DEMOTE' in params['cookies']['SRCHHPGUSR'])
self.assertTrue('_EDGE_S' in params['cookies'])
self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
dicto['language'] = 'fr'
params = bing_images.request(query, dicto)
self.assertTrue('_EDGE_S' in params['cookies'])
self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
dicto['language'] = 'all' dicto['language'] = 'all'
params = bing_images.request(query, dicto) params = bing_images.request(query, dicto)
self.assertIn('SRCHHPGUSR', params['cookies']) self.assertTrue('_EDGE_S' in params['cookies'])
self.assertIn('en', params['cookies']['SRCHHPGUSR']) self.assertTrue('en-us' in params['cookies']['_EDGE_S'])
def test_response(self): def test_response(self):
self.assertRaises(AttributeError, bing_images.response, None) self.assertRaises(AttributeError, bing_images.response, None)
@ -82,3 +91,28 @@ class TestBingImagesEngine(SearxTestCase):
self.assertEqual(results[0]['content'], '') self.assertEqual(results[0]['content'], '')
self.assertEqual(results[0]['thumbnail_src'], 'thumb_url') self.assertEqual(results[0]['thumbnail_src'], 'thumb_url')
self.assertEqual(results[0]['img_src'], 'img_url') self.assertEqual(results[0]['img_src'], 'img_url')
def test_fetch_supported_languages(self):
html = """
<div>
<div id="region-section-content">
<ul class="b_vList">
<li>
<a href="https://bing...&setmkt=de-DE&s...">Germany</a>
<a href="https://bing...&setmkt=nb-NO&s...">Norway</a>
</li>
</ul>
<ul class="b_vList">
<li>
<a href="https://bing...&setmkt=es-AR&s...">Argentina</a>
</li>
</ul>
</div>
</div>
"""
response = mock.Mock(text=html)
languages = list(bing_images._fetch_supported_languages(response))
self.assertEqual(len(languages), 3)
self.assertIn('de-DE', languages)
self.assertIn('no-NO', languages)
self.assertIn('es-AR', languages)

View File

@ -8,6 +8,8 @@ from searx.testing import SearxTestCase
class TestBingVideosEngine(SearxTestCase): class TestBingVideosEngine(SearxTestCase):
def test_request(self): def test_request(self):
bing_videos.supported_languages = ['fr-FR', 'en-US']
query = 'test_query' query = 'test_query'
dicto = defaultdict(dict) dicto = defaultdict(dict)
dicto['pageno'] = 1 dicto['pageno'] = 1

View File

@ -1,71 +0,0 @@
from collections import defaultdict
import mock
from searx.engines import blekko_images
from searx.testing import SearxTestCase
class TestBlekkoImagesEngine(SearxTestCase):
def test_request(self):
query = 'test_query'
dicto = defaultdict(dict)
dicto['pageno'] = 0
dicto['safesearch'] = 1
params = blekko_images.request(query, dicto)
self.assertIn('url', params)
self.assertIn(query, params['url'])
self.assertIn('blekko.com', params['url'])
self.assertIn('page', params['url'])
dicto['pageno'] = 1
params = blekko_images.request(query, dicto)
self.assertNotIn('page', params['url'])
def test_response(self):
self.assertRaises(AttributeError, blekko_images.response, None)
self.assertRaises(AttributeError, blekko_images.response, [])
self.assertRaises(AttributeError, blekko_images.response, '')
self.assertRaises(AttributeError, blekko_images.response, '[]')
response = mock.Mock(text='[]')
self.assertEqual(blekko_images.response(response), [])
json = """
[
{
"c": 1,
"page_url": "http://result_url.html",
"title": "Photo title",
"tn_url": "http://ts1.mm.bing.net/th?id=HN.608050619474382748&pid=15.1",
"url": "http://result_image.jpg"
},
{
"c": 2,
"page_url": "http://companyorange.simpsite.nl/OSM",
"title": "OSM",
"tn_url": "http://ts2.mm.bing.net/th?id=HN.608048068264919461&pid=15.1",
"url": "http://simpsite.nl/userdata2/58985/Home/OSM.bmp"
},
{
"c": 3,
"page_url": "http://invincible.webklik.nl/page/osm",
"title": "OSM",
"tn_url": "http://ts1.mm.bing.net/th?id=HN.608024514657649476&pid=15.1",
"url": "http://www.webklik.nl/user_files/2009_09/65324/osm.gif"
},
{
"c": 4,
"page_url": "http://www.offshorenorway.no/event/companyDetail/id/12492",
"title": "Go to OSM Offshore AS homepage",
"tn_url": "http://ts2.mm.bing.net/th?id=HN.608054265899847285&pid=15.1",
"url": "http://www.offshorenorway.no/firmalogo/OSM-logo.png"
}
]
"""
response = mock.Mock(text=json)
results = blekko_images.response(response)
self.assertEqual(type(results), list)
self.assertEqual(len(results), 4)
self.assertEqual(results[0]['title'], 'Photo title')
self.assertEqual(results[0]['url'], 'http://result_url.html')
self.assertEqual(results[0]['img_src'], 'http://result_image.jpg')

View File

@ -40,9 +40,6 @@ class TestFarooEngine(SearxTestCase):
response = mock.Mock(text='{"data": []}') response = mock.Mock(text='{"data": []}')
self.assertEqual(faroo.response(response), []) self.assertEqual(faroo.response(response), [])
response = mock.Mock(text='{"data": []}', status_code=401)
self.assertRaises(Exception, faroo.response, response)
response = mock.Mock(text='{"data": []}', status_code=429) response = mock.Mock(text='{"data": []}', status_code=429)
self.assertRaises(Exception, faroo.response, response) self.assertRaises(Exception, faroo.response, response)
@ -98,14 +95,14 @@ class TestFarooEngine(SearxTestCase):
response = mock.Mock(text=json) response = mock.Mock(text=json)
results = faroo.response(response) results = faroo.response(response)
self.assertEqual(type(results), list) self.assertEqual(type(results), list)
self.assertEqual(len(results), 4) self.assertEqual(len(results), 3)
self.assertEqual(results[0]['title'], 'This is the title') self.assertEqual(results[0]['title'], 'This is the title')
self.assertEqual(results[0]['url'], 'http://this.is.the.url/') self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
self.assertEqual(results[0]['content'], 'This is the content') self.assertEqual(results[0]['content'], 'This is the content')
self.assertEqual(results[1]['title'], 'This is the title2') self.assertEqual(results[1]['title'], 'This is the title2')
self.assertEqual(results[1]['url'], 'http://this.is.the.url2/') self.assertEqual(results[1]['url'], 'http://this.is.the.url2/')
self.assertEqual(results[1]['content'], 'This is the content2') self.assertEqual(results[1]['content'], 'This is the content2')
self.assertEqual(results[3]['img_src'], 'http://upload.wikimedia.org/optimized.jpg') self.assertEqual(results[2]['thumbnail'], 'http://upload.wikimedia.org/optimized.jpg')
json = """ json = """
{} {}

File diff suppressed because one or more lines are too long

View File

@ -13,38 +13,92 @@ class TestNyaaEngine(SearxTestCase):
params = nyaa.request(query, dic) params = nyaa.request(query, dic)
self.assertTrue('url' in params) self.assertTrue('url' in params)
self.assertTrue(query in params['url']) self.assertTrue(query in params['url'])
self.assertTrue('nyaa.se' in params['url']) self.assertTrue('nyaa.si' in params['url'])
def test_response(self): def test_response(self):
resp = mock.Mock(text='<html></html>') resp = mock.Mock(text='<html></html>')
self.assertEqual(nyaa.response(resp), []) self.assertEqual(nyaa.response(resp), [])
html = """ html = """
<table class="tlist"> <table class="table table-bordered table-hover table-striped torrent-list">
<tbody> <thead>
<tr class="trusted tlistrow"> <tr>
<td class="tlisticon"> <th class="hdr-category text-center" style="width:80px;">
<a href="//www.nyaa.se" title="English-translated Anime"> <div>Category</div>
<img src="//files.nyaa.se" alt="English-translated Anime"> </th>
</a> <th class="hdr-name" style="width:auto;">
</td> <div>Name</div>
<td class="tlistname"> </th>
<a href="//www.nyaa.se/?page3"> <th class="hdr-comments sorting text-center" title="Comments" style="width:50px;">
Sample torrent title <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=comments&amp;o=desc"></a>
</a> <i class="fa fa-comments-o"></i>
</td> </th>
<td class="tlistdownload"> <th class="hdr-link text-center" style="width:70px;">
<a href="//www.nyaa.se/?page_dl" title="Download"> <div>Link</div>
<img src="//files.nyaa.se/www-dl.png" alt="DL"> </th>
</a> <th class="hdr-size sorting text-center" style="width:100px;">
</td> <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=size&amp;o=desc"></a>
<td class="tlistsize">10 MiB</td> <div>Size</div>
<td class="tlistsn">1</td> </th>
<td class="tlistln">3</td> <th class="hdr-date sorting_desc text-center" title="In local time" style="width:140px;">
<td class="tlistdn">666</td> <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=id&amp;o=asc"></a>
<td class="tlistmn">0</td> <div>Date</div>
</tr> </th>
</tbody> <th class="hdr-seeders sorting text-center" title="Seeders" style="width:50px;">
<a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=seeders&amp;o=desc"></a>
<i class="fa fa-arrow-up" aria-hidden="true"></i>
</th>
<th class="hdr-leechers sorting text-center" title="Leechers" style="width:50px;">
<a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=leechers&amp;o=desc"></a>
<i class="fa fa-arrow-down" aria-hidden="true"></i>
</th>
<th class="hdr-downloads sorting text-center" title="Completed downloads" style="width:50px;">
<a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=downloads&amp;o=desc"></a>
<i class="fa fa-check" aria-hidden="true"></i>
</th>
</tr>
</thead>
<tbody>
<tr class="default">
<td style="padding:0 4px;">
<a href="/?c=1_2" title="Anime - English-translated">
<img src="/static/img/icons/nyaa/1_2.png" alt="Anime - English-translated">
</a>
</td>
<td colspan="2">
<a href="/view/1" title="Sample title 1">Sample title 1</a>
</td>
<td class="text-center" style="white-space: nowrap;">
<a href="/download/1.torrent"><i class="fa fa-fw fa-download"></i></a>
<a href="magnet:?xt=urn:btih:2"><i class="fa fa-fw fa-magnet"></i></a>
</td>
<td class="text-center">723.7 MiB</td>
<td class="text-center" data-timestamp="1503307456" title="1 week 3
days 9 hours 44 minutes 39 seconds ago">2017-08-21 11:24</td>
<td class="text-center" style="color: green;">1</td>
<td class="text-center" style="color: red;">3</td>
<td class="text-center">12</td>
</tr>
<tr class="default">
<td style="padding:0 4px;">
<a href="/?c=1_2" title="Anime - English-translated">
<img src="/static/img/icons/nyaa/1_2.png" alt="Anime - English-translated">
</a>
</td>
<td colspan="2">
<a href="/view/2" title="Sample title 2">Sample title 2</a>
</td>
<td class="text-center" style="white-space: nowrap;">
<a href="magnet:?xt=urn:btih:2"><i class="fa fa-fw fa-magnet"></i></a>
</td>
<td class="text-center">8.2 GiB</td>
<td class="text-center" data-timestamp="1491608400" title="4 months 3
weeks 4 days 19 hours 28 minutes 55 seconds ago">2017-04-08 01:40</td>
<td class="text-center" style="color: green;">10</td>
<td class="text-center" style="color: red;">1</td>
<td class="text-center">206</td>
</tr>
</tbody>
</table> </table>
""" """
@ -52,15 +106,19 @@ class TestNyaaEngine(SearxTestCase):
results = nyaa.response(resp) results = nyaa.response(resp)
self.assertEqual(type(results), list) self.assertEqual(type(results), list)
self.assertEqual(len(results), 1) self.assertEqual(len(results), 2)
r = results[0] r = results[0]
self.assertTrue(r['url'].find('www.nyaa.se/?page3') >= 0) self.assertTrue(r['url'].find('1') >= 0)
self.assertTrue(r['torrentfile'].find('www.nyaa.se/?page_dl') >= 0) self.assertTrue(r['torrentfile'].find('1.torrent') >= 0)
self.assertTrue(r['content'].find('English-translated Anime') >= 0) self.assertTrue(r['content'].find('Anime - English-translated') >= 0)
self.assertTrue(r['content'].find('Downloaded 666 times.') >= 0) self.assertTrue(r['content'].find('Downloaded 12 times.') >= 0)
self.assertEqual(r['title'], 'Sample torrent title') self.assertEqual(r['title'], 'Sample title 1')
self.assertEqual(r['seed'], 1) self.assertEqual(r['seed'], 1)
self.assertEqual(r['leech'], 3) self.assertEqual(r['leech'], 3)
self.assertEqual(r['filesize'], 10 * 1024 * 1024) self.assertEqual(r['filesize'], 723700000)
r = results[1]
self.assertTrue(r['url'].find('2') >= 0)
self.assertTrue(r['magnetlink'].find('magnet:') >= 0)

View File

@ -139,9 +139,9 @@ class TestSwisscowsEngine(SearxTestCase):
<div id="regions-popup"> <div id="regions-popup">
<div> <div>
<ul> <ul>
<li><a data-val="browser"></a></li> <li><a data-search-language="browser"></a></li>
<li><a data-val="de-CH"></a></li> <li><a data-search-language="de-CH"></a></li>
<li><a data-val="fr-CH"></a></li> <li><a data-search-language="fr-CH"></a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -14,7 +14,7 @@ class TestTorrentzEngine(SearxTestCase):
params = torrentz.request(query, dic) params = torrentz.request(query, dic)
self.assertTrue('url' in params) self.assertTrue('url' in params)
self.assertTrue(query in params['url']) self.assertTrue(query in params['url'])
self.assertTrue('torrentz.eu' in params['url']) self.assertTrue('torrentz2.eu' in params['url'])
def test_response(self): def test_response(self):
resp = mock.Mock(text='<html></html>') resp = mock.Mock(text='<html></html>')
@ -30,13 +30,11 @@ class TestTorrentzEngine(SearxTestCase):
books ebooks books ebooks
</dt> </dt>
<dd> <dd>
<span class="v">1</span> <span>1</span>
<span class="a"> <span title="1503595924">5 hours</span>
<span title="Sun, 22 Nov 2015 03:01:42">4 months</span> <span>30 MB</span>
</span> <span>14</span>
<span class="s">30 MB</span> <span>1</span>
<span class="u">14</span>
<span class="d">1</span>
</dd> </dd>
</dl> </dl>
@ -48,13 +46,11 @@ class TestTorrentzEngine(SearxTestCase):
books ebooks books ebooks
</dt> </dt>
<dd> <dd>
<span class="v">1</span> <span>1</span>
<span class="a"> <span title="1503595924 aaa">5 hours</span>
<span title="Sun, 2124091j0j190gm42">4 months</span> <span>30MB</span>
</span> <span>5,555</span>
<span class="s">30MB</span> <span>1,234,567</span>
<span class="u">5,555</span>
<span class="d">1,234,567</span>
</dd> </dd>
</dl> </dl>
</div> </div>
@ -68,10 +64,10 @@ class TestTorrentzEngine(SearxTestCase):
# testing against the first result # testing against the first result
r = results[0] r = results[0]
self.assertEqual(r['url'], 'https://torrentz.eu/4362e08b1d80e1820fb2550b752f9f3126fe76d6') self.assertEqual(r['url'], 'https://torrentz2.eu/4362e08b1d80e1820fb2550b752f9f3126fe76d6')
self.assertEqual(r['title'], 'Completely valid info books ebooks') self.assertEqual(r['title'], 'Completely valid info books ebooks')
# 22 Nov 2015 03:01:42 # 22 Nov 2015 03:01:42
self.assertEqual(r['publishedDate'], datetime(2015, 11, 22, 3, 1, 42)) self.assertEqual(r['publishedDate'], datetime.fromtimestamp(1503595924))
self.assertEqual(r['seed'], 14) self.assertEqual(r['seed'], 14)
self.assertEqual(r['leech'], 1) self.assertEqual(r['leech'], 1)
self.assertEqual(r['filesize'], 30 * 1024 * 1024) self.assertEqual(r['filesize'], 30 * 1024 * 1024)
@ -79,7 +75,7 @@ class TestTorrentzEngine(SearxTestCase):
# testing against the second result # testing against the second result
r = results[1] r = results[1]
self.assertEqual(r['url'], 'https://torrentz.eu/poaskdpokaspod') self.assertEqual(r['url'], 'https://torrentz2.eu/poaskdpokaspod')
self.assertEqual(r['title'], 'Invalid hash and date and filesize books ebooks') self.assertEqual(r['title'], 'Invalid hash and date and filesize books ebooks')
self.assertEqual(r['seed'], 5555) self.assertEqual(r['seed'], 5555)
self.assertEqual(r['leech'], 1234567) self.assertEqual(r['leech'], 1234567)

View File

@ -8,13 +8,13 @@
# are written in current directory to avoid overwriting in case something goes wrong. # are written in current directory to avoid overwriting in case something goes wrong.
from requests import get from requests import get
from urllib import urlencode
from lxml.html import fromstring from lxml.html import fromstring
from json import loads, dumps from json import loads, dump
import io import io
from sys import path from sys import path
path.append('../searx') # noqa path.append('../searx') # noqa
from searx import settings from searx import settings
from searx.url_utils import urlencode
from searx.engines import initialize_engines, engines from searx.engines import initialize_engines, engines
# Geonames API for country names. # Geonames API for country names.
@ -70,7 +70,7 @@ def get_country_name(locale):
json = loads(response.text) json = loads(response.text)
content = json.get('geonames', None) content = json.get('geonames', None)
if content is None or len(content) != 1: if content is None or len(content) != 1:
print "No country name found for " + locale[0] + "-" + locale[1] print("No country name found for " + locale[0] + "-" + locale[1])
return '' return ''
return content[0].get('countryName', '') return content[0].get('countryName', '')
@ -84,11 +84,11 @@ def fetch_supported_languages():
try: try:
engines_languages[engine_name] = engines[engine_name].fetch_supported_languages() engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
except Exception as e: except Exception as e:
print e print(e)
# write json file # write json file
with io.open(engines_languages_file, "w", encoding="utf-8") as f: with io.open(engines_languages_file, "w", encoding="utf-8") as f:
f.write(unicode(dumps(engines_languages, ensure_ascii=False, encoding="utf-8"))) dump(engines_languages, f, ensure_ascii=False)
# Join all language lists. # Join all language lists.
@ -97,7 +97,7 @@ def join_language_lists():
global languages global languages
# include wikipedia first for more accurate language names # include wikipedia first for more accurate language names
languages = {code: lang for code, lang languages = {code: lang for code, lang
in engines_languages['wikipedia'].iteritems() in engines_languages['wikipedia'].items()
if valid_code(code)} if valid_code(code)}
for engine_name in engines_languages: for engine_name in engines_languages:
@ -121,7 +121,7 @@ def join_language_lists():
# filter list to include only languages supported by most engines # filter list to include only languages supported by most engines
min_supported_engines = int(0.70 * len(engines_languages)) min_supported_engines = int(0.70 * len(engines_languages))
languages = {code: lang for code, lang languages = {code: lang for code, lang
in languages.iteritems() in languages.items()
if len(lang.get('counter', [])) >= min_supported_engines or if len(lang.get('counter', [])) >= min_supported_engines or
len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines} len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines}
@ -165,7 +165,7 @@ def filter_single_country_languages():
# Write languages.py. # Write languages.py.
def write_languages_file(): def write_languages_file():
new_file = open(languages_file, 'w') new_file = open(languages_file, 'wb')
file_content = '# -*- coding: utf-8 -*-\n'\ file_content = '# -*- coding: utf-8 -*-\n'\
+ '# list of language codes\n'\ + '# list of language codes\n'\
+ '# this file is generated automatically by utils/update_search_languages.py\n'\ + '# this file is generated automatically by utils/update_search_languages.py\n'\

View File

@ -7,9 +7,9 @@
SEARX_DIR='searx' SEARX_DIR='searx'
pybabel extract -F babel.cfg -o messages.pot $SEARX_DIR pybabel extract -F babel.cfg -o messages.pot "$SEARX_DIR"
for f in `ls $SEARX_DIR'/translations/'`; do for f in `ls "$SEARX_DIR"'/translations/'`; do
pybabel update -N -i messages.pot -d $SEARX_DIR'/translations/' -l $f pybabel update -N -i messages.pot -d "$SEARX_DIR"'/translations/' -l "$f"
done done
echo '[!] update done, edit .po files if required and run pybabel compile -d searx/translations/' echo '[!] update done, edit .po files if required and run pybabel compile -d searx/translations/'