Browse Source

Merge pull request #1 from asciimoo/master

-
Apply55gx 7 years ago
parent
commit
d800e3fcfa
49 changed files with 695 additions and 1458 deletions
  1. 31 0
      .codecov.yml
  2. 3 3
      .travis.yml
  3. 9 3
      README.rst
  4. 37 37
      manage.sh
  5. 1 0
      requirements-dev.txt
  6. 1 1
      requirements.txt
  7. 0 0
      searx/data/engines_languages.json
  8. 47 9
      searx/engines/bing_images.py
  9. 4 1
      searx/engines/bing_videos.py
  10. 0 70
      searx/engines/blekko_images.py
  11. 7 0
      searx/engines/digg.py
  12. 1 1
      searx/engines/duckduckgo.py
  13. 19 39
      searx/engines/faroo.py
  14. 0 62
      searx/engines/generalfile.py
  15. 6 2
      searx/engines/gigablast.py
  16. 2 2
      searx/engines/google_news.py
  17. 40 49
      searx/engines/nyaa.py
  18. 1 1
      searx/engines/swisscows.py
  19. 2 3
      searx/engines/tokyotoshokan.py
  20. 22 16
      searx/engines/torrentz.py
  21. 8 10
      searx/languages.py
  22. 28 28
      searx/settings.yml
  23. 0 0
      searx/static/themes/oscar/css/logicodev.min.css
  24. 0 0
      searx/static/themes/oscar/css/pointhi.min.css
  25. 1 1
      searx/static/themes/oscar/js/searx.min.js
  26. 2 0
      searx/static/themes/oscar/less/logicodev/oscar.less
  27. 3 0
      searx/static/themes/oscar/less/logicodev/preferences.less
  28. 2 0
      searx/static/themes/oscar/less/pointhi/oscar.less
  29. 3 0
      searx/static/themes/oscar/less/pointhi/preferences.less
  30. 11 11
      searx/templates/courgette/result_templates/code.html
  31. 11 11
      searx/templates/legacy/result_templates/code.html
  32. 99 88
      searx/templates/oscar/macros.html
  33. 17 14
      searx/templates/oscar/preferences.html
  34. 1 2
      searx/templates/simple/macros.html
  35. BIN
      searx/translations/de_DE/LC_MESSAGES/messages.mo
  36. 0 844
      searx/translations/de_DE/LC_MESSAGES/messages.po
  37. 18 0
      searx/utils.py
  38. 6 4
      searx/webapp.py
  39. 91 0
      tests/unit/engines/test_base.py
  40. 38 4
      tests/unit/engines/test_bing_images.py
  41. 2 0
      tests/unit/engines/test_bing_videos.py
  42. 0 71
      tests/unit/engines/test_blekko_images.py
  43. 2 5
      tests/unit/engines/test_faroo.py
  44. 0 1
      tests/unit/engines/test_google_news.py
  45. 91 33
      tests/unit/engines/test_nyaa.py
  46. 3 3
      tests/unit/engines/test_swisscows.py
  47. 14 18
      tests/unit/engines/test_torrentz.py
  48. 8 8
      utils/fetch_languages.py
  49. 3 3
      utils/update-translations.sh

+ 31 - 0
.codecov.yml

@@ -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

+ 3 - 3
.travis.yml

@@ -13,21 +13,21 @@ python:
 before_install:
   - "export DISPLAY=:99.0"
   - "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
   - ./manage.sh install_geckodriver ~/drivers
   - export PATH=~/drivers:$PATH
 install:
   - ./manage.sh npm_packages
   - ./manage.sh update_dev_packages
-  - pip install coveralls
+  - pip install codecov
 script:
   - ./manage.sh styles
   - ./manage.sh grunt_build
   - ./manage.sh tests
 after_success:
   - ./manage.sh py_test_coverage
-  - coveralls
+  - codecov
 notifications:
   irc:
     channels:

+ 9 - 3
README.rst

@@ -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.
 
-|Flattr searx|
+|OpenCollective searx backers|
+|OpenCollective searx sponsors|
 
 Installation
 ~~~~~~~~~~~~
@@ -41,5 +42,10 @@ More about searx
 -  `twitter <https://twitter.com/Searx_engine>`__
 -  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

+ 37 - 37
manage.sh

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

+ 1 - 0
requirements-dev.txt

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

+ 1 - 1
requirements.txt

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

File diff suppressed because it is too large
+ 0 - 0
searx/data/engines_languages.json


+ 47 - 9
searx/engines/bing_images.py

@@ -18,7 +18,6 @@
 from lxml import html
 from json import loads
 import re
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url
 from searx.url_utils import urlencode
 
 # engine dependent config
@@ -26,6 +25,8 @@ categories = ['images']
 paging = True
 safesearch = True
 time_range_support = True
+language_support = True
+supported_languages_url = 'https://www.bing.com/account/general'
 
 # search-url
 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)
 
 
+# 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
 def request(query, params):
     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(
         query=urlencode({'q': query}),
         offset=offset)
 
+    language = get_region_code(params['language'])
+
     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
     if params['time_range'] in time_range_dict:
@@ -106,3 +125,22 @@ def response(resp):
 
     # 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

+ 4 - 1
searx/engines/bing_videos.py

@@ -12,6 +12,7 @@
 
 from json import loads
 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.url_utils import urlencode
 
@@ -21,6 +22,7 @@ paging = True
 safesearch = True
 time_range_support = True
 number_of_results = 10
+language_support = True
 
 search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\
              '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')
 
     # 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
     params['url'] = search_url.format(query=urlencode({'q': query}),

+ 0 - 70
searx/engines/blekko_images.py

@@ -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

+ 7 - 0
searx/engines/digg.py

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

+ 1 - 1
searx/engines/duckduckgo.py

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

+ 19 - 39
searx/engines/faroo.py

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

+ 0 - 62
searx/engines/generalfile.py

@@ -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

+ 6 - 2
searx/engines/gigablast.py

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

+ 2 - 2
searx/engines/google_news.py

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

+ 40 - 49
searx/engines/nyaa.py

@@ -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
  @using-api    no
  @results      HTML
@@ -12,50 +12,25 @@
 from lxml import html
 from searx.engines.xpath import extract_text
 from searx.url_utils import urlencode
+from searx.utils import get_torrent_size, int_or_zero
 
 # engine dependent config
 categories = ['files', 'images', 'videos', 'music']
 paging = True
 
 # search-url
-base_url = 'http://www.nyaa.se/'
+base_url = 'http://www.nyaa.si/'
 search_url = base_url + '?page=search&{query}&offset={offset}'
 
 # xpath queries
-xpath_results = '//table[@class="tlist"]//tr[contains(@class, "tlistrow")]'
-xpath_category = './/td[@class="tlisticon"]/a'
-xpath_title = './/td[@class="tlistname"]/a'
-xpath_torrent_file = './/td[@class="tlistdownload"]/a'
-xpath_filesize = './/td[@class="tlistsize"]/text()'
-xpath_seeds = './/td[@class="tlistsn"]/text()'
-xpath_leeches = './/td[@class="tlistln"]/text()'
-xpath_downloads = './/td[@class="tlistdn"]/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()]
+xpath_results = '//table[contains(@class, "torrent-list")]//tr[not(th)]'
+xpath_category = './/td[1]/a[1]'
+xpath_title = './/td[2]/a[last()]'
+xpath_torrent_links = './/td[3]/a'
+xpath_filesize = './/td[4]/text()'
+xpath_seeds = './/td[6]/text()'
+xpath_leeches = './/td[7]/text()'
+xpath_downloads = './/td[8]/text()'
 
 
 # do search-request
@@ -72,25 +47,32 @@ def response(resp):
     dom = html.fromstring(resp.text)
 
     for result in dom.xpath(xpath_results):
+        # defaults
+        filesize = 0
+        magnet_link = ""
+        torrent_link = ""
+
         # 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
         page_a = result.xpath(xpath_title)[0]
         title = extract_text(page_a)
 
         # link to the page
-        href = page_a.attrib.get('href')
+        href = base_url + page_a.attrib.get('href')
 
-        # link to the torrent file
-        torrent_link = result.xpath(xpath_torrent_file)[0].attrib.get('href')
-
-        # torrent size
-        try:
-            file_size, suffix = result.xpath(xpath_filesize)[0].split(' ')
-            file_size = int(float(file_size) * get_filesize_mul(suffix))
-        except:
-            file_size = None
+        for link in result.xpath(xpath_torrent_links):
+            url = link.attrib.get('href')
+            if 'magnet' in url:
+                # link to the magnet
+                magnet_link = url
+            else:
+                # link to the torrent file
+                torrent_link = url
 
         # seed count
         seed = int_or_zero(result.xpath(xpath_seeds))
@@ -101,6 +83,14 @@ def response(resp):
         # torrent downloads count
         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 = 'Category: "{category}". Downloaded {downloads} times.'
         content = content.format(category=category, downloads=downloads)
@@ -110,8 +100,9 @@ def response(resp):
                         'content': content,
                         'seed': seed,
                         'leech': leech,
-                        'filesize': file_size,
+                        'filesize': filesize,
                         'torrentfile': torrent_link,
+                        'magnetlink': magnet_link,
                         'template': 'torrent.html'})
 
     return results

+ 1 - 1
searx/engines/swisscows.py

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

+ 2 - 3
searx/engines/tokyotoshokan.py

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

+ 22 - 16
searx/engines/torrentz.py

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

+ 8 - 10
searx/languages.py

@@ -5,6 +5,11 @@
 language_codes = (
     (u"ar-SA", u"العربية", u"", u"Arabic"),
     (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"da-DK", u"Dansk", u"", u"Danish"),
     (u"de", u"Deutsch", u"", u"German"),
@@ -15,9 +20,7 @@ language_codes = (
     (u"en", u"English", u"", u"English"),
     (u"en-AU", u"English", u"Australia", 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-GD", u"English", u"Grenada", u"English"),
     (u"en-ID", u"English", u"Indonesia", u"English"),
     (u"en-IE", u"English", u"Ireland", 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-ZA", u"English", u"South Africa", u"English"),
     (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-CL", u"Español", u"Chile", 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"fi-FI", u"Suomi", u"", u"Finnish"),
     (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-CA", u"Français", u"Canada", u"French"),
     (u"fr-CH", u"Français", u"Suisse", u"French"),
     (u"fr-FR", u"Français", u"France", u"French"),
     (u"he-IL", u"עברית", u"", u"Hebrew"),
-    (u"hr-HR", u"Hrvatski", u"", u"Croatian"),
     (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-CH", u"Italiano", u"Svizzera", u"Italian"),
     (u"it-IT", u"Italiano", u"Italia", u"Italian"),
     (u"ja-JP", u"日本語", u"", u"Japanese"),
     (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-BE", u"Nederlands", u"België", u"Dutch"),
     (u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"),
     (u"no-NO", u"Norsk", u"", u"Norwegian"),
     (u"pl-PL", u"Polski", u"", u"Polish"),
     (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-PT", u"Português", u"Portugal", u"Portuguese"),
     (u"ro-RO", u"Română", u"", u"Romanian"),
     (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"th-TH", u"ไทย", u"", u"Thai"),
     (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-CN", u"中文", u"中国", u"Chinese"),
     (u"zh-HK", u"中文", u"香港", u"Chinese"),

+ 28 - 28
searx/settings.yml

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

File diff suppressed because it is too large
+ 0 - 0
searx/static/themes/oscar/css/logicodev.min.css


File diff suppressed because it is too large
+ 0 - 0
searx/static/themes/oscar/css/pointhi.min.css


File diff suppressed because it is too large
+ 1 - 1
searx/static/themes/oscar/js/searx.min.js


+ 2 - 0
searx/static/themes/oscar/less/logicodev/oscar.less

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

+ 3 - 0
searx/static/themes/oscar/less/logicodev/preferences.less

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

+ 2 - 0
searx/static/themes/oscar/less/pointhi/oscar.less

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

+ 3 - 0
searx/static/themes/oscar/less/pointhi/preferences.less

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

+ 11 - 11
searx/templates/courgette/result_templates/code.html

@@ -1,11 +1,11 @@
-<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>
-    {% 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>
-    {% 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 %}
-    <div dir="ltr">
-    {{ result.codelines|code_highlighter(result.code_language)|safe }}
-	</div>
-
-    <p class="url">{{ result.pretty_url }}&lrm;</p>
-</div>
+<div class="result {{ result.class }}">
+    <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 %}
+    <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 %}
+    <div dir="ltr">
+    {{ result.codelines|code_highlighter(result.code_language)|safe }}
+	</div>
+
+    <p class="url">{{ result.pretty_url }}&lrm;</p>
+</div>

+ 11 - 11
searx/templates/legacy/result_templates/code.html

@@ -1,11 +1,11 @@
-<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>
-    <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 %}
-    <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="result-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 %}
-    
-    <div dir="ltr">
-    {{ result.codelines|code_highlighter(result.code_language)|safe }}
-	</div>
-</div>
+<div class="result {{ result.class }}">
+    <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>
+    {% 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>
+    {% if result.repository %}<p class="result-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 %}
+    
+    <div dir="ltr">
+    {{ result.codelines|code_highlighter(result.code_language)|safe }}
+	</div>
+</div>

+ 99 - 88
searx/templates/oscar/macros.html

@@ -1,88 +1,99 @@
-<!-- Draw glyphicon icon from bootstrap-theme -->
-{% macro icon(action) -%}
-    <span class="glyphicon glyphicon-{{ action }}"></span>
-{%- endmacro %}
-
-<!-- Draw favicon -->
-<!-- TODO: using url_for methode -->
-{% macro draw_favicon(favicon) -%}
-    <img width="32" height="32" class="favicon" src="static/themes/oscar/img/icons/{{ favicon }}.png" alt="{{ favicon }}" />
-{%- endmacro %}
-
-{%- macro result_link(url, title, classes='') -%}
-<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
-{%- endmacro -%}
-
-<!-- Draw result header -->
-{% macro result_header(result, favicons) -%}
-<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result_link(result.url, result.title|safe) }}</h4>
-{%- endmacro %}
-
-<!-- Draw result sub header -->
-{% macro result_sub_header(result) -%}
-    {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
-    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
-    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
-{%- endmacro %}
-
-<!-- Draw result footer -->
-{% macro result_footer(result) -%}
-    <div class="clearfix"></div>
-    <div class="pull-right">
-    {% for engine in result.engines %}
-        <span class="label label-default">{{ engine }}</span>
-    {% endfor %}
-    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
-    {% if proxify %}
-    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
-    {% endif %}
-</div>
-<div class="external-link">{{ result.pretty_url }}</div>
-{%- endmacro %}
-
-<!-- Draw result footer -->
-{% macro result_footer_rtl(result) -%}
-    <div class="clearfix"></div>
-    {% for engine in result.engines %}
-        <span class="label label-default">{{ engine }}</span>
-    {% endfor %}
-    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
-    {% if proxify %}
-    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
-    {% endif %}
-    <div class="external-link">{{ result.pretty_url }}</div>
-{%- endmacro %}
-
-{% macro preferences_item_header(info, label, rtl) -%}
-    {% if rtl %}
-    <div class="row form-group">
-        <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
-        <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
-        <div class="col-sm-4 col-md-4">
-    {% else %}
-    <div class="row form-group">
-        <label class="col-sm-3 col-md-2">{{ label }}</label>
-        <div class="col-sm-4 col-md-4">
-    {% endif %}
-{%- endmacro %}
-
-{% macro preferences_item_footer(info, label, rtl) -%}
-    {% if rtl %}
-        </div>
-    </div>
-    {% else %}
-        </div>
-        <span class="col-sm-5 col-md-6 help-block">{{ info }}</span>
-    </div>
-    {% endif %}
-{%- endmacro %}
-
-{% macro checkbox_toggle(id, blocked) -%}
-    <div class="onoffswitch">
-        <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
-        <label class="onoffswitch-label" for="{{ id }}">
-            <span class="onoffswitch-inner"></span>
-            <span class="onoffswitch-switch"></span>
-        </label>
-    </div>
-{%- endmacro %}
+<!-- Draw glyphicon icon from bootstrap-theme -->
+{% macro icon(action) -%}
+    <span class="glyphicon glyphicon-{{ action }}"></span>
+{%- endmacro %}
+
+<!-- Draw favicon -->
+{% macro draw_favicon(favicon) -%}
+    <img width="32" height="32" class="favicon" src="{{ url_for('static', filename='themes/oscar/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
+{%- endmacro %}
+
+{%- macro result_link(url, title, classes='') -%}
+<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
+{%- endmacro -%}
+
+<!-- Draw result header -->
+{% macro result_header(result, favicons) -%}
+<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result_link(result.url, result.title|safe) }}</h4>
+{%- endmacro %}
+
+<!-- Draw result sub header -->
+{% macro result_sub_header(result) -%}
+    {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
+    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
+    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
+{%- endmacro %}
+
+<!-- Draw result footer -->
+{% macro result_footer(result) -%}
+    <div class="clearfix"></div>
+    <div class="pull-right">
+    {% for engine in result.engines %}
+        <span class="label label-default">{{ engine }}</span>
+    {% endfor %}
+    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+    {% if proxify %}
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+    {% endif %}
+</div>
+<div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
+<!-- Draw result footer -->
+{% macro result_footer_rtl(result) -%}
+    <div class="clearfix"></div>
+    {% for engine in result.engines %}
+        <span class="label label-default">{{ engine }}</span>
+    {% endfor %}
+    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+    {% if proxify %}
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+    {% endif %}
+    <div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
+{% macro preferences_item_header(info, label, rtl) -%}
+    {% if rtl %}
+    <div class="row form-group">
+        <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
+        <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
+        <div class="col-sm-4 col-md-4">
+    {% else %}
+    <div class="row form-group">
+        <label class="col-sm-3 col-md-2">{{ label }}</label>
+        <div class="col-sm-4 col-md-4">
+    {% endif %}
+{%- endmacro %}
+
+{% macro preferences_item_footer(info, label, rtl) -%}
+    {% if rtl %}
+        </div>
+    </div>
+    {% else %}
+        </div>
+        <span class="col-sm-5 col-md-6 help-block">{{ info }}</span>
+    </div>
+    {% endif %}
+{%- endmacro %}
+
+{% macro checkbox_toggle(id, blocked) -%}
+    <div class="onoffswitch">
+        <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
+        <label class="onoffswitch-label" for="{{ id }}">
+            <span class="onoffswitch-inner"></span>
+            <span class="onoffswitch-switch"></span>
+        </label>
+    </div>
+{%- 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 %}

+ 17 - 14
searx/templates/oscar/preferences.html

@@ -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" %}
 {% block title %}{{ _('preferences') }} - {% endblock %}
 {% block content %}
+
 <div>
 
     <h1>{{ _('Preferences') }}</h1>
@@ -148,7 +149,7 @@
 				    <th>{{ _("Allow") }}</th>
 				    <th>{{ _("Engine name") }}</th>
 				    <th>{{ _("Shortcut") }}</th>
-				    <th>{{ _("Supports selected language") }}</th>
+				    <th>{{ _("Selected language") }}</th>
 				    <th>{{ _("SafeSearch") }}</th>
 				    <th>{{ _("Time range") }}</th>
 				    <th>{{ _("Avg. time") }}</th>
@@ -156,8 +157,9 @@
                                     {% else %}
 				    <th>{{ _("Max time") }}</th>
 				    <th>{{ _("Avg. time") }}</th>
+				    <th>{{ _("Time range") }}</th>
 				    <th>{{ _("SafeSearch") }}</th>
-				    <th>{{ _("Supports selected language") }}</th>
+				    <th>{{ _("Selected language") }}</th>
 				    <th>{{ _("Shortcut") }}</th>
 				    <th>{{ _("Engine name") }}</th>
 				    <th>{{ _("Allow") }}</th>
@@ -172,17 +174,18 @@
                                     </td>
                                     <th>{{ search_engine.name }}</th>
 				    <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><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td>
-				    <td><input type="checkbox" {{ "checked" if search_engine.time_range_support==True else ""}} readonly="readonly" disabled="disabled"></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>
-                                    {% 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_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><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>{{ 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>{{ support_toggle(search_engine.safesearch==True) }}</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_timeout'] else '' }}">{{ search_engine.timeout }}</td>
+									{% 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_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+					<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
+					<td>{{ support_toggle(search_engine.safesearch==True) }}</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>
                                     <td class="onoff-checkbox">
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}

+ 1 - 2
searx/templates/simple/macros.html

@@ -8,9 +8,8 @@
 {%- endmacro %}
 
 <!-- Draw favicon -->
-<!-- TODO: using url_for methode -->
 {% 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 %}
 
 {% macro result_open_link(url, classes='') -%}

BIN
searx/translations/de_DE/LC_MESSAGES/messages.mo


+ 0 - 844
searx/translations/de_DE/LC_MESSAGES/messages.po

@@ -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 ..."

+ 18 - 0
searx/utils.py

@@ -1,4 +1,6 @@
 import csv
+import hashlib
+import hmac
 import os
 import re
 
@@ -290,6 +292,15 @@ def convert_str_to_int(number_str):
         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):
     is_abbr = (len(lang) == 2)
     if is_abbr:
@@ -312,3 +323,10 @@ def load_module(filename, module_dir):
     module = load_source(modname, filepath)
     module.name = modname
     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()

+ 6 - 4
searx/webapp.py

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

+ 91 - 0
tests/unit/engines/test_base.py

@@ -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.')

+ 38 - 4
tests/unit/engines/test_bing_images.py

@@ -8,10 +8,12 @@ from searx.testing import SearxTestCase
 class TestBingImagesEngine(SearxTestCase):
 
     def test_request(self):
+        bing_images.supported_languages = ['fr-FR', 'en-US']
+
         query = 'test_query'
         dicto = defaultdict(dict)
         dicto['pageno'] = 1
-        dicto['language'] = 'fr_FR'
+        dicto['language'] = 'fr-FR'
         dicto['safesearch'] = 1
         dicto['time_range'] = ''
         params = bing_images.request(query, dicto)
@@ -19,12 +21,19 @@ class TestBingImagesEngine(SearxTestCase):
         self.assertTrue(query in params['url'])
         self.assertTrue('bing.com' in params['url'])
         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'
         params = bing_images.request(query, dicto)
-        self.assertIn('SRCHHPGUSR', params['cookies'])
-        self.assertIn('en', params['cookies']['SRCHHPGUSR'])
+        self.assertTrue('_EDGE_S' in params['cookies'])
+        self.assertTrue('en-us' in params['cookies']['_EDGE_S'])
 
     def test_response(self):
         self.assertRaises(AttributeError, bing_images.response, None)
@@ -82,3 +91,28 @@ class TestBingImagesEngine(SearxTestCase):
         self.assertEqual(results[0]['content'], '')
         self.assertEqual(results[0]['thumbnail_src'], 'thumb_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)

+ 2 - 0
tests/unit/engines/test_bing_videos.py

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

+ 0 - 71
tests/unit/engines/test_blekko_images.py

@@ -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')

+ 2 - 5
tests/unit/engines/test_faroo.py

@@ -40,9 +40,6 @@ class TestFarooEngine(SearxTestCase):
         response = mock.Mock(text='{"data": []}')
         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)
         self.assertRaises(Exception, faroo.response, response)
 
@@ -98,14 +95,14 @@ class TestFarooEngine(SearxTestCase):
         response = mock.Mock(text=json)
         results = faroo.response(response)
         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]['url'], 'http://this.is.the.url/')
         self.assertEqual(results[0]['content'], 'This is the content')
         self.assertEqual(results[1]['title'], 'This is the title2')
         self.assertEqual(results[1]['url'], 'http://this.is.the.url2/')
         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 = """
         {}

File diff suppressed because it is too large
+ 0 - 1
tests/unit/engines/test_google_news.py


+ 91 - 33
tests/unit/engines/test_nyaa.py

@@ -13,38 +13,92 @@ class TestNyaaEngine(SearxTestCase):
         params = nyaa.request(query, dic)
         self.assertTrue('url' in params)
         self.assertTrue(query in params['url'])
-        self.assertTrue('nyaa.se' in params['url'])
+        self.assertTrue('nyaa.si' in params['url'])
 
     def test_response(self):
         resp = mock.Mock(text='<html></html>')
         self.assertEqual(nyaa.response(resp), [])
 
         html = """
-        <table class="tlist">
-          <tbody>
-            <tr class="trusted tlistrow">
-              <td class="tlisticon">
-                <a href="//www.nyaa.se" title="English-translated Anime">
-                   <img src="//files.nyaa.se" alt="English-translated Anime">
-                </a>
-              </td>
-              <td class="tlistname">
-                <a href="//www.nyaa.se/?page3">
-                  Sample torrent title
-                </a>
-              </td>
-              <td class="tlistdownload">
-                <a href="//www.nyaa.se/?page_dl" title="Download">
-                  <img src="//files.nyaa.se/www-dl.png" alt="DL">
-                </a>
-              </td>
-              <td class="tlistsize">10 MiB</td>
-              <td class="tlistsn">1</td>
-              <td class="tlistln">3</td>
-              <td class="tlistdn">666</td>
-              <td class="tlistmn">0</td>
-            </tr>
-          </tbody>
+        <table class="table table-bordered table-hover table-striped torrent-list">
+        <thead>
+        <tr>
+        <th class="hdr-category text-center" style="width:80px;">
+        <div>Category</div>
+        </th>
+        <th class="hdr-name" style="width:auto;">
+        <div>Name</div>
+        </th>
+        <th class="hdr-comments sorting text-center" title="Comments" style="width:50px;">
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=comments&amp;o=desc"></a>
+        <i class="fa fa-comments-o"></i>
+        </th>
+        <th class="hdr-link text-center" style="width:70px;">
+        <div>Link</div>
+        </th>
+        <th class="hdr-size sorting text-center" style="width:100px;">
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=size&amp;o=desc"></a>
+        <div>Size</div>
+        </th>
+        <th class="hdr-date sorting_desc text-center" title="In local time" style="width:140px;">
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=id&amp;o=asc"></a>
+        <div>Date</div>
+        </th>
+        <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>
         """
 
@@ -52,15 +106,19 @@ class TestNyaaEngine(SearxTestCase):
         results = nyaa.response(resp)
 
         self.assertEqual(type(results), list)
-        self.assertEqual(len(results), 1)
+        self.assertEqual(len(results), 2)
 
         r = results[0]
-        self.assertTrue(r['url'].find('www.nyaa.se/?page3') >= 0)
-        self.assertTrue(r['torrentfile'].find('www.nyaa.se/?page_dl') >= 0)
-        self.assertTrue(r['content'].find('English-translated Anime') >= 0)
-        self.assertTrue(r['content'].find('Downloaded 666 times.') >= 0)
+        self.assertTrue(r['url'].find('1') >= 0)
+        self.assertTrue(r['torrentfile'].find('1.torrent') >= 0)
+        self.assertTrue(r['content'].find('Anime - English-translated') >= 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['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)

+ 3 - 3
tests/unit/engines/test_swisscows.py

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

+ 14 - 18
tests/unit/engines/test_torrentz.py

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

+ 8 - 8
utils/fetch_languages.py

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

+ 3 - 3
utils/update-translations.sh

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

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